mcorbin / meuse Goto Github PK
View Code? Open in Web Editor NEWA private Cargo crate registry, for Rust
Home Page: https://meuse.mcorbin.fr/
License: Eclipse Public License 2.0
A private Cargo crate registry, for Rust
Home Page: https://meuse.mcorbin.fr/
License: Eclipse Public License 2.0
this was a mistake on my part but thought I should mention it as other situations might arise that are similar:
when I initially created the repo (metadata.path) I just created an empty directory and forgot about it. when I attempted to publish a crate the git part failed because it wasn't actually a repo. I fixed that and tried to publish again but meuse complained that the package version already existed. Sure enough it had written the database entries. I deleted those rows and was able to publish again.
it seems to me like the database writing and the git updating should be atomic.
Got an error when trying to run cargo yank. From the logs:
java.io.FileNotFoundException: /app/registry/se/rv/mycrate (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at clojure.java.io$fn__11520.invokeStatic(io.clj:229)
at clojure.java.io$fn__11520.invoke(io.clj:229)
at clojure.java.io$fn__11433$G__11426__11440.invoke(io.clj:69)
at clojure.java.io$fn__11532.invokeStatic(io.clj:258)
at clojure.java.io$fn__11532.invoke(io.clj:254)
at clojure.java.io$fn__11433$G__11426__11440.invoke(io.clj:69)
at clojure.java.io$fn__11494.invokeStatic(io.clj:165)
at clojure.java.io$fn__11494.invoke(io.clj:165)
at clojure.java.io$fn__11446$G__11422__11453.invoke(io.clj:69)
at clojure.java.io$reader.invokeStatic(io.clj:102)
at clojure.java.io$reader.doInvoke(io.clj:86)
at clojure.lang.RestFn.invoke(RestFn.java:410)
at clojure.lang.AFn.applyToHelper(AFn.java:154)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at clojure.core$apply.invokeStatic(core.clj:669)
at clojure.core$slurp.invokeStatic(core.clj:6944)
at clojure.core$slurp.doInvoke(core.clj:6944)
at clojure.lang.RestFn.invoke(RestFn.java:410)
at meuse.metadata$update_yank.invokeStatic(metadata.clj:65)
at meuse.metadata$update_yank.invoke(metadata.clj:61)
at meuse.api.crate.yank$update_yank$fn__12902.invoke(yank.clj:26)
at meuse.api.crate.yank$update_yank.invokeStatic(yank.clj:25)
at meuse.api.crate.yank$update_yank.invoke(yank.clj:11)
at meuse.api.crate.yank$yank.invokeStatic(yank.clj:41)
at meuse.api.crate.yank$yank.invoke(yank.clj:39)
at meuse.inject$inject_crate_api_BANG_$fn__17980.invoke(inject.clj:115)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at meuse.interceptor.route$fn__18970.invokeStatic(route.clj:66)
at meuse.interceptor.route$fn__18970.invoke(route.clj:56)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at meuse.interceptor.route$fn__18996.invokeStatic(route.clj:107)
at meuse.interceptor.route$fn__18996.invoke(route.clj:102)
at exoscale.interceptor.impl$invoke_stage.invokeStatic(impl.cljc:45)
at exoscale.interceptor.impl$invoke_stage.invoke(impl.cljc:39)
at exoscale.interceptor.impl$enter.invokeStatic(impl.cljc:77)
at exoscale.interceptor.impl$enter.invoke(impl.cljc:65)
at exoscale.interceptor.impl$execute.invokeStatic(impl.cljc:104)
at exoscale.interceptor.impl$execute.invoke(impl.cljc:101)
at exoscale.interceptor$execute.invokeStatic(interceptor.cljc:41)
at exoscale.interceptor$execute.invoke(interceptor.cljc:8)
at exoscale.interceptor$execute.invokeStatic(interceptor.cljc:39)
at exoscale.interceptor$execute.invoke(interceptor.cljc:8)
at meuse.http$interceptor_handler$handler__19386.invoke(http.clj:43)
at ring.adapter.jetty$proxy_handler$fn__19341.invoke(jetty.clj:27)
at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.Server.handle(Server.java:516)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:388)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:633)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:380)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:383)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:882)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1036)
at java.base/java.lang.Thread.run(Thread.java:829)
To use as private registry, and provide a great monitoring on access (very important because it store sources of code...), I think it can be awesome to add the last usage of the token.
It requires to update database, Rest API and UI.
I am trying to publish a crate with a semver-compatibile version, but Meuse rejects it:
Uploading foo v7.0.0-alpha.4 (/path/to/foo)
error: api errors (status 200 OK): Wrong input parameters:
- field vers: the value should be a valid semver string
Am I missing something here?
See also: https://semver.org/spec/v2.0.0.html#spec-item-9 (and the BNF at the bottom)
encountered this error message from running $ docker build .
:
Step 10/13 : RUN apt-get update && apt-get -y upgrade && apt-get install -y git
---> Running in 107a7a75fb08
/bin/sh: apt-get: command not found
It's confusing because the image it was using before (openjdk:11
) did that step just fine, I guess they changed it.
I found another image, openjdk:slim-bullseye
that works. "bullseye" is debian release code name. not sure if "slim" is worth much vs the fat one but it was the first one I got to work.
Initially I tried to do openjdk:17-alpine
but that had a lot of differences (apk
vs apt-get
, need to install openssh
, different command line options for useradd
/groupadd
and I abandoned that effort).
Downloading a crate from meuse returns a 404 if the crate version contains a + sign.
tested with openssl-src, flatc and a few other crates.
From crates.io the following works as expected:
curl -v -L "https://crates.io/api/v1/crates/flatc/0.1.0+1.12.0/download" -o tmpdl
curl -v -L "https://crates.io/api/v1/crates/openssl-src/111.10.1+1.1.1g/download" -o tmpdl
via meuse I get a 404 on:
curl -v -L "https://mirror.company.com/api/v1/mirror/flatc/0.1.0+1.12.0/download" -o tmpdl
curl -v -L "https://mirror.company.com/api/v1/mirror/openssl-src/111.10.1+1.1.1g/download" -o tmpdl
but 200 on:
curl -v -L "https://mirror.company.com/api/v1/mirror/unicase/2.6.0/download" -o tmpdl
So it seems that versions with a + are not working atm.
Hi. I want to ask if you have any plans to support sparse registries. This could be especially helpful with mirroring, since crates.io index is very heavy.
Or if you could give some tips on how this could be done and where to start I could try to implement this (although I never used Clojure).
Hi!
Is the documentation source files available somewhere? :) I have some issues to access the full documentation website, it would be nice to be able to download/build the full documentation locally :)
Kind Regards
Carl
Thanks for starting this project! I wanted to experiment with what you have so far but am having trouble getting started. I'm coming at this from the perspective of never having worked on a Clojure project. Basically I'm looking for a 'from git clone to cargo publish' steps. What I figured out so far:
at this point it's up and running, I already have postgres running on my machine but didn't create the meuse user/schema. I was expecting an exception when running meuse but it didn't so I figured I'd try and publish and that would tell me what I had to get working next. However I haven't figured out what URL to use for the private registry. Example:
in .cargo/config:
[registries]
icooke = { index = "http://dev-icooke:8855/what/goes/here" }
because every cargo api call results in:
DEBUG [2019-05-02 05:08:02,479] manifold-pool-2-1 - meuse.http request b561f5de-eb3d-4c7a-8692-dea6989b3d5c with subsystem :meuse.api.default with action :not-found
so... do you have an example registries entry?
Google Chrome Audits shows some problems:
Fix these to speed up page loading for devices with a slow internet connection.
Cache-control
header: There are probably options in the web framework, I don't knowIt seems like you need a token to create a token. I tried the following in Postman:
POST localhost:8855/api/v1/meuse/user
Content-Type: application/json
{
"name": "admin_token",
"validity": 10,
"user": "admin",
"password": "REDACTED"
}
and got this result:
{
"errors": [
{
"detail": "token missing in the header"
}
]
}
INFO [2019-12-10 18:21:49,102] manifold-pool-2-21 - meuse.http handler is :meuse.api.meuse.http/new-user
DEBUG [2019-12-10 18:21:49,103] manifold-pool-2-21 - meuse.http request 2feda17a-2443-4360-904a-6d662f189180 with subsystem :meuse.api.meuse.http with action :new-user
ERROR [2019-12-10 18:21:49,105] manifold-pool-2-21 - meuse.error 2feda17a-2443-4360-904a-6d662f189180 #error {
:cause token missing in the header
:data {:type :exoscale.ex/forbidden}
:via
[{:type clojure.lang.ExceptionInfo
:message token missing in the header
:data {:type :exoscale.ex/forbidden}
:at [exoscale.ex$ex_info invokeStatic ex.clj 237]}]
:trace
[[exoscale.ex$ex_info invokeStatic ex.clj 237]
[exoscale.ex$ex_info invoke ex.clj 219]
[exoscale.ex$ex_forbidden invokeStatic ex.clj 265]
[exoscale.ex$ex_forbidden invoke ex.clj 265]
[exoscale.ex$ex_forbidden invokeStatic ex.clj 265]
[exoscale.ex$ex_forbidden invoke ex.clj 265]
[meuse.auth.request$check_user invokeStatic request.clj 31]
[meuse.auth.request$check_user invoke request.clj 10]
[meuse.http$eval21915$fn__21916 invoke http.clj 79]
[clojure.lang.MultiFn invoke MultiFn.java 229]
[meuse.http$get_handler$handler__21926 invoke http.clj 123]
[ring.middleware.resource$wrap_resource_prefer_resources$fn__21816 invoke resource.clj 25]
[meuse.middleware$wrap_json$fn__20645 invoke middleware.clj 14]
[ring.middleware.keyword_params$wrap_keyword_params$fn__21868 invoke keyword_params.clj 53]
[ring.middleware.params$wrap_params$fn__21892 invoke params.clj 67]
[aleph.http.server$handle_request$fn__12888$f__7600__auto____12889 invoke server.clj 166]
[clojure.lang.AFn run AFn.java 22]
[io.aleph.dirigiste.Executor$Worker$1 run Executor.java 62]
[manifold.executor$thread_factory$reify__7482$f__7483 invoke executor.clj 47]
[clojure.lang.AFn run AFn.java 22]
[java.lang.Thread run Thread.java 834]]} http error {:type :exoscale.ex/forbidden} 403
are there any plans on adding token generation through the front-end's /me URL? That's a bit more friendly than using curl
. This is how cargo's help prompts a user to create a token.
Hello,
I have restarted the service, it won't restart as I have received that error:
destination path 'crate-index' already exists and is not an empty directory
Is it possible to fix that ?
Thank you !
Marc-Antoine
Hello again,
It's more a question, but can be a great feature ...
Do you think meuse can generate and host the Rust documentation of projects ?
Marc-Antoine
run docker build -t meuse .
it will fail on
Step 8/13 : COPY --from=build-env /app/target/uberjar/meuse-*-standalone.jar /app/meuse.jar
COPY failed: no source files were specified
In the small UI, it can be important for private to see list of users.
And maybe also the number of token generated for each user.
I am not sure how to tell if the current project depends on a version of log4j that has the known security vulnerabilities (CVE-2021-44228).
But, if so, please bump the dependencies to fix this issue.
AFAICT, org.clojure/tools.logging
needs to be bumped to 1.2.2 and spootnik/unilog
to 0.7.29, but the latter does not seem to be out yet. Am I missing any other libraries?
https://github.com/mcorbin/meuse/blob/master/src/meuse/db/queries/category.clj#L27-L37
this code seems to return all categories, not just the categories associated with the crate.
I think the query needs to have the crate_id
condition as part of the where clause, not as part of the left-join.
exapmle:
meuse=# select c.id, c.description, c.name, cc.crate_id
from categories c
left join crates_categories cc on c.id=cc.category_id and cc.crate_id='2ff956f6-7b3e-49fc-8697-845d3a10fc75';
id | description | name | crate_id
--------------------------------------+--------------------------+--------------------------+--------------------------------------
5527b5af-84ec-432f-b4a5-698048e5d096 | statistics | statistics | 2ff956f6-7b3e-49fc-8697-845d3a10fc75
3759e640-2ba1-49c0-9ca3-10babdb07629 | science | science |
"science" row should not be part of query results. if you change the second clause of the left-join to be in the where clause instead, it works without returning extra rows:
meuse=# select c.id, c.description, c.name, cc.crate_id
from categories c
left join crates_categories cc on c.id=cc.category_id
where cc.crate_id='2ff956f6-7b3e-49fc-8697-845d3a10fc75';
id | description | name | crate_id
--------------------------------------+--------------------------+--------------------------+--------------------------------------
5527b5af-84ec-432f-b4a5-698048e5d096 | statistics | statistics | 2ff956f6-7b3e-49fc-8697-845d3a10fc75
fwiw I did try to fix this in the clojure code but failed :( - maybe next time.
cargo search
does not seem to pass the user token to the HTTP call.
Currently, Meuse checks the token for the search
endpoint, so search calls with cargo are failing (but work with curl).
I can easily remove the auth check on the search
endpoint, but I would like to first check if it's possible to (optionally) pass the token in Cargo itself.
To simplify user management, do you think it can be possible to add a OAuth2 (and maybe SSO) login system ?
Personally I use Google for my company, so it can be excellent to support that, even for open source.
I think a support of Google and Github and Gitlab will be awesome !
Do you think it can be complex to do that in Clojure ?
I noticed that the code in src/meuse/store/s3.clj
pretty uniformly includes a notion of a prefix
that is appended to the beginning of the object key. However, the config specification in src/meuse/spec.clj
does not include any option to specify a key prefix for s3 storage.
Is the notion of prefix
in the s3 storage code limited to prefixing keys by crate name and/or version? Or would it be possible to allow a configurable key prefix to be used, as a means of namespacing the s3 objects at smaller granularity than a bucket?
In other words, would it be possible to change the spec to
(defmethod crate-store "s3"
[_]
(s/keys :req-un [:s3/access-key
:s3/secret-key
:s3/endpoint
:s3/bucket
:s3/key-prefix]))
... and then integrate the s3/key-prefix
value into the key-generating code in s3.clj
?
(I am not a clojure developer - asking to check whether I am missing something big about this. I would be happy to attempt to make the changes myself if this is a desirable change and you think it will be straightforward.)
One problem a configurable key prefix would create is if the prefix configuration were changed, after objects had already been written to s3 -- the current code would not be able to find those objects, would it?
Another possible solution would be to include the crates_versions
id in the s3 key, which would at least guarantee unique object keys.
Be able to list all token generate for my account.
Be able to notify on Slack a new published item.
Can't open website:
https://meuse.mcorbin.fr/
version 0.5.0
when following the example in the documentation:
curl --header "Content-Type: application/json" --request POST --data '{"name":"test_token","validity":10,"user":"root_user","password":"do_not_use_this_password"}' localhost:8855/api/v1/meuse/token
the result is
java.lang.NoSuchMethodError: java.nio.CharBuffer.flip()Ljava/nio/CharBuffer;
at byte_streams.char_sequence$lazy_char_buffer_sequence$fn__11298.invoke(char_sequence.clj:77)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:535)
at clojure.core$seq__5402.invokeStatic(core.clj:137)
at clojure.core$seq__5402.invoke(core.clj:137)
at byte_streams.char_sequence$decode_byte_source$reify__11308.toString(char_sequence.clj:120)
at byte_streams$fn__11633$f__11337__auto____11634.invoke(byte_streams.clj:556)
at byte_streams.graph$conversion_fn$fn__10929$fn__10930.invoke(graph.clj:259)
at clojure.lang.PersistentUnrolledVector$Card2.reduce(PersistentUnrolledVector.java:482)
at clojure.core$reduce.invokeStatic(core.clj:6827)
at clojure.core$reduce.invoke(core.clj:6810)
at byte_streams.graph$conversion_fn$fn__10929.invoke(graph.clj:253)
at byte_streams$convert.invokeStatic(byte_streams.clj:193)
at byte_streams$convert.invoke(byte_streams.clj:162)
at byte_streams$convert.invokeStatic(byte_streams.clj:177)
at byte_streams$convert.invoke(byte_streams.clj:162)
at meuse.request$convert_body_edn$fn__12262.invoke(request.clj:14)
at clojure.core$update.invokeStatic(core.clj:6196)
at clojure.core$update.invoke(core.clj:6188)
at meuse.request$convert_body_edn.invokeStatic(request.clj:12)
at meuse.request$convert_body_edn.invoke(request.clj:7)
at meuse.interceptor.route$fn__18506.invokeStatic(route.clj:72)
at meuse.interceptor.route$fn__18506.invoke(route.clj:69)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at meuse.interceptor.route$fn__18518.invokeStatic(route.clj:92)
at meuse.interceptor.route$fn__18518.invoke(route.clj:87)
at exoscale.interceptor.impl$invoke_stage.invokeStatic(impl.cljc:45)
at exoscale.interceptor.impl$invoke_stage.invoke(impl.cljc:39)
at exoscale.interceptor.impl$enter.invokeStatic(impl.cljc:77)
at exoscale.interceptor.impl$enter.invoke(impl.cljc:65)
at exoscale.interceptor.impl$execute.invokeStatic(impl.cljc:104)
at exoscale.interceptor.impl$execute.invoke(impl.cljc:101)
at exoscale.interceptor$execute.invokeStatic(interceptor.cljc:41)
at exoscale.interceptor$execute.invoke(interceptor.cljc:8)
at exoscale.interceptor$execute.invokeStatic(interceptor.cljc:39)
at exoscale.interceptor$execute.invoke(interceptor.cljc:8)
at meuse.http$interceptor_handler$handler__18755.invoke(http.clj:44)
at aleph.http.server$handle_request$fn__15150$f__8803__auto____15151.invoke(server.clj:166)
at clojure.lang.AFn.run(AFn.java:22)
at io.aleph.dirigiste.Executor$Worker$1.run(Executor.java:62)
at manifold.executor$thread_factory$reify__8695$f__8696.invoke(executor.clj:47)
at clojure.lang.AFn.run(AFn.java:22)
at java.lang.Thread.run(Thread.java:748)
Hi,
I am following the installation guide and I want to run the schema script that is linked but the link returns 404:
https://github.com/mcorbin/meuse/blob/master/dev/resources/sql/schema.sql
I got the link from here: https://meuse.mcorbin.fr/installation/requirements/
Where can I find it?
when meuse creates the crate description it writes a key 'version_req'. that should be just 'req' as specified in https://github.com/rust-lang/rfcs/blob/master/text/2141-alternative-registries.md. cargo won't download the crate as published but after editing the repo and doing a search/replace I was able to download the crate.
I have tried to publish on my private crate the gloo-file
dependency.
It works but not when I try to depend on it in an another project.
Do you think it can be a Meuse issue ?
I forgot to put a 'allowed-registries' key to whitelist crates.io as described at https://github.com/rust-lang/rfcs/blob/master/text/2141-alternative-registries.md but meuse let me publish my crate with crates.io dependencies anyways. I think it should have rejected my publish.
Hi and thanks for the great custom registry! :)
I've come over an issue that i don't know how to handle: it's impossible to use a crate that renames a crate and uses it as a feature.
Step to reproduce:
$ cargo init --lib test-crate
and $ cargo init --lib use-test-crate
.test-crate
(test-crate/Cargo.toml
):[package]
name = "test-crate"
version = "0.1.0"
authors = []
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
with = [ "bit-vec6" ]
[dependencies]
bit-vec6 = { version = "0.6", package = "bit-vec" }
use-test-crate
(use-test-crate/Cargo.toml
):[package]
name = "use-test-crate"
version = "0.1.0"
authors = []
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
test-crate = { version = "0.1.0", registry = "REGISTRY-NAME"}
test-crate
: $ cd test-crate && cargo publish --registry REGISTRY-NAME
use-test-crate
: $ cd use-test-crate && cargo check
Updating `ssh://git@..../crates-index.git` index
error: no matching package named `test-crate` found
location searched: registry `ssh://git@.../crates-index.git`
required by package `use-test-crate v0.1.0 (/home/mexus/test/rust/use-test-crate)`
cargo search
however returns the expected results though:
$ cargo search --registry REGISTRY-NAME test-crate
test-crate = "0.1.0" #
Any ideas how to fix the issue? :)
I read on your blog that you would like help improving the Frontend. I have a bit of free time, so I'd like to offer my help.
About me: I'm a CS student and live in Germany. I'm experienced in writing Java, Kotlin, Javascript, Typescript, HTML, CSS, SQL and Rust.
I'm not yet familiar with Clojure, but I guess I don't have to understand everything to work with the HTML templates :)
Could you create some issues with a frontend
label, so I know what needs to be implemented?
P.S. could you add build instructions to the readme?
Add schema to database configuration. It will look like this.
database:
user: "meuse"
host: "127.0.0.1"
name: "meuse"
schema: "project1"
Tables will be created in that schema. It means that instead of tabname it would be project1.tabname.
This is a very easy to do. You just need to issue 2 SQL commands:
CREATE schema IF NOT EXISTS project1;
SET search_path TO project1;
and access tables like you normally do now.
Hello,
I have configured my Meuse, that's awesome project, the first one usable for Rust private registry in private.
I just received that error:
front:2 Resource interpreted as Stylesheet but transferred with MIME type text/plain: "https://crates.media-io.com/static/css/style.css".
front:2 Resource interpreted as Stylesheet but transferred with MIME type text/plain: "https://crates.media-io.com/static/css/bootstrap.min.css".
Which render me the page without any css. Pretty bad design :P
Do you think it's possible to set the MIME type to "text/css" ?
I don't know Closure to contribute...
Best regards,
Marc-Antoine
Currently, there is no authentication.
We should decide which authentication mechanism we want to use.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.