polyci 0.0.6 → 0.0.8
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 +91 -48
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -4,24 +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-rule <regex>", "Branch rule regex for module jobs", DEFAULT_BRANCH_RULE)
|
|
15
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)
|
|
16
37
|
.option("--cwd <path>", "Working directory", process.cwd())
|
|
17
38
|
.parse();
|
|
18
39
|
const options = program.opts();
|
|
19
40
|
const output = options.output ?? program.args[0] ?? "";
|
|
20
41
|
const cwd = path.resolve(options.cwd);
|
|
21
42
|
const modulesRoot = path.resolve(cwd, options.modulesRoot);
|
|
22
|
-
const branchRule = options.branchRule;
|
|
23
43
|
const mainBranch = options.mainBranch;
|
|
24
|
-
|
|
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
|
+
};
|
|
25
66
|
}
|
|
26
67
|
function toPosixPath(p) {
|
|
27
68
|
return p.split(path.sep).join("/");
|
|
@@ -109,7 +150,7 @@ function appendGlobalVariables(lines) {
|
|
|
109
150
|
lines.push(" - deploy");
|
|
110
151
|
lines.push("");
|
|
111
152
|
}
|
|
112
|
-
function appendModuleBuildJob(lines, module
|
|
153
|
+
function appendModuleBuildJob(lines, module) {
|
|
113
154
|
lines.push(`${module.jobId}_build:`);
|
|
114
155
|
lines.push(" stage: build");
|
|
115
156
|
lines.push(" rules:");
|
|
@@ -130,7 +171,7 @@ function appendModuleBuildJob(lines, module, branchRule) {
|
|
|
130
171
|
lines.push(" expire_in: 1 day");
|
|
131
172
|
lines.push("");
|
|
132
173
|
}
|
|
133
|
-
function appendModuleTestJob(lines, module
|
|
174
|
+
function appendModuleTestJob(lines, module) {
|
|
134
175
|
lines.push(`${module.jobId}_test:`);
|
|
135
176
|
lines.push(" stage: test");
|
|
136
177
|
lines.push(" rules:");
|
|
@@ -148,7 +189,7 @@ function appendModuleTestJob(lines, module, branchRule) {
|
|
|
148
189
|
lines.push(' - echo "test is tasty"');
|
|
149
190
|
lines.push("");
|
|
150
191
|
}
|
|
151
|
-
function appendModuleReleaseJob(lines, module
|
|
192
|
+
function appendModuleReleaseJob(lines, module) {
|
|
152
193
|
lines.push(`${module.jobId}_release:`);
|
|
153
194
|
lines.push(" stage: release");
|
|
154
195
|
lines.push(" rules:");
|
|
@@ -176,6 +217,7 @@ function appendModuleReleaseJob(lines, module, branchRule) {
|
|
|
176
217
|
lines.push(` else`);
|
|
177
218
|
lines.push(` PACKAGE_VERSION="$NEXT_VERSION-$CI_COMMIT_BRANCH-$NEXT_INCREMENT"`);
|
|
178
219
|
lines.push(` fi`);
|
|
220
|
+
lines.push(" - |");
|
|
179
221
|
lines.push(` if [ "$MODULE_TYPE" = "node-express" ]; then`);
|
|
180
222
|
lines.push(` node -e "const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); pkg.version=process.env.PACKAGE_VERSION; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\\n');"`);
|
|
181
223
|
lines.push(` node ci/prepare-deploy-variables.js --instance-branch $CI_COMMIT_BRANCH`);
|
|
@@ -197,7 +239,7 @@ function appendModuleReleaseJob(lines, module, branchRule) {
|
|
|
197
239
|
lines.push(` expire_in: 1 day`);
|
|
198
240
|
lines.push("");
|
|
199
241
|
}
|
|
200
|
-
function appendGlobalReleaseJob(lines, modules, mainBranch) {
|
|
242
|
+
function appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate) {
|
|
201
243
|
lines.push("release_and_tag:");
|
|
202
244
|
lines.push(" stage: release");
|
|
203
245
|
lines.push(" needs:");
|
|
@@ -211,46 +253,47 @@ function appendGlobalReleaseJob(lines, modules, mainBranch) {
|
|
|
211
253
|
lines.push(" script:");
|
|
212
254
|
lines.push(" - apk update");
|
|
213
255
|
lines.push(" - apk add $GIT_PACKAGE");
|
|
214
|
-
lines.push(" -
|
|
215
|
-
lines.push("
|
|
216
|
-
lines.push("
|
|
217
|
-
lines.push("
|
|
218
|
-
lines.push("
|
|
219
|
-
lines.push(" TAG_CREATED=false");
|
|
256
|
+
lines.push(" - set -euo pipefail");
|
|
257
|
+
lines.push(" - git config user.email \"polyci@anarun.net\"");
|
|
258
|
+
lines.push(" - git config user.name \"polyci\"");
|
|
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");
|
|
220
261
|
lines.push("");
|
|
221
262
|
for (const module of modules) {
|
|
222
|
-
lines.push(`
|
|
223
|
-
lines.push(
|
|
224
|
-
lines.push(`
|
|
225
|
-
lines.push("
|
|
226
|
-
lines.push(
|
|
227
|
-
lines.push(
|
|
228
|
-
lines.push(
|
|
229
|
-
lines.push(
|
|
230
|
-
lines.push(` echo "Version did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
231
|
-
lines.push(" fi");
|
|
263
|
+
lines.push(` - echo "Tagging release for ${module.moduleName} (${module.modulePath})"`);
|
|
264
|
+
lines.push(" - |");
|
|
265
|
+
lines.push(` if [ -f "${module.modulePath}/semalease.env" ]; then`);
|
|
266
|
+
lines.push(` . "${module.modulePath}/semalease.env"`);
|
|
267
|
+
lines.push(" TAG_NAME=\"\"");
|
|
268
|
+
lines.push(` if [ "$CI_COMMIT_BRANCH" = "${mainBranch}" ]; then`);
|
|
269
|
+
lines.push(" if [ \"${NEXT_VERSION:-}\" != \"${LATEST_VERSION:-}\" ]; then");
|
|
270
|
+
lines.push(` TAG_NAME="${applyTagTemplate(tagTemplate, module.moduleName)}"`);
|
|
232
271
|
lines.push(" else");
|
|
233
|
-
lines.push(
|
|
234
|
-
lines.push(` TAG_NAME="${module.moduleName}-v\${NEXT_VERSION}-\${CI_COMMIT_BRANCH}-\${NEXT_INCREMENT}"`);
|
|
235
|
-
lines.push(" else");
|
|
236
|
-
lines.push(` echo "Version/increment did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
237
|
-
lines.push(" fi");
|
|
272
|
+
lines.push(` echo "Version did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
238
273
|
lines.push(" fi");
|
|
239
|
-
lines.push("
|
|
274
|
+
lines.push(" else");
|
|
275
|
+
lines.push(" if [ \"${NEXT_VERSION:-}\" != \"${LATEST_VERSION:-}\" ] || [ \"${NEXT_INCREMENT:-}\" != \"${LATEST_INCREMENT:-}\" ]; then");
|
|
276
|
+
lines.push(` TAG_NAME="${applyTagTemplate(branchTagTemplate, module.moduleName)}"`);
|
|
277
|
+
lines.push(" else");
|
|
278
|
+
lines.push(` echo "Version/increment did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
|
|
279
|
+
lines.push(" fi");
|
|
280
|
+
lines.push(" fi");
|
|
281
|
+
lines.push(" if [ \"$TAG_NAME\" != \"\" ]; then");
|
|
240
282
|
if (module.moduleType === "node-express") {
|
|
241
|
-
lines.push(`
|
|
283
|
+
lines.push(` git add "${module.modulePath}/package.json"`);
|
|
242
284
|
}
|
|
243
285
|
else if (module.moduleType === "node-vite") {
|
|
244
|
-
lines.push(`
|
|
286
|
+
lines.push(` git add "${module.modulePath}/package.json" "${module.modulePath}/public/release.json"`);
|
|
245
287
|
}
|
|
246
|
-
lines.push("
|
|
247
|
-
lines.push("
|
|
248
|
-
lines.push(" fi");
|
|
249
|
-
lines.push(" else");
|
|
250
|
-
lines.push(` echo "Missing ${module.modulePath}/semalease.env, skipping ${module.moduleName}"`);
|
|
288
|
+
lines.push(" git tag \"$TAG_NAME\"");
|
|
289
|
+
lines.push(" TAG_CREATED=true");
|
|
251
290
|
lines.push(" fi");
|
|
291
|
+
lines.push(" else");
|
|
292
|
+
lines.push(` echo "Missing ${module.modulePath}/semalease.env, skipping ${module.moduleName}"`);
|
|
293
|
+
lines.push(" fi");
|
|
252
294
|
}
|
|
253
295
|
lines.push("");
|
|
296
|
+
lines.push(" - |");
|
|
254
297
|
lines.push(" if [ \"$TAG_CREATED\" = true ]; then");
|
|
255
298
|
lines.push(" git commit -m \"release: update affected modules\"");
|
|
256
299
|
lines.push(" git push origin HEAD");
|
|
@@ -267,7 +310,7 @@ function appendGlobalReleaseJob(lines, modules, mainBranch) {
|
|
|
267
310
|
lines.push(` expire_in: 1 day`);
|
|
268
311
|
lines.push("");
|
|
269
312
|
}
|
|
270
|
-
function appendModulePublishJob(lines, module
|
|
313
|
+
function appendModulePublishJob(lines, module) {
|
|
271
314
|
lines.push(`${module.jobId}_publish:`);
|
|
272
315
|
lines.push(" stage: publish");
|
|
273
316
|
lines.push(" rules:");
|
|
@@ -295,7 +338,7 @@ function appendModulePublishJob(lines, module, branchRule) {
|
|
|
295
338
|
lines.push(" expire_in: 1 day");
|
|
296
339
|
lines.push("");
|
|
297
340
|
}
|
|
298
|
-
function appendModuleDeployJob(lines, module
|
|
341
|
+
function appendModuleDeployJob(lines, module) {
|
|
299
342
|
lines.push(`${module.jobId}_deploy:`);
|
|
300
343
|
lines.push(" stage: deploy");
|
|
301
344
|
lines.push(" rules:");
|
|
@@ -326,23 +369,23 @@ function appendModuleDeployJob(lines, module, branchRule) {
|
|
|
326
369
|
lines.push(" - rm -rf ~/.ssh");
|
|
327
370
|
lines.push("");
|
|
328
371
|
}
|
|
329
|
-
function buildPipeline(modules,
|
|
372
|
+
function buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate) {
|
|
330
373
|
const lines = [];
|
|
331
374
|
appendGlobalVariables(lines);
|
|
332
375
|
for (const module of modules) {
|
|
333
|
-
appendModuleBuildJob(lines, module
|
|
334
|
-
appendModuleTestJob(lines, module
|
|
335
|
-
appendModuleReleaseJob(lines, module
|
|
376
|
+
appendModuleBuildJob(lines, module);
|
|
377
|
+
appendModuleTestJob(lines, module);
|
|
378
|
+
appendModuleReleaseJob(lines, module);
|
|
336
379
|
}
|
|
337
|
-
appendGlobalReleaseJob(lines, modules, mainBranch);
|
|
380
|
+
appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate);
|
|
338
381
|
for (const module of modules) {
|
|
339
|
-
appendModulePublishJob(lines, module
|
|
340
|
-
appendModuleDeployJob(lines, module
|
|
382
|
+
appendModulePublishJob(lines, module);
|
|
383
|
+
appendModuleDeployJob(lines, module);
|
|
341
384
|
}
|
|
342
385
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
343
386
|
}
|
|
344
387
|
function main() {
|
|
345
|
-
const { cwd, output, modulesRoot,
|
|
388
|
+
const { cwd, output, modulesRoot, mainBranch, tagTemplate, branchTagTemplate, } = parseArgs();
|
|
346
389
|
if (!output) {
|
|
347
390
|
log.error("Output path is required. Use [output] or --output <path>.");
|
|
348
391
|
process.exit(1);
|
|
@@ -352,7 +395,7 @@ function main() {
|
|
|
352
395
|
log.error({ cwd, modulesRoot }, "No supported modules were discovered under modules root");
|
|
353
396
|
process.exit(1);
|
|
354
397
|
}
|
|
355
|
-
const pipeline = buildPipeline(modules,
|
|
398
|
+
const pipeline = buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate);
|
|
356
399
|
const outputPath = path.resolve(cwd, output);
|
|
357
400
|
fs.writeFileSync(outputPath, pipeline, "utf8");
|
|
358
401
|
log.info({ modules: modules.map((m) => m.modulePath), outputPath }, "Generated GitLab pipeline");
|