octokit / oauth-app.js Goto Github PK
View Code? Open in Web Editor NEWGitHub OAuth toolset for Node.js
License: MIT License
GitHub OAuth toolset for Node.js
License: MIT License
Testing OAuth apps locally is hard-ish. Multiple OAuth callback URLs can now be registered for GitHub/OAuth Apps, but when running the server locally, the redirectUrl
argument has to be passed to /api/github/oauth/login?redirectUrl=...
.
It would be more elegant if we could pass a default redirectUrl to the OAuthApp constructor instead:
const app = new OAuthApp({
clientType: "oauth-app",
clientId: "1234567890abcdef1234",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
redirectUrl: process.env.NODE_ENV === "production" ? PRODUCTION_URL : "http://localhost:3000"
});
@octokit/[email protected]
n/a
n/a
What’s missing?
Compare https://github.com/octokit/core.js#defaults
const MyOAuthApp = OAuthApp.defaults({
/* set default constructor options */
})
Why?
Needed for the octokit
module, see octokit/octokit.js#15. The Octokit
constructor needs to be set to Octokit
exported by the octokit
module, which includes several plugins.
Alternatives you tried
I could implement the defaults functionality within octokit
but there it's the same amount of work, and OAuthApp.defaults()
will be useful outside of octokit
What happened?
see octokit/octokit.js#24 (comment)
What the problem might be
We have to remove the type imports from node's "http"
package.
compare the change at octokit/webhooks.js@662f35c
🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨
To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.
Since we didn’t receive a CI status on the greenkeeper/initial
branch, it’s possible that you don’t have CI set up yet.
We recommend using:
If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/
.
Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.
3.x
branch failed. 🚨I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this 💪.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the 3.x
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here are some links that can help you:
If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.
4.0.4
on branch 3.x
cannot be published as it is out of range.Only releases within the range >=3.0.0 <4.0.0
can be merged into the maintenance branch 3.x
and published to the 3.x
distribution channel.
The branch 3.x
head should be reset to a previous commit so the commit with tag v4.0.4
is removed from the branch history.
See the workflow configuration documentation for more details.
Good luck with your project ✨
Your semantic-release bot 📦🚀
Originally commented here: octokit/octokit.js#2450 (comment)
Importing https://esm.sh/octokit
fails, at least in Deno 1.34.2, because it cannot see the createNodeMiddleware
export. I think this is due to something in #416 because overriding the dependency to use 4.2.1 works fine.
❯ cat foobar.ts
import { App } from "https://esm.sh/octokit"; console.log(App);
❯ deno run --unstable -A --no-lock --reload foobar.ts
error: Uncaught SyntaxError: The requested module '/v125/@octokit/[email protected]/denonext/oauth-app.mjs' does not provide an export named 'createNodeMiddleware'
at <anonymous> (https://esm.sh/v125/@octokit/[email protected]/denonext/app.mjs:2:842)
Failing version: https://esm.sh/[email protected]
Working version: https://esm.sh/[email protected]?dts&target=deno&deps=@octokit/[email protected]
❯ deno --version
deno 1.34.2 (release, aarch64-apple-darwin)
v8 11.5.150.2
typescript 5.0.4
No response
What happened?
import { createToken, checkToken } from "@octokit/oauth-app";
const result = await checkToken({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
token
});
This throws a 404 error, the authorization
header is not set
What did you expect to happen?
The authorization
header should be set to basic auth from the clientId
and clientSecret
options
What the problem might be
The stateless checkToken
method does not set any auth
oauth-app.js/src/methods/check-token.ts
Lines 15 to 28 in b281913
To do so, let's remove the middleware entirely from this repository, and release it in an external package, such as @octokit/middleware-aws-lambda-oauth
Originally posted by @gr2m in #331 (comment)
Hi – I was wondering what the intended usage of /api/github/oauth/callback
is, beyond demonstration purposes? Is an implementor expected to override the route, or just implement a separate one?
Along those lines, I was thinking it'd be good to provide a callback mechanism for overriding the route instead.
Subtask of octokit/core.js#588
What’s missing?
If an authorization error occurs, the callback redirect has different query parameters. Example
?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch
Why?
It's a standard error behavior of GitHub's API and should be covered by Octokit
What happened?
When authenticating octokit using the docs:
const { octokit } = await app.getUserOctokit({ code: "code123" });
would always return that octokit
was undefined, so I tried doing
const octokit = await app.getUserOctokit({ code: "code123" });
and that gave me an octokit
instance that I was able to use.
What did you expect to happen?
const { octokit } = await app.getUserOctokit({ code: "code123" });
should return a valid instance of octokit
, but it does not, so we could change the docs to say:
const octokit = await app.getUserOctokit({ code: "code123" });
The code that worked for me
const app = new OAuthApp({
clientType: "oauth-app",
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
defaultScopes: "repo"
});
const octokit = await app.getUserOctokit({ code, state });
What happened?
While upgrading the dependency in octokit/app.js#306 due to the dropping of NodeJS v10 and v12 support, the tests failed
What did you expect to happen?
The tests to not have failed
What the problem might be
The regression was introduced by #288
/cc @baoshan
What happened?
Given I have this code
app.on("token", async () => console.log("token event emitted"))
Using app.getUserOctokit()
and octokit.request("GET /user")
will not trigger the "token"
event
const octokit = await app.getUserOctokit({ code });
const { data } = await octokit.request("GET /user");
But app.createToken()
does, as expected.
app.createToken({ code });
What did you expect to happen?
The first request (octokit.request("GET /user")
) should have triggered the "token"
event, as the code exchange happened at this point.
What the problem might be
I'm not sure. The first step would be to create a test to replicate the problem
What’s missing?
context.octokit
is currently not set for the events token.deleted
and token.authorization
. We should instead set it, but make it explicitly unauthorized using @octokit/auth-unauthorized
Why?
We will do the same thing when handling installation.suspended
and installation.deleted
webhook events. That's what we already do in Probot today. It will keep it concise.
Alternatives you tried
🤷🏼
There is a discrepancy between the getWebFlowAuthorizationUrl options and the params accepted by the login middleware: The login
option is not available as a param to the login middleware.
If using the login
option is desired then users must use the getWebFlowAuthorizationUrl
method instead of the middleware. Is this intentional?
Is login
still a valid option to the method or is the documentation outdated?
oauth-app.js/src/middleware/handle-request.ts
Lines 76 to 83 in 7fa986f
@octokit/[email protected]
No response
No response
🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨
To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.
Since we didn’t receive a CI status on the greenkeeper/initial
branch, it’s possible that you don’t have CI set up yet.
We recommend using:
If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/
.
Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.
Automated renovate PRs were merged that contained breaking changes, since those versions dropped support for Node v14, v16
Now users won't be able to install these packages anymore
@octokit/oauth-app v4.2.3
No response
When I am using createNodeMiddleware
I noticed that the authorization URL is using allowSignup
as true
by default. In order to disable that I added oauth.allowSignup: false
in the App options as such
function setupGitHub() {
return new App({
appId: process.env.GITHUB_APP_ID!,
privateKey: readFileSync(process.env.GITHUB_KEY!, 'utf8'),
webhooks: {
secret: process.env.GITHUB_WEBHOOK_SECRET!
},
oauth: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
allowSignup: false,
redirectUrl: process.env.GITHUB_REDIRECT_URL
}
})
}
But this had no effect.
This seems to me because I didnt add the allowsignup
parameter to the login
request. The relevant line where the bug is happening is
options.allowSignup
in the above line is true becausequery.allowSignup !== "false"
evaulates to true
if the login request doesn't contain the allowSignup
parameter.
[email protected] and @octokit/[email protected]
No response
What happened?
import { Octokit, OAuthApp } from 'octokit'
const app = new OAuthApp({
clientType: 'oauth-app',
clientId: 'fakeClientId',
clientSecret: 'fakeClientSecret',
})
const octokit: Octokit = await app.getUserOctokit({ code: 'fakeCode' })
causes a type error in the last line, because the unwrapped return value of app.getUserOctokit
is not assignable to the constant of type Octokit
.
What did you expect to happen?
No type errors.
What the problem might be
app.getUserOctokit
returns a promise of an instance type of Octokit
option.
However, app.getUserOctokit
is declared as returning Promise<OctokitInstance>
, where OctokitInstance
is hardcoded to be an instance type of OAuthTypeOctokit
, regardless of the options passed to OAuthApp
constructor.
It should probably be declared as returning Promise<OctokitTypeFromOptions<TOptions>>
instead.
A similar issue (but about octokit
field instead of getUserOctokit
method) is #212.
Follow up to octokit/auth-app.js#253
OAuth Apps support scopes
in order to request specific access for OAuth tokens for user access.
GitHub Apps support the OAuth flows, too, however the scopes feature is not supported. Instead the OAuth tokens for user access inherit the permissions from the app and its installations.
Hope I'm doing something silly here and it will be easy to spot
Here is the code: https://github.com/ed42311/github-new-repo
Not sure if there is a better way to present this, might be a bit much to test run, if you clone, install and yarn start
Oh you'll need to create an OAuth app and put CLIENT_ID and CLIENT_SECRET in a .env
Here is the flow:
localhost:8080
octokit.repos.createForAuthenticatedUser
creates a hello_world repoI've tried the last action with a personal access token and it works, but through the auth flow I'm obviously missing something.
Querying for the access token:
const tokenResponse = await axios({
method: 'post',
url: `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&code=${requestToken}&scope=user%20public_repo%20repo`,
headers: {
accept: 'application/json',
},
})
const accessToken = tokenResponse.data.access_token
then
const octokit = new Octokit({ auth: accessToken })
const response = await octokit.repos.createForAuthenticatedUser({
name: 'hello_world',
})
response errors out in the trycatch
RequestError [HttpError]: Not Found
at /Users/contextcue/code/experiments/node-oauth-example/node_modules/@octokit/request/dist-node/index.js:66:23
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async /Users/contextcue/code/experiments/node-oauth-example/dist/index.js:39:26 {
status: 404,
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset',
connection: 'close',
'content-encoding': 'gzip',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Fri, 24 Jul 2020 18:10:10 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'GitHub.com',
status: '404 Not Found',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
'transfer-encoding': 'chunked',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-oauth-scopes': 'public_repo, repo',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'CEA1:2742:1C206:431AD:5F1B2402',
'x-oauth-client-id': '093b90a514074765c4d8',
'x-oauth-scopes': '',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4996',
'x-ratelimit-reset': '1595617064',
'x-xss-protection': '1; mode=block'
},
request: {
method: 'POST',
url: 'https://api.github.com/user/repos',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-rest.js/18.0.2 octokit-core.js/3.1.1 Node.js/14.6.0 (darwin; x64)',
authorization: 'token [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"name":"hello_world"}',
request: { hook: [Function: bound bound register] }
},
documentation_url: 'https://developer.github.com/v3/repos/#create'
}
noticed that scopes are blank, so maybe I need to format scopes differently? I've seen both scopes
and scope
used and tried both but no dice.
What’s missing?
This module itself with all non-optional dependencies is quite small compared to the size of aws-lambda
and its dependencies, which is +44x the size.
Since optional dependencies are installed by default and there's no simple way of opting out of specific optional dependencies the change in #283 made most users of this module (and in turn the users of octokit
) pull in aws-lambda
and in turn aws-sdk
, which is likely going to be the heaviest of all the modules in their node_modules
folder.
Why?
If aws-lambda
needs to be included as a dependency, then make it so that it will only actively be pulled in by those actively wanting it and avoid the drive-by adding of the aws-lambda
/aws-sdk
to everyone else.
Alternatives you tried
I did try npm ci --no-optional
and that worked, but also causes all optionals to be opt outed from, not just this one, and it was very easy to miss that it would help this nested behemoth from being pulled in.
I think I might be living into a bit of an anti pattern here or at the very least over complicating so please let me know if I am missing something obvious.
I have an Express API that is using oauth-app.js
(middlewares are so nice BTW 🎉) and I have two different React UI's that hit this API based on different things (one helps create a PR one helps to create an issue) I am struggling with two things.
Based on which UI the user came from I want to send them back to the right UI via the I misunderstood redirect URL's and now realize this is the right approach.oauth
callback URL. now from what I can see the state
parameter mentioned here looks like it might work so I am going to try for an implementation with that.
How do I maintain a "session" between the two UI's or even with multiple people hitting the API from one UI, based on my understanding there is essentially one app
instance that is updated on every event i.e:
app.on("token", async ({ token, octokit }) => {
const { data } = await octokit.request("GET /user");
console.log(`Token retrieved for ${data.login}`);
});
Would this mean I need to essentially create an instance of app
for each user session I have? (being completely honest I am new-ish to server side and the notion of sessions has always confused me) or would it simply be better to associate each token
with each user session and create my own octokit
instances as needed with the token added to the headers?
UPDATE After reading this probot post I realize I was approaching this wrong and thinking of how I would make sessions work with the pre existing middleware routes, when instead I need to write my own routes, as for the octokit
instance it looks like updating this based on the session token will be the way to go.
I think #249 might have introduced an issue in the generated types of this package. When using the package in a project that uses TypeScript (versione 4.3.5), I get the following errors after updating to the latest version of @octokit/oauth-app
:
node_modules/@octokit/oauth-app/dist-types/middleware/cloudflare/index.d.ts:4:36 - error TS2304: Cannot find name 'Request'.
4 onUnhandledRequest?: (request: Request) => Response | Promise<Response>;
~~~~~~~
node_modules/@octokit/oauth-app/dist-types/middleware/cloudflare/index.d.ts:4:48 - error TS2304: Cannot find name 'Response'.
4 onUnhandledRequest?: (request: Request) => Response | Promise<Response>;
~~~~~~~~
node_modules/@octokit/oauth-app/dist-types/middleware/cloudflare/index.d.ts:4:67 - error TS2304: Cannot find name 'Response'.
4 onUnhandledRequest?: (request: Request) => Response | Promise<Response>;
~~~~~~~~
node_modules/@octokit/oauth-app/dist-types/middleware/cloudflare/index.d.ts:5:15 - error TS2304: Cannot find name 'Request'.
5 }): (request: Request) => Promise<Response>;
~~~~~~~
node_modules/@octokit/oauth-app/dist-types/middleware/cloudflare/index.d.ts:5:35 - error TS2304: Cannot find name 'Response'.
5 }): (request: Request) => Promise<Response>;
I guess the Request
and Response
types need to be explicitly imported?
Node v10 has been deprecated so there is no need to keep giving support to it in our CI.
You can find more details here
What’s missing?
import { createNodeMiddleware } from "@octokit/oauth-app"
Why?
We currently have an inconsistent naming for a method that exports a http middleware that is compatible with Node's built in http server as well as express. @octokit/oauth-app
currently exports getNodeMiddleware
, but in other modules and in @probot we use the name createNodeMiddleware
.
How
Start by creating /test/deprecations.test.ts
, similar to probot's deprecation tests in v10. The test should import getNodeMiddleware
and use it, then verify that a deprecation message was logged.
You can start a pull request draft at this point.
Then update the existing tests to import createNodeMiddleware
instead of getNodeMiddleware
.
Finally implement the deprecation log when calling getNodeMiddleware
and the new createNodeMiddleware
export
Specifically, I am talking about https://developer.github.com/changes/2020-02-10-deprecating-auth-through-query-param/
I am considering using this package but unsure due to the confusing message regarding deprecation of using clientId + clientSecret, which is similar to the main example of:
const app = new OAuthApp({
clientType: "oauth-app",
clientId: "1234567890abcdef1234",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
});
☝️ Important announcement: Greenkeeper will be saying goodbye 👋 and passing the torch to Snyk on June 3rd, 2020! Find out how to migrate to Snyk and more at greenkeeper.io
25.2.1
to 25.3.0
.This version is covered by your current version range and after updating it in your project the build failed.
ts-jest is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot 🌴
☝️ Important announcement: Greenkeeper will be saying goodbye 👋 and passing the torch to Snyk on June 3rd, 2020! Find out how to migrate to Snyk and more at greenkeeper.io
2.0.3
to 2.0.4
.This version is covered by your current version range and after updating it in your project the build failed.
prettier is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot 🌴
The docs say that refresh tokens are automatically refreshed, but what about after my application restarts? Does the github app still know about all the tokens I am refreshing, and it will still trigger the "token"
event handler?
No response
No response
No response
const { Octokit } = require("@octokit/rest")
const app = new OAuthApp({
clientId: "...",
clientSecret: "...",
Octokit
});
app.octokit.issues.create // should get TypeScript IntelliSense
master
branch failed. 🚨I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here is some links that can help you:
If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.
semantic-release cannot push the version tag to the branch master
on the remote Git repository with URL https://x-access-token:[secure]@github.com/octokit/oauth-app.js
.
This can be caused by:
Good luck with your project ✨
Your semantic-release bot 📦🚀
What’s missing?
Currently links appear to point to the old documentation i.e:
Why?
I wanted to see if this was intentional before I open a PR to resolve.
Alternatives you tried
N/A
This issue contains a list of Renovate updates and their statuses.
These updates are awaiting their schedule. Click on a checkbox to ignore the schedule.
follow up to octokit/webhooks.js#534 by @honnix.
This issue is meant as a reminder to double check that we implemented the node middleware correctly to work with express as expected.
There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.
Location: package.json
Error type: The renovate configuration file contains some invalid settings
Message: Configuration option 'packageRules[0].npm' should be a json object, Invalid configuration option: @pika/pack, Invalid configuration option: author, Invalid configuration option: jest, Invalid configuration option: keywords, Invalid configuration option: license, Invalid configuration option: name, Invalid configuration option: packageRules[0].@octokit/auth-oauth-app, Invalid configuration option: packageRules[0].@octokit/auth-oauth-user, Invalid configuration option: packageRules[0].@octokit/auth-unauthenticated, Invalid configuration option: packageRules[0].@octokit/core, Invalid configuration option: packageRules[0].@octokit/oauth-authorization-url, Invalid configuration option: packageRules[0].@octokit/oauth-methods, Invalid configuration option: packageRules[0].fromentries, Invalid configuration option: packageRules[0].i, Invalid configuration option: packageRules[0].universal-user-agent, Invalid configuration option: packageRules[1].@pika/pack, Invalid configuration option: packageRules[1].@pika/plugin-build-node, Invalid configuration option: packageRules[1].@pika/plugin-ts-standard-pkg, Invalid configuration option: packageRules[1].@types/jest, Invalid configuration option: packageRules[1].@types/node, Invalid configuration option: packageRules[1].@types/node-fetch, Invalid configuration option: packageRules[1].fetch-mock, Invalid configuration option: packageRules[1].jest, Invalid configuration option: packageRules[1].nock, Invalid configuration option: packageRules[1].node-fetch, Invalid configuration option: packageRules[1].prettier, Invalid configuration option: packageRules[1].semantic-release, Invalid configuration option: packageRules[1].semantic-release-plugin-update-version-in-files, Invalid configuration option: packageRules[1].ts-jest, Invalid configuration option: packageRules[1].typescript, Invalid configuration option: publishConfig, Invalid configuration option: release, Invalid configuration option: renovate, Invalid configuration option: scripts, Invalid configuration option: version, The "npm" object can only be configured at the top level of a config but was found inside "packageRules[0]"
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.