@projectdochelp/s3te 3.4.3 → 3.4.5

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/README.md CHANGED
@@ -293,6 +293,7 @@ GitHub preparation checklist:
293
293
  6. Add these repository variables:
294
294
  - `S3TE_ENVIRONMENT`
295
295
  Use the exact environment name from `s3te.config.json`, for example `dev`, `test`, or `prod`.
296
+ This is required for push-based sync when your project has more than one configured environment.
296
297
  - `S3TE_GIT_BRANCH` optional
297
298
  Use the branch that should trigger the sync job, for example `main`.
298
299
  7. In GitHub open `Settings -> Secrets and variables -> Actions -> Secrets`.
@@ -300,7 +301,9 @@ GitHub preparation checklist:
300
301
  - `AWS_ACCESS_KEY_ID`
301
302
  - `AWS_SECRET_ACCESS_KEY`
302
303
  9. Leave `.github/workflows/s3te-sync.yml` unchanged unless you want a custom CI flow. The scaffolded workflow already reads:
303
- - the environment from `S3TE_ENVIRONMENT`
304
+ - the environment from the manual `workflow_dispatch` input when provided
305
+ - otherwise from `S3TE_ENVIRONMENT`
306
+ - otherwise automatically from `s3te.config.json` when exactly one environment exists
304
307
  - the branch from `S3TE_GIT_BRANCH` or defaults to `main`
305
308
  - the AWS region from `s3te.config.json`
306
309
 
@@ -327,6 +330,7 @@ Where to get the AWS values:
327
330
  [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-keys-admin-managed.html).
328
331
  - `S3TE_ENVIRONMENT`
329
332
  This is the environment key from your `s3te.config.json`, for example `test` or `prod`.
333
+ For repositories with multiple environments, set this variable or pass the environment manually when starting `workflow_dispatch`.
330
334
  - AWS region
331
335
  You do not need to copy this into GitHub. The workflow reads `environments.<name>.awsRegion` directly from `s3te.config.json`.
332
336
 
@@ -376,10 +380,13 @@ The scaffolded workflow looks like this:
376
380
  # Required GitHub repository secrets:
377
381
  # - AWS_ACCESS_KEY_ID
378
382
  # - AWS_SECRET_ACCESS_KEY
379
- # Required GitHub repository variable:
383
+ # Required GitHub repository variable for push-based sync in multi-environment projects:
380
384
  # - S3TE_ENVIRONMENT (for example dev, test, or prod)
381
385
  # Optional GitHub repository variable:
382
386
  # - S3TE_GIT_BRANCH (defaults to main)
387
+ # Notes:
388
+ # - workflow_dispatch can override the environment manually
389
+ # - if s3te.config.json contains exactly one environment, no S3TE_ENVIRONMENT variable is needed
383
390
  # This workflow reads s3te.config.json at runtime and syncs all variants into their own code buckets.
384
391
  name: S3TE Sync
385
392
  on:
@@ -423,7 +430,7 @@ jobs:
423
430
  WORKFLOW_INPUT_ENVIRONMENT: ${{ inputs.environment }}
424
431
  REPOSITORY_S3TE_ENVIRONMENT: ${{ vars.S3TE_ENVIRONMENT }}
425
432
  run: |
426
- node -e "const fs=require('node:fs'); const requested=(process.env.WORKFLOW_INPUT_ENVIRONMENT || process.env.REPOSITORY_S3TE_ENVIRONMENT || '').trim(); const config=JSON.parse(fs.readFileSync('s3te.config.json','utf8')); const known=Object.keys(config.environments ?? {}); if(!requested){ console.error('Missing GitHub repository variable S3TE_ENVIRONMENT.'); process.exit(1);} const environmentConfig=config.environments?.[requested]; if(!environmentConfig){ console.error('Unknown environment ' + requested + '. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} fs.appendFileSync(process.env.GITHUB_OUTPUT, 'environment=' + requested + '\n'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'aws_region=' + environmentConfig.awsRegion + '\n');"
433
+ node -e "const fs=require('node:fs'); const fromInput=(process.env.WORKFLOW_INPUT_ENVIRONMENT || '').trim(); const fromVariable=(process.env.REPOSITORY_S3TE_ENVIRONMENT || '').trim(); const config=JSON.parse(fs.readFileSync('s3te.config.json','utf8')); const known=Object.keys(config.environments ?? {}); const requested=(fromInput || fromVariable || (known.length === 1 ? known[0] : '')).trim(); if(!requested){ console.error('Missing S3TE environment. Provide workflow_dispatch input \"environment\" or set GitHub repository variable S3TE_ENVIRONMENT. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} const environmentConfig=config.environments?.[requested]; if(!environmentConfig){ console.error('Unknown environment ' + requested + '. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} fs.appendFileSync(process.env.GITHUB_OUTPUT, 'environment=' + requested + '\n'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'aws_region=' + environmentConfig.awsRegion + '\n');"
427
434
  - name: Configure AWS credentials
428
435
  uses: aws-actions/configure-aws-credentials@v4
429
436
  with:
@@ -724,7 +731,8 @@ Filter notes:
724
731
  - multiple clauses are combined with logical `AND`
725
732
  - `__typename` matches the content model, for example `article`
726
733
  - supported legacy value wrappers are `S`, `N`, `BOOL`, `NULL`, and `L`
727
- - results are sorted deterministically; numeric `order` comes first, then `contentId`, then `id`
734
+ - results are sorted deterministically; items with a numeric field `order` are rendered first in ascending order, then items without a numeric `order`, then `contentId`, then `id`
735
+ - this also works with mirrored Webiny content if the model contains a field with the exact field ID `order` and type `number`
728
736
 
729
737
  **Example**
730
738
 
@@ -1018,6 +1026,13 @@ That makes the option example above equivalent to a config that contains:
1018
1026
 
1019
1027
  Use this for every Webiny model whose entries should be available to S3TE template commands like `dbitem`, `dbmulti`, `dbmultifile`, `dbmultifileitem`, or `dbpart`.
1020
1028
 
1029
+ If a Webiny model should control the output order for `dbmulti` or `dbmultifile`, add a field with:
1030
+
1031
+ - field ID `order`
1032
+ - field type `number`
1033
+
1034
+ S3TE sorts matching items by that numeric `order` in ascending order. Items without a numeric `order` stay at the end.
1035
+
1021
1036
  If different environments should read from different Webiny installations or tenants, run the option command per environment:
1022
1037
 
1023
1038
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectdochelp/s3te",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "description": "CLI, render core, AWS adapter, and testkit for S3TemplateEngine projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,6 +36,12 @@ function normalizeKey(value) {
36
36
  return String(value).replace(/\\/g, "/");
37
37
  }
38
38
 
39
+ function matchesProjectRelativeRoot(key, root) {
40
+ const normalizedKey = normalizeKey(key);
41
+ const normalizedRoot = normalizeKey(root);
42
+ return normalizedKey === normalizedRoot || normalizedKey.startsWith(`${normalizedRoot}/`);
43
+ }
44
+
39
45
  function buildSourceId(environment, variant, language, outputKey) {
40
46
  return `${environment}#${variant}#${language}#${outputKey}`;
41
47
  }
@@ -312,17 +318,18 @@ export class S3TemplateRepository {
312
318
  const normalized = normalizeKey(key);
313
319
  const activeVariant = this.environmentManifest.variants[this.activeVariantName];
314
320
 
315
- if (normalized.startsWith(`${this.activeVariantName}/`)) {
321
+ if (matchesProjectRelativeRoot(normalized, activeVariant.partDir)) {
322
+ const normalizedPartDir = normalizeKey(activeVariant.partDir);
316
323
  return {
317
324
  bucket: activeVariant.codeBucket,
318
- objectKey: normalized
325
+ objectKey: `part/${normalized.slice(normalizedPartDir.length + 1)}`
319
326
  };
320
327
  }
321
328
 
322
- if (normalized.startsWith(`${activeVariant.partDir}/`)) {
329
+ if (normalized.startsWith(`${this.activeVariantName}/`)) {
323
330
  return {
324
331
  bucket: activeVariant.codeBucket,
325
- objectKey: `part/${normalized.slice(activeVariant.partDir.length + 1)}`
332
+ objectKey: normalized
326
333
  };
327
334
  }
328
335
 
@@ -365,10 +365,13 @@ function githubSyncWorkflowTemplate() {
365
365
  return `# Required GitHub repository secrets:
366
366
  # - AWS_ACCESS_KEY_ID
367
367
  # - AWS_SECRET_ACCESS_KEY
368
- # Required GitHub repository variable:
368
+ # Required GitHub repository variable for push-based sync in multi-environment projects:
369
369
  # - S3TE_ENVIRONMENT (for example dev, test, or prod)
370
370
  # Optional GitHub repository variable:
371
371
  # - S3TE_GIT_BRANCH (defaults to main)
372
+ # Notes:
373
+ # - workflow_dispatch can override the environment manually
374
+ # - if s3te.config.json contains exactly one environment, no S3TE_ENVIRONMENT variable is needed
372
375
  # This workflow reads s3te.config.json at runtime and syncs all variants into their own code buckets.
373
376
  name: S3TE Sync
374
377
 
@@ -413,7 +416,7 @@ jobs:
413
416
  WORKFLOW_INPUT_ENVIRONMENT: \${{ inputs.environment }}
414
417
  REPOSITORY_S3TE_ENVIRONMENT: \${{ vars.S3TE_ENVIRONMENT }}
415
418
  run: |
416
- node -e "const fs=require('node:fs'); const requested=(process.env.WORKFLOW_INPUT_ENVIRONMENT || process.env.REPOSITORY_S3TE_ENVIRONMENT || '').trim(); const config=JSON.parse(fs.readFileSync('s3te.config.json','utf8')); const known=Object.keys(config.environments ?? {}); if(!requested){ console.error('Missing GitHub repository variable S3TE_ENVIRONMENT.'); process.exit(1);} const environmentConfig=config.environments?.[requested]; if(!environmentConfig){ console.error('Unknown environment ' + requested + '. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} fs.appendFileSync(process.env.GITHUB_OUTPUT, 'environment=' + requested + '\\n'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'aws_region=' + environmentConfig.awsRegion + '\\n');"
419
+ node -e "const fs=require('node:fs'); const fromInput=(process.env.WORKFLOW_INPUT_ENVIRONMENT || '').trim(); const fromVariable=(process.env.REPOSITORY_S3TE_ENVIRONMENT || '').trim(); const config=JSON.parse(fs.readFileSync('s3te.config.json','utf8')); const known=Object.keys(config.environments ?? {}); const requested=(fromInput || fromVariable || (known.length === 1 ? known[0] : '')).trim(); if(!requested){ console.error('Missing S3TE environment. Provide workflow_dispatch input \"environment\" or set GitHub repository variable S3TE_ENVIRONMENT. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} const environmentConfig=config.environments?.[requested]; if(!environmentConfig){ console.error('Unknown environment ' + requested + '. Known environments: ' + (known.length > 0 ? known.join(', ') : '(none)') + '.'); process.exit(1);} fs.appendFileSync(process.env.GITHUB_OUTPUT, 'environment=' + requested + '\\n'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'aws_region=' + environmentConfig.awsRegion + '\\n');"
417
420
  - name: Configure AWS credentials
418
421
  uses: aws-actions/configure-aws-credentials@v4
419
422
  with:
@@ -83,13 +83,42 @@ function compareFilter(actual, expected, filterType) {
83
83
  return actual === expected;
84
84
  }
85
85
 
86
+ function coerceNumericOrderValue(value) {
87
+ if (typeof value === "number") {
88
+ return Number.isFinite(value) ? value : null;
89
+ }
90
+
91
+ if (typeof value === "string") {
92
+ const trimmed = value.trim();
93
+ if (!trimmed) {
94
+ return null;
95
+ }
96
+ const parsed = Number(trimmed);
97
+ return Number.isFinite(parsed) ? parsed : null;
98
+ }
99
+
100
+ const legacyValue = legacyAttributeValueToPlain(value);
101
+ if (legacyValue !== undefined && legacyValue !== value) {
102
+ return coerceNumericOrderValue(legacyValue);
103
+ }
104
+
105
+ return null;
106
+ }
107
+
108
+ function readOrderValue(item) {
109
+ const candidate = item?.values?.order ?? item?.order;
110
+ return coerceNumericOrderValue(candidate);
111
+ }
112
+
86
113
  function compareOrder(a, b) {
87
- const aHasOrder = typeof a.values?.order === "number";
88
- const bHasOrder = typeof b.values?.order === "number";
114
+ const aOrder = readOrderValue(a);
115
+ const bOrder = readOrderValue(b);
116
+ const aHasOrder = aOrder !== null;
117
+ const bHasOrder = bOrder !== null;
89
118
 
90
119
  if (aHasOrder && bHasOrder) {
91
- if (a.values.order !== b.values.order) {
92
- return a.values.order - b.values.order;
120
+ if (aOrder !== bOrder) {
121
+ return aOrder - bOrder;
93
122
  }
94
123
  } else if (aHasOrder && !bHasOrder) {
95
124
  return -1;