polyci 0.0.20 → 0.0.22

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.
Files changed (2) hide show
  1. package/dist/main.js +71 -64
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -5,15 +5,22 @@ import pino from "pino";
5
5
  import pretty from "pino-pretty";
6
6
  const log = pino(pretty());
7
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) {
8
+ const DEFAULT_VERSION_TEMPLATE = "{version}";
9
+ const DEFAULT_BRANCH_VERSION_TEMPLATE = "{version}-{branch}-{increment}";
10
+ const DEFAULT_TAG_PATTERN = "^$MODULE_NAME-v(?<version>\\d+(\\.\\d+)*)-$CI_COMMIT_BRANCH-(?<increment>\\d+(\\.\\d+)*)$";
11
+ const ALLOWED_TAG_PLACEHOLDERS = new Set(["module", "version"]);
12
+ const ALLOWED_VERSION_PLACEHOLDERS = new Set(["branch", "version", "increment"]);
13
+ function applyVersionTemplate(template) {
11
14
  return template
12
- .replaceAll("{module}", moduleName)
13
15
  .replaceAll("{branch}", "${CI_COMMIT_BRANCH}")
14
16
  .replaceAll("{version}", "${NEXT_VERSION}")
15
17
  .replaceAll("{increment}", "${NEXT_INCREMENT}");
16
18
  }
19
+ function applyTagTemplate(template, moduleName, version) {
20
+ return template
21
+ .replaceAll("{module}", moduleName)
22
+ .replaceAll("{version}", version);
23
+ }
17
24
  function getInvalidTagTemplatePlaceholders(template) {
18
25
  const matches = template.matchAll(/\{([^}]+)\}/g);
19
26
  const invalid = new Set();
@@ -25,6 +32,17 @@ function getInvalidTagTemplatePlaceholders(template) {
25
32
  }
26
33
  return [...invalid];
27
34
  }
35
+ function getInvalidVersionTemplatePlaceholders(template) {
36
+ const matches = template.matchAll(/\{([^}]+)\}/g);
37
+ const invalid = new Set();
38
+ for (const match of matches) {
39
+ const placeholder = match[1];
40
+ if (!ALLOWED_VERSION_PLACEHOLDERS.has(placeholder)) {
41
+ invalid.add(placeholder);
42
+ }
43
+ }
44
+ return [...invalid];
45
+ }
28
46
  function parseArgs() {
29
47
  const program = new Command();
30
48
  program
@@ -32,8 +50,10 @@ function parseArgs() {
32
50
  .option("-o, --output <path>", "Output pipeline file path")
33
51
  .option("--modules-root <path>", "Modules root directory", "./modules")
34
52
  .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)
53
+ .option("--version-template <template>", "Template for primary branch module version; placeholders {version}, {branch}, {increment}", DEFAULT_VERSION_TEMPLATE)
54
+ .option("--branch-version-template <template>", "Template for non-primary branch module version; placeholders {version}, {branch}, {increment}", DEFAULT_BRANCH_VERSION_TEMPLATE)
55
+ .option("--tag-template <template>", "Template for non-primary branch tags; use placeholders {module}, {version}", DEFAULT_TAG_TEMPLATE)
56
+ .option("--tag-pattern <pattern>", "Tag pattern passed to semalease to find the latest tag and calculate the next version/increment", DEFAULT_TAG_PATTERN)
37
57
  .option("--cwd <path>", "Working directory", process.cwd())
38
58
  .parse();
39
59
  const options = program.opts();
@@ -41,17 +61,25 @@ function parseArgs() {
41
61
  const cwd = path.resolve(options.cwd);
42
62
  const modulesRoot = path.resolve(cwd, options.modulesRoot);
43
63
  const mainBranch = options.mainBranch;
64
+ const versionTemplate = options.versionTemplate;
65
+ const branchVersionTemplate = options.branchVersionTemplate;
44
66
  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
67
+ const tagPattern = options.tagPattern;
68
+ const invalidVersionTemplate = getInvalidVersionTemplatePlaceholders(versionTemplate);
69
+ if (invalidVersionTemplate.length > 0) {
70
+ program.error(`Invalid --version-template placeholders: ${invalidVersionTemplate
49
71
  .map((x) => `{${x}}`)
50
- .join(", ")}. Allowed: {module}, {branch}, {version}, {increment}.`);
72
+ .join(", ")}. Allowed: {version}, {branch}, {increment}.`);
51
73
  }
52
- const invalidSecondary = getInvalidTagTemplatePlaceholders(branchTagTemplate);
53
- if (invalidSecondary.length > 0) {
54
- program.error(`Invalid --branch-tag-template placeholders: ${invalidSecondary
74
+ const invalidBranchVersionTemplate = getInvalidVersionTemplatePlaceholders(branchVersionTemplate);
75
+ if (invalidBranchVersionTemplate.length > 0) {
76
+ program.error(`Invalid --branch-version-template placeholders: ${invalidBranchVersionTemplate
77
+ .map((x) => `{${x}}`)
78
+ .join(", ")}. Allowed: {version}, {branch}, {increment}.`);
79
+ }
80
+ const invalidTagTemplate = getInvalidTagTemplatePlaceholders(tagTemplate);
81
+ if (invalidTagTemplate.length > 0) {
82
+ program.error(`Invalid --tag-template placeholders: ${invalidTagTemplate
55
83
  .map((x) => `{${x}}`)
56
84
  .join(", ")}. Allowed: {module}, {branch}, {version}, {increment}.`);
57
85
  }
@@ -60,8 +88,10 @@ function parseArgs() {
60
88
  output,
61
89
  modulesRoot,
62
90
  mainBranch,
91
+ versionTemplate,
92
+ branchVersionTemplate,
63
93
  tagTemplate,
64
- branchTagTemplate,
94
+ tagPattern,
65
95
  };
66
96
  }
67
97
  function toPosixPath(p) {
@@ -190,7 +220,7 @@ function appendModuleTestJob(lines, module) {
190
220
  lines.push(` - echo "test is tasty"`);
191
221
  lines.push(``);
192
222
  }
193
- function appendModuleReleaseJob(lines, module) {
223
+ function appendModuleReleaseJob(lines, module, tagPattern) {
194
224
  lines.push(`${module.jobId}_release:`);
195
225
  lines.push(` stage: release`);
196
226
  lines.push(` rules:`);
@@ -207,41 +237,16 @@ function appendModuleReleaseJob(lines, module) {
207
237
  lines.push(` script:`);
208
238
  lines.push(` - apk update`);
209
239
  lines.push(` - apk add $GIT_PACKAGE`);
210
- lines.push(` - cp -r ci $MODULE_PATH/ci`);
211
- lines.push(` - cd $MODULE_PATH`);
212
- lines.push(` - npm ci`);
213
- lines.push(` - git tag -l "^${module.moduleName}-v(?<version>\\d+(\\.\\d+)*)-$CI_COMMIT_BRANCH-(?<increment>\\d+(\\.\\d+)*)$"`);
214
- lines.push(` - npx semalease --tag-pattern "^${module.moduleName}-v(?<version>\\d+(\\.\\d+)*)-$CI_COMMIT_BRANCH-(?<increment>\\d+(\\.\\d+)*)$" semalease.env`);
215
- lines.push(` - source semalease.env`);
216
- lines.push(` - |`);
217
- lines.push(` if [ "$CI_COMMIT_BRANCH" = "main" ]; then`);
218
- lines.push(` PACKAGE_VERSION="$NEXT_VERSION"`);
219
- lines.push(` else`);
220
- lines.push(` PACKAGE_VERSION="$NEXT_VERSION-$CI_COMMIT_BRANCH-$NEXT_INCREMENT"`);
221
- lines.push(` fi`);
222
- lines.push(` - |`);
223
- lines.push(` if [ "$MODULE_TYPE" = "node-express" ]; then`);
224
- lines.push(` node -e "const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); pkg.version=process.argv[1]; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\\n');" "$PACKAGE_VERSION"`);
225
- lines.push(` node ci/prepare-deploy-variables.js --instance-branch $CI_COMMIT_BRANCH`);
226
- lines.push(` elif [ "$MODULE_TYPE" = "node-vite" ]; then`);
227
- lines.push(` node ci/set-release-data.js -i $CI_COMMIT_BRANCH --version=$PACKAGE_VERSION -t`);
228
- lines.push(` node -e "const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); pkg.version=process.argv[1]; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\\n');" "$PACKAGE_VERSION"`);
229
- lines.push(` node ci/prepare-deploy-variables.js --instance-branch $CI_COMMIT_BRANCH`);
230
- lines.push(` else`);
231
- lines.push(` echo "Invalid module type: $MODULE_TYPE"`);
232
- lines.push(` exit 1`);
233
- lines.push(` fi`);
240
+ lines.push(` - cd ${module.modulePath}`);
241
+ lines.push(` - npx semalease --tag-pattern "${tagPattern}" semalease.env`);
234
242
  lines.push(` artifacts:`);
235
243
  lines.push(` paths:`);
236
244
  lines.push(` - $MODULE_PATH/dist`);
237
- lines.push(` - $MODULE_PATH/package.json`);
238
- lines.push(` - $MODULE_PATH/public/release.json`);
239
245
  lines.push(` - $MODULE_PATH/semalease.env`);
240
- lines.push(` - $MODULE_PATH/deploy.env`);
241
246
  lines.push(` expire_in: 1 day`);
242
247
  lines.push(``);
243
248
  }
244
- function appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate) {
249
+ function appendGlobalReleaseJob(lines, modules, mainBranch, versionTemplate, branchVersionTemplate, tagTemplate) {
245
250
  lines.push(`release_and_tag:`);
246
251
  lines.push(` stage: release`);
247
252
  lines.push(` needs:`);
@@ -266,31 +271,31 @@ function appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchT
266
271
  lines.push(` - |`);
267
272
  lines.push(` if [ -f "${module.modulePath}/semalease.env" ]; then`);
268
273
  lines.push(` . "${module.modulePath}/semalease.env"`);
269
- lines.push(` TAG_NAME=""`);
274
+ lines.push(` MODULE_TAG=""`);
270
275
  lines.push(` if [ "$CI_COMMIT_BRANCH" = "${mainBranch}" ]; then`);
271
276
  lines.push(` if [ "\${NEXT_VERSION:-}" != "\${LATEST_VERSION:-}" ]; then`);
272
- lines.push(` TAG_NAME="${applyTagTemplate(tagTemplate, module.moduleName)}"`);
277
+ lines.push(` MODULE_VERSION="${applyVersionTemplate(versionTemplate)}"`);
278
+ lines.push(` MODULE_TAG="${applyTagTemplate(tagTemplate, module.moduleName, applyVersionTemplate(versionTemplate))}"`);
273
279
  lines.push(` else`);
274
- lines.push(` echo "Version did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
280
+ lines.push(` echo "Version did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping release."`);
275
281
  lines.push(` fi`);
276
282
  lines.push(` else`);
277
283
  lines.push(` if [ "\${NEXT_VERSION:-}" != "\${LATEST_VERSION:-}" ] || [ "\${NEXT_INCREMENT:-}" != "\${LATEST_INCREMENT:-}" ]; then`);
278
- lines.push(` TAG_NAME="${applyTagTemplate(branchTagTemplate, module.moduleName)}"`);
284
+ lines.push(` MODULE_VERSION="${applyVersionTemplate(branchVersionTemplate)}"`);
285
+ lines.push(` MODULE_TAG="${applyTagTemplate(tagTemplate, module.moduleName, applyVersionTemplate(branchVersionTemplate))}"`);
279
286
  lines.push(` else`);
280
- lines.push(` echo "Version/increment did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping tag"`);
287
+ lines.push(` echo "Version/increment did not change for ${module.moduleName} on branch $CI_COMMIT_BRANCH, skipping release."`);
281
288
  lines.push(` fi`);
282
289
  lines.push(` fi`);
283
- lines.push(` if [ "$TAG_NAME" != "" ]; then`);
284
- if (module.moduleType === "node-express") {
285
- lines.push(` git add "${module.modulePath}/package.json"`);
286
- }
287
- else if (module.moduleType === "node-vite") {
288
- lines.push(` git add "${module.modulePath}/package.json" "${module.modulePath}/public/release.json"`);
289
- }
290
- lines.push(` TAG_NAMES="\${TAG_NAMES} \${TAG_NAME}"`);
290
+ lines.push(` if [ "$MODULE_TAG" != "" ]; then`);
291
+ lines.push(` cp -r ci "${module.modulePath}/ci"`);
292
+ lines.push(` cd "${module.modulePath}"`);
293
+ lines.push(` source "ci/${module.moduleType}/release.sh"`);
294
+ lines.push(` cd - >/dev/null`);
295
+ lines.push(` TAG_NAMES="\${TAG_NAMES} \${MODULE_TAG}"`);
291
296
  lines.push(` fi`);
292
297
  lines.push(` else`);
293
- lines.push(` echo "Missing ${module.modulePath}/semalease.env, skipping ${module.moduleName}"`);
298
+ lines.push(` echo "Missing ${module.modulePath}/semalease.env, skipping ${module.moduleName} release."`);
294
299
  lines.push(` fi`);
295
300
  }
296
301
  lines.push(``);
@@ -356,6 +361,9 @@ function appendModuleDeployJob(lines, module) {
356
361
  lines.push(` MODULE_TYPE: ${module.moduleType}`);
357
362
  lines.push(` script:`);
358
363
  lines.push(` - source $MODULE_PATH/deploy.env`);
364
+ lines.push(` - export INSTANCE_HOST`);
365
+ lines.push(` - export INSTANCE_CONTAINER `);
366
+ lines.push(` - export INSTANCE_VERSION`);
359
367
  lines.push(` - apk add --update --no-cache openssh-client-default`);
360
368
  lines.push(` - eval $(ssh-agent -s)`);
361
369
  lines.push(` - echo "$DEPLOY_USER_KEY" | tr -d '\\r' | ssh-add -`);
@@ -365,7 +373,6 @@ function appendModuleDeployJob(lines, module) {
365
373
  lines.push(` - chmod 644 ~/.ssh/known_hosts`);
366
374
  lines.push(` - echo -n $CI_JOB_TOKEN | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY`);
367
375
  lines.push(` - docker context create remote --docker "host=ssh://$DEPLOY_USER@$DEPLOY_ADDRESS:$DEPLOY_PORT"`);
368
- // lines.push(` - docker context use remote`);
369
376
  lines.push(` - cd ci`);
370
377
  lines.push(` - export DOCKER_API_VERSION=1.41`);
371
378
  lines.push(` - docker --context remote compose -p $INSTANCE_CONTAINER down --remove-orphans`);
@@ -374,15 +381,15 @@ function appendModuleDeployJob(lines, module) {
374
381
  lines.push(` - rm -rf ~/.ssh`);
375
382
  lines.push(``);
376
383
  }
377
- function buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate) {
384
+ function buildPipeline(modules, mainBranch, versionTemplate, branchVersionTemplate, tagTemplate, tagPattern) {
378
385
  const lines = [];
379
386
  appendGlobalVariables(lines);
380
387
  for (const module of modules) {
381
388
  appendModuleBuildJob(lines, module);
382
389
  appendModuleTestJob(lines, module);
383
- appendModuleReleaseJob(lines, module);
390
+ appendModuleReleaseJob(lines, module, tagPattern);
384
391
  }
385
- appendGlobalReleaseJob(lines, modules, mainBranch, tagTemplate, branchTagTemplate);
392
+ appendGlobalReleaseJob(lines, modules, mainBranch, versionTemplate, branchVersionTemplate, tagTemplate);
386
393
  for (const module of modules) {
387
394
  appendModulePublishJob(lines, module);
388
395
  appendModuleDeployJob(lines, module);
@@ -390,7 +397,7 @@ function buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate) {
390
397
  return `${lines.join("\n").trimEnd()}\n`;
391
398
  }
392
399
  function main() {
393
- const { cwd, output, modulesRoot, mainBranch, tagTemplate, branchTagTemplate, } = parseArgs();
400
+ const { cwd, output, modulesRoot, mainBranch, versionTemplate, branchVersionTemplate, tagTemplate, tagPattern } = parseArgs();
394
401
  if (!output) {
395
402
  log.error("Output path is required. Use [output] or --output <path>.");
396
403
  process.exit(1);
@@ -400,7 +407,7 @@ function main() {
400
407
  log.error({ cwd, modulesRoot }, "No supported modules were discovered under modules root");
401
408
  process.exit(1);
402
409
  }
403
- const pipeline = buildPipeline(modules, mainBranch, tagTemplate, branchTagTemplate);
410
+ const pipeline = buildPipeline(modules, mainBranch, versionTemplate, branchVersionTemplate, tagTemplate, tagPattern);
404
411
  const outputPath = path.resolve(cwd, output);
405
412
  fs.writeFileSync(outputPath, pipeline, "utf8");
406
413
  log.info({ modules: modules.map((m) => m.modulePath), outputPath }, "Generated GitLab pipeline");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "polyci",
3
3
  "description": "Monorepo CI/CD utilities.",
4
- "version": "0.0.20",
4
+ "version": "0.0.22",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "author": "Alexander Tsarev",