polyci 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +74 -25
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -4,22 +4,65 @@ import * as path from "node:path";
|
|
|
4
4
|
import pino from "pino";
|
|
5
5
|
import pretty from "pino-pretty";
|
|
6
6
|
const log = pino(pretty());
|
|
7
|
-
const
|
|
7
|
+
const DEFAULT_TAG_TEMPLATE = "{module}-v{version}";
|
|
8
|
+
const DEFAULT_BRANCH_TAG_TEMPLATE = "{module}-v{version}-{branch}-{increment}";
|
|
9
|
+
const ALLOWED_TAG_PLACEHOLDERS = new Set(["module", "branch", "version", "increment"]);
|
|
10
|
+
function applyTagTemplate(template, moduleName) {
|
|
11
|
+
return template
|
|
12
|
+
.replaceAll("{module}", moduleName)
|
|
13
|
+
.replaceAll("{branch}", "${CI_COMMIT_BRANCH}")
|
|
14
|
+
.replaceAll("{version}", "${NEXT_VERSION}")
|
|
15
|
+
.replaceAll("{increment}", "${NEXT_INCREMENT}");
|
|
16
|
+
}
|
|
17
|
+
function getInvalidTagTemplatePlaceholders(template) {
|
|
18
|
+
const matches = template.matchAll(/\{([^}]+)\}/g);
|
|
19
|
+
const invalid = new Set();
|
|
20
|
+
for (const match of matches) {
|
|
21
|
+
const placeholder = match[1];
|
|
22
|
+
if (!ALLOWED_TAG_PLACEHOLDERS.has(placeholder)) {
|
|
23
|
+
invalid.add(placeholder);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return [...invalid];
|
|
27
|
+
}
|
|
8
28
|
function parseArgs() {
|
|
9
29
|
const program = new Command();
|
|
10
30
|
program
|
|
11
31
|
.argument("[output]", "Output pipeline file path (or use --output)")
|
|
12
32
|
.option("-o, --output <path>", "Output pipeline file path")
|
|
13
33
|
.option("--modules-root <path>", "Modules root directory", "./modules")
|
|
14
|
-
.option("--branch
|
|
34
|
+
.option("--main-branch <name>", "Primary branch name for release tagging logic", "main")
|
|
35
|
+
.option("--tag-template <template>", "Template for non-primary branch tags; use placeholders {module}, {branch}, {version}, {increment}", DEFAULT_TAG_TEMPLATE)
|
|
36
|
+
.option("--branch-tag-template <template>", "Template for non-primary branch tags; use placeholders {module}, {branch}, {version}, {increment}", DEFAULT_BRANCH_TAG_TEMPLATE)
|
|
15
37
|
.option("--cwd <path>", "Working directory", process.cwd())
|
|
16
38
|
.parse();
|
|
17
39
|
const options = program.opts();
|
|
18
40
|
const output = options.output ?? program.args[0] ?? "";
|
|
19
41
|
const cwd = path.resolve(options.cwd);
|
|
20
42
|
const modulesRoot = path.resolve(cwd, options.modulesRoot);
|
|
21
|
-
const
|
|
22
|
-
|
|
43
|
+
const mainBranch = options.mainBranch;
|
|
44
|
+
const tagTemplate = options.tagTemplate;
|
|
45
|
+
const branchTagTemplate = options.branchTagTemplate;
|
|
46
|
+
const invalidPrimary = getInvalidTagTemplatePlaceholders(tagTemplate);
|
|
47
|
+
if (invalidPrimary.length > 0) {
|
|
48
|
+
program.error(`Invalid --tag-template placeholders: ${invalidPrimary
|
|
49
|
+
.map((x) => `{${x}}`)
|
|
50
|
+
.join(", ")}. Allowed: {module}, {branch}, {version}, {increment}.`);
|
|
51
|
+
}
|
|
52
|
+
const invalidSecondary = getInvalidTagTemplatePlaceholders(branchTagTemplate);
|
|
53
|
+
if (invalidSecondary.length > 0) {
|
|
54
|
+
program.error(`Invalid --branch-tag-template placeholders: ${invalidSecondary
|
|
55
|
+
.map((x) => `{${x}}`)
|
|
56
|
+
.join(", ")}. Allowed: {module}, {branch}, {version}, {increment}.`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
cwd,
|
|
60
|
+
output,
|
|
61
|
+
modulesRoot,
|
|
62
|
+
mainBranch,
|
|
63
|
+
tagTemplate,
|
|
64
|
+
branchTagTemplate,
|
|
65
|
+
};
|
|
23
66
|
}
|
|
24
67
|
function toPosixPath(p) {
|
|
25
68
|
return p.split(path.sep).join("/");
|
|
@@ -107,7 +150,7 @@ function appendGlobalVariables(lines) {
|
|
|
107
150
|
lines.push(" - deploy");
|
|
108
151
|
lines.push("");
|
|
109
152
|
}
|
|
110
|
-
function appendModuleBuildJob(lines, module
|
|
153
|
+
function appendModuleBuildJob(lines, module) {
|
|
111
154
|
lines.push(`${module.jobId}_build:`);
|
|
112
155
|
lines.push(" stage: build");
|
|
113
156
|
lines.push(" rules:");
|
|
@@ -128,7 +171,7 @@ function appendModuleBuildJob(lines, module, branchRule) {
|
|
|
128
171
|
lines.push(" expire_in: 1 day");
|
|
129
172
|
lines.push("");
|
|
130
173
|
}
|
|
131
|
-
function appendModuleTestJob(lines, module
|
|
174
|
+
function appendModuleTestJob(lines, module) {
|
|
132
175
|
lines.push(`${module.jobId}_test:`);
|
|
133
176
|
lines.push(" stage: test");
|
|
134
177
|
lines.push(" rules:");
|
|
@@ -146,7 +189,7 @@ function appendModuleTestJob(lines, module, branchRule) {
|
|
|
146
189
|
lines.push(' - echo "test is tasty"');
|
|
147
190
|
lines.push("");
|
|
148
191
|
}
|
|
149
|
-
function appendModuleReleaseJob(lines, module
|
|
192
|
+
function appendModuleReleaseJob(lines, module) {
|
|
150
193
|
lines.push(`${module.jobId}_release:`);
|
|
151
194
|
lines.push(" stage: release");
|
|
152
195
|
lines.push(" rules:");
|
|
@@ -195,7 +238,7 @@ function appendModuleReleaseJob(lines, module, branchRule) {
|
|
|
195
238
|
lines.push(` expire_in: 1 day`);
|
|
196
239
|
lines.push("");
|
|
197
240
|
}
|
|
198
|
-
function appendGlobalReleaseJob(lines, modules) {
|
|
241
|
+
function appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate) {
|
|
199
242
|
lines.push("release_and_tag:");
|
|
200
243
|
lines.push(" stage: release");
|
|
201
244
|
lines.push(" needs:");
|
|
@@ -214,21 +257,22 @@ function appendGlobalReleaseJob(lines, modules) {
|
|
|
214
257
|
lines.push(" git config user.email \"polyci@anarun.net\"");
|
|
215
258
|
lines.push(" git config user.name \"polyci\"");
|
|
216
259
|
lines.push(" git remote set-url origin \"https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"");
|
|
260
|
+
lines.push(" TAG_CREATED=false");
|
|
217
261
|
lines.push("");
|
|
218
262
|
for (const module of modules) {
|
|
219
263
|
lines.push(` echo "Tagging release for ${module.moduleName} (${module.modulePath})"`);
|
|
220
264
|
lines.push(` if [ -f "${module.modulePath}/semalease.env" ]; then`);
|
|
221
265
|
lines.push(` . "${module.modulePath}/semalease.env"`);
|
|
222
266
|
lines.push(" TAG_NAME=\"\"");
|
|
223
|
-
lines.push(
|
|
267
|
+
lines.push(` if [ "$CI_COMMIT_BRANCH" = "${mainBranch}" ]; then`);
|
|
224
268
|
lines.push(" if [ \"${NEXT_VERSION:-}\" != \"${LATEST_VERSION:-}\" ]; then");
|
|
225
|
-
lines.push(` TAG_NAME="${module.moduleName}
|
|
269
|
+
lines.push(` TAG_NAME="${applyTagTemplate(tagTemplate, module.moduleName)}"`);
|
|
226
270
|
lines.push(" else");
|
|
227
271
|
lines.push(` echo "Version did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
228
272
|
lines.push(" fi");
|
|
229
273
|
lines.push(" else");
|
|
230
274
|
lines.push(" if [ \"${NEXT_VERSION:-}\" != \"${LATEST_VERSION:-}\" ] || [ \"${NEXT_INCREMENT:-}\" != \"${LATEST_INCREMENT:-}\" ]; then");
|
|
231
|
-
lines.push(` TAG_NAME="${module.moduleName}
|
|
275
|
+
lines.push(` TAG_NAME="${applyTagTemplate(branchTagTemplate, module.moduleName)}"`);
|
|
232
276
|
lines.push(" else");
|
|
233
277
|
lines.push(` echo "Version/increment did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
234
278
|
lines.push(" fi");
|
|
@@ -241,15 +285,20 @@ function appendGlobalReleaseJob(lines, modules) {
|
|
|
241
285
|
lines.push(` git add "${module.modulePath}/package.json" "${module.modulePath}/public/release.json"`);
|
|
242
286
|
}
|
|
243
287
|
lines.push(" git tag \"$TAG_NAME\"");
|
|
288
|
+
lines.push(" TAG_CREATED=true");
|
|
244
289
|
lines.push(" fi");
|
|
245
290
|
lines.push(" else");
|
|
246
291
|
lines.push(` echo "Missing ${module.modulePath}/semalease.env, skipping ${module.moduleName}"`);
|
|
247
292
|
lines.push(" fi");
|
|
248
293
|
}
|
|
249
294
|
lines.push("");
|
|
250
|
-
lines.push("
|
|
251
|
-
lines.push("
|
|
252
|
-
lines.push("
|
|
295
|
+
lines.push(" if [ \"$TAG_CREATED\" = true ]; then");
|
|
296
|
+
lines.push(" git commit -m \"release: update affected modules\"");
|
|
297
|
+
lines.push(" git push origin HEAD");
|
|
298
|
+
lines.push(" git push origin --tags");
|
|
299
|
+
lines.push(" else");
|
|
300
|
+
lines.push(" echo \"No new tags created; skipping commit/push\"");
|
|
301
|
+
lines.push(" fi");
|
|
253
302
|
lines.push(` artifacts:`);
|
|
254
303
|
lines.push(` paths:`);
|
|
255
304
|
for (const module of modules) {
|
|
@@ -259,7 +308,7 @@ function appendGlobalReleaseJob(lines, modules) {
|
|
|
259
308
|
lines.push(` expire_in: 1 day`);
|
|
260
309
|
lines.push("");
|
|
261
310
|
}
|
|
262
|
-
function appendModulePublishJob(lines, module
|
|
311
|
+
function appendModulePublishJob(lines, module) {
|
|
263
312
|
lines.push(`${module.jobId}_publish:`);
|
|
264
313
|
lines.push(" stage: publish");
|
|
265
314
|
lines.push(" rules:");
|
|
@@ -287,7 +336,7 @@ function appendModulePublishJob(lines, module, branchRule) {
|
|
|
287
336
|
lines.push(" expire_in: 1 day");
|
|
288
337
|
lines.push("");
|
|
289
338
|
}
|
|
290
|
-
function appendModuleDeployJob(lines, module
|
|
339
|
+
function appendModuleDeployJob(lines, module) {
|
|
291
340
|
lines.push(`${module.jobId}_deploy:`);
|
|
292
341
|
lines.push(" stage: deploy");
|
|
293
342
|
lines.push(" rules:");
|
|
@@ -318,23 +367,23 @@ function appendModuleDeployJob(lines, module, branchRule) {
|
|
|
318
367
|
lines.push(" - rm -rf ~/.ssh");
|
|
319
368
|
lines.push("");
|
|
320
369
|
}
|
|
321
|
-
function buildPipeline(modules,
|
|
370
|
+
function buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate) {
|
|
322
371
|
const lines = [];
|
|
323
372
|
appendGlobalVariables(lines);
|
|
324
373
|
for (const module of modules) {
|
|
325
|
-
appendModuleBuildJob(lines, module
|
|
326
|
-
appendModuleTestJob(lines, module
|
|
327
|
-
appendModuleReleaseJob(lines, module
|
|
374
|
+
appendModuleBuildJob(lines, module);
|
|
375
|
+
appendModuleTestJob(lines, module);
|
|
376
|
+
appendModuleReleaseJob(lines, module);
|
|
328
377
|
}
|
|
329
|
-
appendGlobalReleaseJob(lines, modules);
|
|
378
|
+
appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate);
|
|
330
379
|
for (const module of modules) {
|
|
331
|
-
appendModulePublishJob(lines, module
|
|
332
|
-
appendModuleDeployJob(lines, module
|
|
380
|
+
appendModulePublishJob(lines, module);
|
|
381
|
+
appendModuleDeployJob(lines, module);
|
|
333
382
|
}
|
|
334
383
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
335
384
|
}
|
|
336
385
|
function main() {
|
|
337
|
-
const { cwd, output, modulesRoot,
|
|
386
|
+
const { cwd, output, modulesRoot, mainBranch, tagTemplate, branchTagTemplate, } = parseArgs();
|
|
338
387
|
if (!output) {
|
|
339
388
|
log.error("Output path is required. Use [output] or --output <path>.");
|
|
340
389
|
process.exit(1);
|
|
@@ -344,7 +393,7 @@ function main() {
|
|
|
344
393
|
log.error({ cwd, modulesRoot }, "No supported modules were discovered under modules root");
|
|
345
394
|
process.exit(1);
|
|
346
395
|
}
|
|
347
|
-
const pipeline = buildPipeline(modules,
|
|
396
|
+
const pipeline = buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate);
|
|
348
397
|
const outputPath = path.resolve(cwd, output);
|
|
349
398
|
fs.writeFileSync(outputPath, pipeline, "utf8");
|
|
350
399
|
log.info({ modules: modules.map((m) => m.modulePath), outputPath }, "Generated GitLab pipeline");
|