Skip to content

Commit 457a2be

Browse files
feat: tool and label rework
Feat tool label rework
2 parents 3a07da7 + e3e6af5 commit 457a2be

File tree

6 files changed

+263
-159
lines changed

6 files changed

+263
-159
lines changed

src/app.ts

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,109 @@
11
import { Command } from "@commander-js/extra-typings";
22

3-
import { SyncArguments, SyncOptions } from "./types/labels";
4-
import { sync } from "./labels";
3+
import LabelSync from "./labels";
4+
import { Action, type SyncOptions, type Tokens } from "./types";
5+
import logger, { setVerbose } from "./log";
6+
import { promptMultiple } from "./io";
57
import { name, description, version } from "../package.json";
68

7-
new Command<SyncArguments, SyncOptions>()
9+
const validateTokens = (opts: SyncOptions): Tokens | null => {
10+
const token = opts.token || process.env.GITHUB_TOKEN;
11+
if (token) {
12+
return { destToken: token, originToken: token };
13+
}
14+
15+
const originToken = opts.tokenOrigin || process.env.GITHUB_TOKEN_ORIGIN;
16+
if (!originToken) {
17+
logger.error("No origin token specified and couldn't find any in environment");
18+
return null;
19+
}
20+
21+
const destToken = opts.tokenDestination || process.env.GITHUB_TOKEN_DESTINATION;
22+
if (!destToken) {
23+
logger.error("No destination token specified and couldn't find any in environment");
24+
return null;
25+
}
26+
27+
return { destToken, originToken };
28+
};
29+
30+
new Command()
831
.name(name)
932
.description(description)
1033
.version(version)
1134
.showHelpAfterError("(add --help for additional information)")
1235

13-
.argument("origin <string>", "Repository where the labels we want to keep are: (`{owner}/{repository}`)")
14-
.argument("target <string>", "Repository whose labels we're updating (`{owner}/{repository}`)")
36+
.argument("origin", "Repository that has the configs you want to sync: (`{owner}/{repository}`)")
37+
.argument("destination", "Repository where you want to update the configs: (`{owner}/{repository}`)")
38+
39+
.option(
40+
"--token <token>",
41+
"Auth token to allow gh-sync to do it's thing. Use this if the token is the same for both origin and destination repositories",
42+
)
43+
.option("--token-origin <token>", "Auth token of the origin repository, to allow gh-sync to do it's thing")
44+
.option(
45+
"--token-destination <token>",
46+
"Auth token of the destination repository,to allow gh-sync to do it's thing to allow gh-sync to do it's thing",
47+
)
48+
.option("--verbose", `Runs ${name} in verbose mode`, false)
49+
50+
.action(async function (origin, destination, options: SyncOptions) {
51+
if (options.verbose) {
52+
setVerbose();
53+
}
54+
55+
if (origin === destination) {
56+
logger.error("`origin` and `destination` can't be the same");
57+
return;
58+
}
59+
60+
const tokens = validateTokens(options);
61+
if (!tokens) {
62+
return;
63+
}
64+
65+
const actions = await promptMultiple<string[]>("What do you want to sync ? (you can choose multiple)", [
66+
// fine grained:
67+
// issues read / write; (get and add)
68+
// pull requests read / write; (delete labels)
69+
// metadata read (mandatory)
70+
{ label: "Labels", value: Action.LABELS, hint: "Check documentation for necessary permissions" },
71+
{ label: "Workflows", value: Action.WORKFLOWS, hint: "Check documentation for necessary permissions" },
72+
{ label: "Branch rules", value: Action.BRANCH_RULES, hint: "Check documentation for necessary permissions" },
73+
]);
74+
75+
let labelSync: LabelSync | null = null;
76+
77+
if (actions.includes(Action.LABELS)) {
78+
labelSync = new LabelSync(origin, destination, tokens);
79+
labelSync.prepare();
80+
}
81+
82+
if (Action.WORKFLOWS in actions) {
83+
// stuff with workflows
84+
}
85+
86+
if (Action.BRANCH_RULES in actions) {
87+
// stuff with branch rules
88+
}
89+
90+
logger.box("Before we proceed, just double checking all the actions..");
91+
92+
// confirm things
93+
if (labelSync) {
94+
labelSync.confirmChoices();
95+
}
96+
97+
logger.info("Starting sync process...");
1598

16-
.option("-t, --token <string>", "GitHub token to be able to do stuff in GutHub")
17-
.option("--verbose", `Runs ${name} in verbose mode`)
18-
// .option("--clean", "Will delete any existing in the target repos before synchronizing origin's tags")
99+
// process things
100+
if (labelSync) {
101+
logger.info("Syncing labels...");
102+
labelSync.sync();
103+
logger.info("Finished syncing labels");
104+
}
19105

20-
// @ts-expect-error todo: fix typing
21-
.action(sync)
106+
logger.success("GitHub configurations sync'd");
107+
})
22108

23109
.parse();

src/io.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import logger from "./log";
2+
3+
const BASE_URL = "https://api.github.com";
4+
5+
export const request = async <T>(endpoint: string, method: string, token: string, body?: BodyInit | null) => {
6+
return new Promise<T>((resolve, reject) => {
7+
fetch(`${BASE_URL}/repos/${endpoint}`, {
8+
method,
9+
body,
10+
headers: {
11+
Authorization: `Bearer ${token}`,
12+
"Content-Type": "application/json",
13+
"X-GitHub-Api-Version": "2022-11-28",
14+
},
15+
})
16+
.then(async (response) => {
17+
const json = await response.json();
18+
19+
if ([200, 201, 204].includes(response.status)) {
20+
resolve(json);
21+
} else {
22+
reject(json);
23+
}
24+
})
25+
.catch(reject);
26+
});
27+
};
28+
29+
interface SelectOption {
30+
label: string;
31+
value: string;
32+
hint?: string;
33+
}
34+
35+
export const promptMultiple = async <T>(message: string, options: SelectOption[]): Promise<T> => {
36+
return (await logger.prompt(message, { cancel: "reject", type: "multiselect", required: false, options })) as T;
37+
};

0 commit comments

Comments
 (0)