Skip to content

Commit

Permalink
Init files
Browse files Browse the repository at this point in the history
  • Loading branch information
kkweon committed Aug 12, 2019
1 parent b68652b commit aa47e51
Show file tree
Hide file tree
Showing 9 changed files with 672 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
node_modules
9 changes: 9 additions & 0 deletions .prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
printWidth: 80
tabWidth: 2
useTabs: false
semi: false
singleQuote: true
trailingComma: all
bracketSpacing: true
jsxBracketSameLine: false
arrowParens: avoid
11 changes: 11 additions & 0 deletions members.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
admin:
- deep-diver
- kkweon
members:
- Sihan-Son
- arin-kwak
- coffeedjimmy
- jehyunlee
- olramde
- taegon
- mukaman84
18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "signup-req",
"version": "1.0.0",
"main": "src/index.ts",
"repository": "[email protected]:fast-ai-kr/signup-req.git",
"author": "fast-ai-kr",
"license": "MIT",
"private": false,
"dependencies": {
"@octokit/rest": "^16.28.7",
"js-yaml": "^3.13.1",
"typescript": "^3.5.3"
},
"devDependencies": {
"@types/js-yaml": "^3.12.1",
"ts-node": "^8.3.0"
}
}
90 changes: 90 additions & 0 deletions src/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Octokit from '@octokit/rest'

export interface GitHubServiceConfig {
org: string
}

export class GitHubService {
private cache = new Map()
constructor(private octokit: Octokit, private config: GitHubServiceConfig) {}

async getTeamByName(name: string): Promise<Octokit.TeamsGetByNameResponse> {
if (this.cache.has(name)) {
return Promise.resolve(this.cache.get(name))
}

return this.octokit.teams
.getByName({ org: this.config.org, team_slug: name })
.then(res => {
this.cache.set(name, res.data)
return res.data
})
}

async getTeams(): Promise<Octokit.TeamsListResponse> {
const getTeamsCacheId = '__TEAMS__'

if (this.cache.has(getTeamsCacheId))
return Promise.resolve(this.cache.get(getTeamsCacheId))

return this.octokit.teams
.list({ org: this.config.org })
.then(res => res.data)
.then(teams => {
teams.forEach(team => {
this.cache.set(team.name, team)
})

this.cache.set(getTeamsCacheId, teams)

return teams
})
}

async inviteToMaintainerTeam(username: string) {
const maintainerTeam = await this.getTeamByName('maintainer')
return this.octokit.teams.addOrUpdateMembership({
team_id: maintainerTeam.id,
username,
})
}

async inviteToAdminTeam(username: string) {
return this.octokit.orgs.addOrUpdateMembership({
org: this.config.org,
username,
role: 'admin',
})
}

async removeMember(username: string) {
return this.octokit.orgs.removeMembership({
org: this.config.org,
username,
})
}

async getAdminMembers(): Promise<Octokit.OrgsListMembersResponse> {
const getAdminMembersCacheId = '__GET_ADMIN_MEMEBERS_CACHE_ID__'
return this.getMembersInOrg(getAdminMembersCacheId, true)
}

async getMaintainerMembers(): Promise<Octokit.OrgsListMembersResponse> {
const getMaintainerMembersCacheId = '__GET_MAINTAINER_MEMBERS_CACHE_ID'
return this.getMembersInOrg(getMaintainerMembersCacheId, false)
}

private async getMembersInOrg(cacheId: string, admin: boolean) {
const ret = this.cache.get(cacheId)
if (ret) return Promise.resolve(ret)
return this.octokit.orgs
.listMembers({
org: this.config.org,
role: admin ? 'admin' : 'member',
})
.then(res => {
this.cache.set(cacheId, res.data)
return res.data
})
}
}
57 changes: 57 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Octokit from '@octokit/rest'
import { GitHubService } from './github'
import { MembersService } from './members-service'
import path from 'path'

const org = 'fast-ai-kr'
const shouldSubmit = !!process.env.SHOULD_SUBMIT || false

function buildOctokit(): Octokit {
const auth = process.env.GITHUB_AUTH
if (!auth) {
throw new Error('GITHUB_AUTH is required')
}
return new Octokit({
auth,
})
}

async function main(send = false) {
const service = new GitHubService(buildOctokit(), { org })
const membersService = new MembersService(path.resolve('members.yaml'))

const currentMembers = (await service.getMaintainerMembers()).map(
m => m.login,
)

const newMembersToAdd = membersService.getMembersToAdd(currentMembers)
const newMembersDelete = membersService.getMembersToDelete(currentMembers)

const currentAdmins = (await service.getAdminMembers()).map(m => m.login)
const newAdminsToAdd = membersService.getMembersToAdd(currentAdmins, true)
const newAdminsToDelete = membersService.getMembersToDelete(
currentAdmins,
true,
)

console.log({
newMembersToAdd,
newMembersDelete,
newAdminsToAdd,
newAdminsToDelete,
})

if (send) {
return Promise.all(
newMembersToAdd
.map(m => service.inviteToMaintainerTeam(m))
.concat(newMembersDelete.map(m => service.removeMember(m)))
.concat(newAdminsToAdd.map(m => service.inviteToAdminTeam(m)))
.concat(newAdminsToDelete.map(m => service.removeMember(m))),
)
}

return Promise.resolve(true)
}

main(shouldSubmit).then(console.log)
67 changes: 67 additions & 0 deletions src/members-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import fs from 'fs'
import yaml from 'js-yaml'

export class MembersService {
data: {
admin: Set<string>
members: Set<string>
}

constructor(private filename: string) {
this.data = {
admin: new Set(),
members: new Set(),
}
const membersDataFile = yaml.safeLoad(fs.readFileSync(filename, 'utf8'))
this.data.members = new Set(membersDataFile.members)
this.data.admin = new Set(membersDataFile.admin)
}

addMember(member: string) {
this.data.members.add(member)
}

hasMember(member: string): boolean {
return this.data.members.has(member)
}

addAdmin(member: string) {
this.data.admin.add(member)
}

hadAdmin(member: string): boolean {
return this.data.admin.has(member)
}

getMembersToAdd(currentMembers: string[], admin: boolean = false): string[] {
const currentMembersSet = new Set(currentMembers)
const data = admin ? this.data.admin : this.data.members
return Array.from(new Set([...data].filter(m => !currentMembersSet.has(m))))
}

getMembersToDelete(
currentMembers: string[],
admin: boolean = false,
): string[] {
const s = admin ? this.data.admin : this.data.members
return currentMembers.filter(m => !s.has(m))
}

write() {
fs.writeFileSync(
this.filename,
yaml.safeDump(this.getDump(), { sortKeys: true }),
)
}

private getDump() {
return {
admin: this.cleanArray(this.data.admin),
members: this.cleanArray(this.data.members),
}
}

private cleanArray(xs: Set<string>) {
return [...Array.from(xs)].sort()
}
}
63 changes: 63 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist" /* Redirect output structure to the directory. */,
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
"removeComments": true /* Do not emit comments to output. */,
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */,
"noUnusedParameters": true /* Report errors on unused parameters. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,

/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */

/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
Loading

0 comments on commit aa47e51

Please sign in to comment.