Skip to content

Commit d054d51

Browse files
authored
Merge pull request #14 from froi/implement-device-auth-flow
Implement device auth flow
2 parents 7b857fb + 60b7e59 commit d054d51

File tree

9 files changed

+1125
-427
lines changed

9 files changed

+1125
-427
lines changed

package-lock.json

Lines changed: 542 additions & 198 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
{
22
"name": "gh-project-migrator",
33
"version": "1.0.1",
4-
"description": "Helper script to migrate columns and crads from a GitHub project to another.",
4+
"description": "Application that helps you move your GitHub project boards between repositories and organizations.",
55
"main": "dist/cli.js",
6+
"bin": {
7+
"gh-project-migrator": "dist/cli.js"
8+
},
69
"dependencies": {
710
"@octokit/graphql": "^4.5.7",
8-
"inquirer": "^7.3.3"
11+
"commander": "^6.2.0",
12+
"date-fns": "^2.16.1",
13+
"inquirer": "^7.3.3",
14+
"node-fetch": "^2.6.1",
15+
"open": "^7.3.0",
16+
"yaml": "^1.10.0"
917
},
1018
"devDependencies": {
1119
"@octokit/types": "^2.16.2",
1220
"@types/inquirer": "^7.3.1",
1321
"@types/jest": "^25.2.3",
1422
"@types/node": "^13.13.31",
15-
"@typescript-eslint/eslint-plugin": "^2.34.0",
16-
"@typescript-eslint/parser": "^2.34.0",
23+
"@types/node-fetch": "^2.5.7",
24+
"@typescript-eslint/eslint-plugin": "^4.10.0",
25+
"@typescript-eslint/parser": "^4.10.0",
1726
"copyfiles": "^2.4.1",
1827
"dotenv": "^8.2.0",
19-
"eslint": "^6.8.0",
20-
"eslint-plugin-jest": "^23.20.0",
28+
"eslint": "^7.16.0",
29+
"eslint-plugin-jest": "^24.1.3",
2130
"jest": "^25.5.4",
2231
"lint-staged": "^10.5.1",
2332
"prettier": "^2.1.2",

src/cli.ts

Lines changed: 138 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,150 @@
1+
#!/usr/bin/env node
2+
13
import * as inquirer from 'inquirer';
2-
import {validateProjectNumberInput, validateRepoInput, splitRepo} from './libs/utils';
3-
import {WorkItem, WorkItemType, DefaultCliAnswers, OrgToOrgCliAnswers} from './libs/types';
4+
import {splitRepo, getConfig} from './libs/utils';
5+
import {WorkItem, WorkItemType, DefaultCliAnswers, OrgToOrgCliAnswers, ActionsTypes} from './libs/types';
6+
import * as prompts from './libs/prompts';
47
import {migrate} from './main';
8+
import {auth} from './libs/auth';
9+
import {program} from 'commander';
10+
11+
async function repoToOrg(gitHubHost: string): Promise<void> {
12+
const answers: DefaultCliAnswers | OrgToOrgCliAnswers = await inquirer.prompt(prompts.REPO_TO_ORG_PROMPTS) as DefaultCliAnswers;
13+
const source: WorkItem = {
14+
type: WorkItemType.REPO,
15+
value: splitRepo(answers.source),
16+
project: parseInt(answers.sourceProjectNumber)
17+
};
18+
const target: WorkItem = {
19+
type: WorkItemType.ORG,
20+
value: {
21+
name: answers.target,
22+
},
23+
project: parseInt(answers.targetProjectNumber)
24+
};
25+
for await(const result of migrate(source, target, gitHubHost)){
26+
const column = result.addProjectCard.cardEdge.node.column;
27+
const messsage = `Card created for project ${column.project.name} in column ${column.name}`;
28+
29+
console.log(messsage);
30+
}
31+
}
32+
async function orgToRepo(gitHubHost: string): Promise<void> {
33+
const answers: DefaultCliAnswers | OrgToOrgCliAnswers = await inquirer.prompt(prompts.ORG_TO_REPO_PROMPTS) as DefaultCliAnswers;
34+
const source: WorkItem = {
35+
type: WorkItemType.ORG,
36+
value: {
37+
name: answers.source,
38+
},
39+
project: parseInt(answers.sourceProjectNumber)
40+
};
41+
const target: WorkItem = {
42+
type: WorkItemType.REPO,
43+
value: splitRepo(answers.target),
44+
project: parseInt(answers.targetProjectNumber)
45+
};
46+
for await(const result of migrate(source, target, gitHubHost)){
47+
const column = result.addProjectCard.cardEdge.node.column;
48+
const messsage = `Card created for project ${column.project.name} in column ${column.name}`;
49+
50+
console.log(messsage);
51+
}
52+
}
53+
async function repoToRepo(gitHubHost: string): Promise<void> {
54+
const answers: DefaultCliAnswers | OrgToOrgCliAnswers = await inquirer.prompt(prompts.REPO_TO_REPO_PROMPTS) as DefaultCliAnswers;
55+
const source: WorkItem = {
56+
type: WorkItemType.REPO,
57+
value: splitRepo(answers.source),
58+
project: parseInt(answers.sourceProjectNumber)
59+
};
60+
const target: WorkItem = {
61+
type: WorkItemType.REPO,
62+
value: splitRepo(answers.target),
63+
project: parseInt(answers.targetProjectNumber)
64+
};
65+
for await(const result of migrate(source, target, gitHubHost)){
66+
const column = result.addProjectCard.cardEdge.node.column;
67+
const messsage = `Card created for project ${column.project.name} in column ${column.name}`;
68+
69+
console.log(messsage);
70+
}
71+
}
72+
async function orgToOrg(gitHubHost: string): Promise<void> {
73+
const answers: DefaultCliAnswers | OrgToOrgCliAnswers = await inquirer.prompt(prompts.ORG_TO_ORG_PROMPTS) as OrgToOrgCliAnswers;
74+
const source: WorkItem = {
75+
type: WorkItemType.ORG,
76+
value: {
77+
name: answers.org
78+
},
79+
project: parseInt(answers.sourceProjectNumber)
80+
};
81+
const target: WorkItem = {
82+
type: WorkItemType.ORG,
83+
value: {
84+
name: answers.org
85+
},
86+
project: parseInt(answers.targetProjectNumber)
87+
};
88+
for await(const result of migrate(source, target, gitHubHost)){
89+
const column = result.addProjectCard.cardEdge.node.column;
90+
const messsage = `Card created for project ${column.project.name} in column ${column.name}`;
91+
92+
console.log(messsage);
93+
}
94+
}
595

696
async function main(): Promise<void> {
7-
const {action} = await inquirer.prompt([
8-
{
9-
type: 'list',
10-
name: 'action',
11-
choices: [
12-
{
13-
name:'Migrate from repository to organization level',
14-
value: 'repoToOrg'
15-
},
16-
{
17-
name: 'Migrate from an organization to a repository',
18-
value: 'orgToRepo'
19-
},
20-
{
21-
name: 'Migrate between repositories',
22-
value: 'repoToRepo'
23-
}
24-
]
25-
}
26-
]);
2797

28-
let source: WorkItem;
29-
let target: WorkItem;
30-
let answers: DefaultCliAnswers | OrgToOrgCliAnswers;
31-
switch(action) {
32-
case 'repoToOrg':
33-
answers = await inquirer.prompt([
34-
{
35-
type: "input",
36-
message: "What is the source repository (owner/repo_name)?",
37-
name: "source",
38-
validate: validateRepoInput
39-
},
40-
{
41-
type: "input",
42-
message: "What is the project number you wish to migrate?",
43-
name: "sourceProjectNumber",
44-
validate: validateProjectNumberInput
45-
},
46-
{
47-
type: "input",
48-
message: "What is the organization you wish to migrate to?",
49-
name: "target",
50-
validate: (input: string) => {
51-
return input ? true : false;
52-
}
53-
},
54-
{
55-
type: "input",
56-
message: "What is the target project number?",
57-
name: "targetProjectNumber",
58-
validate: validateProjectNumberInput
59-
}
60-
]) as DefaultCliAnswers;
61-
source = {
62-
type: WorkItemType.REPO,
63-
value: splitRepo(answers.source),
64-
project: parseInt(answers.sourceProjectNumber)
65-
};
66-
target = {
67-
type: WorkItemType.ORG,
68-
value: {
69-
name: answers.target,
70-
},
71-
project: parseInt(answers.targetProjectNumber)
72-
};
73-
for await(const result of migrate(source, target)){
74-
console.log(JSON.stringify(result, null, 2));
98+
program
99+
.command('migrate')
100+
.action(async () => {
101+
const gitHubHost = 'github.com';
102+
let config = getConfig(gitHubHost);
103+
104+
if(!config || !config.oauth_token) {
105+
config = await auth(gitHubHost);
75106
}
76-
break;
77-
case 'orgToRepo':
78-
answers = await inquirer.prompt([
79-
{
80-
type: "input",
81-
message: "What is the source organization?",
82-
name: "source",
83-
validate: async (input: string) => {
84-
return input ? true : false;
85-
}
86-
},
87-
{
88-
type: "input",
89-
message: "What is the project number you wish to migrate?",
90-
name: "sourceProjectNumber",
91-
validate: validateProjectNumberInput
92-
},
93-
{
94-
type: "input",
95-
message: "What is the repository you wish to migrate to (owner/repo)?",
96-
name: "target",
97-
validate: validateRepoInput
98-
},
99-
{
100-
type: "input",
101-
message: "What is the target project number?",
102-
name: "targetProjectNumber",
103-
validate: validateProjectNumberInput
104-
}
105-
]) as DefaultCliAnswers;
106-
source = {
107-
type: WorkItemType.ORG,
108-
value: {
109-
name: answers.source,
110-
},
111-
project: parseInt(answers.sourceProjectNumber)
112-
};
113-
target = {
114-
type: WorkItemType.REPO,
115-
value: splitRepo(answers.target),
116-
project: parseInt(answers.targetProjectNumber)
117-
};
118-
for await(const result of migrate(source, target)){
119-
console.log(JSON.stringify(result, null, 2));
107+
108+
const { action } = await inquirer.prompt(prompts.ACTIONS_PROMPTS);
109+
110+
switch(action) {
111+
case ActionsTypes.REPO_TO_ORG:
112+
await repoToOrg(gitHubHost);
113+
break;
114+
case ActionsTypes.ORG_TO_REPO:
115+
await orgToRepo(gitHubHost);
116+
break;
117+
case ActionsTypes.REPO_TO_REPO:
118+
await repoToRepo(gitHubHost);
119+
break;
120+
case ActionsTypes.ORG_TO_ORG:
121+
await orgToOrg(gitHubHost);
122+
break;
123+
default:
124+
console.log("Nope");
120125
}
121-
break;
122-
case 'repoToRepo':
123-
answers = await inquirer.prompt([
124-
{
125-
type: "input",
126-
message: "What is the repository you wish to migrate from (owner/repo)?",
127-
name: "source",
128-
validate: validateRepoInput
129-
},
130-
{
131-
type: "input",
132-
message: "What is the project number you wish to migrate?",
133-
name: "sourceProjectNumber",
134-
validate: validateProjectNumberInput
135-
},
136-
{
137-
type: "input",
138-
message: "What is the repository you wish to migrate to (owner/repo)?",
139-
name: "target",
140-
validate: validateRepoInput
141-
},
142-
{
143-
type: "input",
144-
message: "What is the target project number?",
145-
name: "targetProjectNumber",
146-
validate: validateProjectNumberInput
126+
});
127+
program.command('auth')
128+
.action(async () => {
129+
// const homeDir = homedir();
130+
const gitHubHost = 'github.com';
131+
const config = getConfig(gitHubHost);
132+
let execAuth = true;
133+
134+
if(config && config.oauth_token) {
135+
const {reAuth} = await inquirer.prompt([{
136+
type: 'confirm',
137+
message: `You've already authenticated. Do you wish to do so again?`,
138+
name: 'reAuth'
139+
}]) as { reAuth: boolean};
140+
execAuth = reAuth;
141+
}
142+
if( execAuth ) {
143+
await auth(gitHubHost);
147144
}
148-
]) as DefaultCliAnswers;
145+
});
149146

150-
source = {
151-
type: WorkItemType.REPO,
152-
value: splitRepo(answers.source),
153-
project: parseInt(answers.sourceProjectNumber)
154-
};
155-
target = {
156-
type: WorkItemType.REPO,
157-
value: splitRepo(answers.target),
158-
project: parseInt(answers.targetProjectNumber)
159-
};
160-
for await(const result of migrate(source, target)){
161-
console.log(JSON.stringify(result, null, 2));
162-
}
163-
break;
164-
case 'orgToOrg':
165-
answers = await inquirer.prompt([
166-
{
167-
type: "input",
168-
message: "What organization are the projects in?",
169-
name: "org"
170-
},
171-
{
172-
type: "input",
173-
message: "What is the project number you wish to migrate?",
174-
name: "sourceProjectNumber",
175-
validate: validateProjectNumberInput
176-
},
177-
{
178-
type: "input",
179-
message: "What is the project number you wish to migrate to?",
180-
name: "targetProjectNumber",
181-
validate: validateProjectNumberInput
182-
},
183-
]) as OrgToOrgCliAnswers;
184-
source = {
185-
type: WorkItemType.ORG,
186-
value: {
187-
name: answers.org
188-
},
189-
project: parseInt(answers.sourceProjectNumber)
190-
};
191-
target = {
192-
type: WorkItemType.ORG,
193-
value: {
194-
name: answers.org
195-
},
196-
project: parseInt(answers.targetProjectNumber)
197-
};
198-
for await(const result of migrate(source, target)){
199-
console.log(JSON.stringify(result, null, 2));
200-
}
201-
break;
202-
default:
203-
console.log("Nope");
204-
}
147+
program.parseAsync(process.argv);
205148
}
206149

207150
main()

0 commit comments

Comments
 (0)