1 | #!/usr/bin/env node
|
2 | import Chalk from 'chalk';
|
3 | import Sywac from 'sywac';
|
4 | import * as Gitbeaker from '@gitbeaker/rest';
|
5 | import API_MAP from '@gitbeaker/core/map.json' assert { type: 'json' };
|
6 | import { decamelize, depascalize, camelize } from 'xcase';
|
7 |
|
8 | function param(value) {
|
9 | let cleaned = value;
|
10 | const exceptions = [
|
11 | "GitLabCI",
|
12 | "YML",
|
13 | "GPG",
|
14 | "SSH",
|
15 | "IId",
|
16 | "NPM",
|
17 | "NuGet",
|
18 | "DORA4",
|
19 | "LDAP",
|
20 | "CICD",
|
21 | "SAML",
|
22 | "SCIM",
|
23 | "PyPI"
|
24 | ];
|
25 | exceptions.filter((e) => value.includes(e)).forEach((ex) => {
|
26 | cleaned = cleaned.replace(ex, ex.charAt(0).toUpperCase() + ex.slice(1).toLowerCase());
|
27 | });
|
28 | const decamelized = decamelize(cleaned, "-");
|
29 | return decamelized !== cleaned ? decamelized : depascalize(cleaned, "-");
|
30 | }
|
31 | function normalizeEnviromentVariables(env) {
|
32 | const normalized = {};
|
33 | const suffixes = [
|
34 | "TOKEN",
|
35 | "OAUTH_TOKEN",
|
36 | "JOB_TOKEN",
|
37 | "HOST",
|
38 | "SUDO",
|
39 | "CAMELIZE",
|
40 | "REQUEST_TIMEOUT",
|
41 | "PROFILE_TOKEN",
|
42 | "PROFILE_MODE"
|
43 | ];
|
44 | suffixes.forEach((s) => {
|
45 | if (env[`GITLAB_${s}`])
|
46 | normalized[`GITBEAKER_${s}`] = env[`GITLAB_${s}`];
|
47 | if (env[`GITBEAKER_${s}`])
|
48 | normalized[`GITBEAKER_${s}`] = env[`GITBEAKER_${s}`];
|
49 | });
|
50 | return normalized;
|
51 | }
|
52 | function buildArgumentObjects(globalConfig, method, rawArgs) {
|
53 | const ignoreOptions = ["_", "$0", "v", "version", "h", "help", "g", "global-args"];
|
54 | const coreArgs = {};
|
55 | const optionalArgs = {};
|
56 | const initArgs = {};
|
57 | Object.entries(rawArgs).forEach(([argName, value]) => {
|
58 | if (ignoreOptions.includes(argName) || value == null)
|
59 | return;
|
60 | const camelCased = camelize(argName.replace("gb-", "").replace("gl-", ""), "-");
|
61 | if (globalConfig[argName.replace("gl-", "gb-")]) {
|
62 | initArgs[camelCased] = value;
|
63 | } else if (method.args.includes(camelCased))
|
64 | coreArgs[camelCased] = value;
|
65 | else
|
66 | optionalArgs[camelCased] = value;
|
67 | });
|
68 | return {
|
69 | initArgs,
|
70 | coreArgs,
|
71 | optionalArgs
|
72 | };
|
73 | }
|
74 | function getDisplayConfig(globalConfig) {
|
75 | const display = {};
|
76 | Object.entries(globalConfig).forEach(([k, v]) => {
|
77 | if (v.defaultValue == null)
|
78 | return;
|
79 | display[k] = {
|
80 | alias: v.alias,
|
81 | description: v.desc,
|
82 | value: v.defaultValue
|
83 | };
|
84 | });
|
85 | return display;
|
86 | }
|
87 | function getGlobalConfig(env = process.env) {
|
88 | const normalEnv = normalizeEnviromentVariables(env);
|
89 | return {
|
90 | "gb-token": {
|
91 | alias: "gl-token",
|
92 | desc: "Your Gitlab Personal Token",
|
93 | type: "string",
|
94 | defaultValue: normalEnv.GITBEAKER_TOKEN
|
95 | },
|
96 | "gb-oauth-token": {
|
97 | alias: "gl-oauth-token",
|
98 | desc: "Your Gitlab OAuth Token",
|
99 | type: "string",
|
100 | defaultValue: normalEnv.GITBEAKER_OAUTH_TOKEN
|
101 | },
|
102 | "gb-job-token": {
|
103 | alias: "gl-job-token",
|
104 | desc: "Your Gitlab Job Token",
|
105 | type: "string",
|
106 | defaultValue: normalEnv.GITBEAKER_JOB_TOKEN
|
107 | },
|
108 | "gb-host": {
|
109 | alias: "gl-host",
|
110 | desc: "Your Gitlab API host (Defaults to https://www.gitlab.com)",
|
111 | type: "string",
|
112 | defaultValue: normalEnv.GITBEAKER_HOST
|
113 | },
|
114 | "gb-sudo": {
|
115 | alias: "gl-sudo",
|
116 | desc: "[Sudo](https://docs.gitlab.com/ee/api/#sudo) query parameter",
|
117 | type: "string",
|
118 | defaultValue: normalEnv.GITBEAKER_SUDO
|
119 | },
|
120 | "gb-camelize": {
|
121 | alias: "gl-camelize",
|
122 | desc: "Camelizes all response body keys",
|
123 | type: "boolean",
|
124 | defaultValue: normalEnv.GITBEAKER_CAMELIZE
|
125 | },
|
126 | "gb-request-timeout": {
|
127 | alias: "gl-request-timeout",
|
128 | desc: "Timeout for API requests. Measured in ms",
|
129 | type: "number",
|
130 | defaultValue: normalEnv.GITBEAKER_REQUEST_TIMEOUT && parseInt(normalEnv.GITBEAKER_REQUEST_TIMEOUT, 10)
|
131 | },
|
132 | "gb-profile-token": {
|
133 | alias: "gl-profile-token",
|
134 | desc: "[Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html)",
|
135 | type: "string",
|
136 | defaultValue: normalEnv.GITBEAKER_PROFILE_TOKEN
|
137 | },
|
138 | "gb-profile-mode": {
|
139 | alias: "gl-profile-mode",
|
140 | desc: "[Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html)",
|
141 | type: "string",
|
142 | defaultValue: normalEnv.GITBEAKER_PROFILE_MODE
|
143 | }
|
144 | };
|
145 | }
|
146 | function getExposedAPIs(map) {
|
147 | const { Gitlab, AccessLevel, ...exposed } = map;
|
148 | return exposed;
|
149 | }
|
150 |
|
151 |
|
152 | function setupAPIMethods(setupArgs, methodArgs) {
|
153 | methodArgs.forEach((name) => {
|
154 | setupArgs.positional(`[--${param(name)}] <${param(name)}>`, {
|
155 | group: "Required Options",
|
156 | type: "string"
|
157 | });
|
158 | });
|
159 | return setupArgs;
|
160 | }
|
161 | function runAPIMethod(ctx, args, apiName, method) {
|
162 | const globalConfig = getGlobalConfig();
|
163 | const { initArgs, coreArgs, optionalArgs } = buildArgumentObjects(globalConfig, method, args);
|
164 | const s = new Gitbeaker[apiName](initArgs);
|
165 | return s[method.name](...Object.values(coreArgs), optionalArgs).then((r) => {
|
166 | ctx.output = JSON.stringify(r, null, 3);
|
167 | }).catch((e) => {
|
168 | ctx.output = e;
|
169 | });
|
170 | }
|
171 | function setupAPIs(setupArgs, apiName, methods) {
|
172 | const globalConfig = getGlobalConfig();
|
173 | Object.entries(globalConfig).forEach(([k, v]) => {
|
174 | setupArgs.option(`${k} <value>`, {
|
175 | group: "Base Options",
|
176 | ...v
|
177 | });
|
178 | });
|
179 | for (let i = 1; i < methods.length; i += 1) {
|
180 | const method = methods[i];
|
181 | setupArgs.command(param(method.name), {
|
182 | setup: (setupMethodArgs) => setupAPIMethods(setupMethodArgs, method.args),
|
183 | run: (args, ctx) => runAPIMethod(ctx, args, apiName, method)
|
184 | });
|
185 | }
|
186 | return setupArgs;
|
187 | }
|
188 | var commandStyle = Chalk.hex("#e34329").bold;
|
189 | var groupStyle = Chalk.hex("#fca325").bold;
|
190 | var usageStyle = Chalk.hex("#fc6e26").bold;
|
191 | var optionStyle = Chalk.white.bold;
|
192 | var descriptionStyle = Chalk.hex("#848484");
|
193 | var hintStyle = Chalk.hex("#6a5f88");
|
194 | var cli = Sywac.version("-v, --version").help("-h, --help").showHelpByDefault().epilogue("Copyright 2023").style({
|
195 | usagePrefix: usageStyle,
|
196 | group: groupStyle,
|
197 | flags: optionStyle,
|
198 | usageCommandPlaceholder: commandStyle,
|
199 | usageOptionsPlaceholder: optionStyle,
|
200 | desc: descriptionStyle,
|
201 | hints: hintStyle
|
202 | });
|
203 | cli.boolean("-g --global-args", {
|
204 | desc: "Show global arguments currently set in the environment variables"
|
205 | });
|
206 | cli.command("*", (argv, ctx) => {
|
207 | if (!argv.g)
|
208 | return;
|
209 | const globalConfig = getGlobalConfig();
|
210 | const display = getDisplayConfig(globalConfig);
|
211 | ctx.output = Object.keys(display).length === 0 ? "No global variables have been set!" : JSON.stringify(display, null, 3);
|
212 | });
|
213 | var exposedAPIs = getExposedAPIs(API_MAP);
|
214 | Object.entries(exposedAPIs).forEach(([apiName, methods]) => {
|
215 | cli.command(param(apiName), {
|
216 | desc: `The ${apiName} API`,
|
217 | setup: (setupArgs) => setupAPIs(setupArgs, apiName, methods)
|
218 | });
|
219 | });
|
220 |
|
221 |
|
222 | cli.parseAndExit();
|