project-auto-wizard 0.16.1 → 0.17.0

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 (49) hide show
  1. package/LICENSE +1 -0
  2. package/README.ja.md +5 -1
  3. package/README.ko.md +5 -1
  4. package/README.md +6 -2
  5. package/README.zh-CN.md +5 -1
  6. package/package.json +1 -1
  7. package/payload/scripts/messages.py +44 -6
  8. package/payload/scripts/version_manager.py +76 -3
  9. package/payload/version.yml.template +5 -4
  10. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +7 -7
  11. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +111 -57
  12. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +10 -1
  13. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +13 -3
  14. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +10 -1
  15. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +2 -2
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +10 -10
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +10 -10
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +14 -12
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +35 -18
  20. package/payload/workflows/go/PROJECT-GO-CI.yaml +3 -1
  21. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +81 -61
  22. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +3 -1
  23. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +81 -61
  24. package/payload/workflows/react/PROJECT-REACT-CI.yaml +3 -2
  25. package/payload/workflows/spring/PROJECT-SPRING-CI.yml +6 -5
  26. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +86 -68
  27. package/src/commands/doctor.js +14 -24
  28. package/src/commands/dry-run.js +5 -3
  29. package/src/commands/full.js +6 -6
  30. package/src/commands/interactive.js +16 -3
  31. package/src/commands/status.js +9 -2
  32. package/src/core/copy/gitignore.js +3 -3
  33. package/src/core/copy/readme.js +11 -10
  34. package/src/core/detect-fs.js +9 -4
  35. package/src/core/options.js +36 -0
  36. package/src/core/version-yml.js +43 -13
  37. package/src/i18n/catalog/en/cli.js +1 -0
  38. package/src/i18n/catalog/en/commands.js +4 -1
  39. package/src/i18n/catalog/en/core.js +1 -0
  40. package/src/i18n/catalog/en/core2.js +2 -0
  41. package/src/i18n/catalog/en/ui.js +5 -0
  42. package/src/i18n/catalog/ko/cli.js +1 -0
  43. package/src/i18n/catalog/ko/commands.js +4 -1
  44. package/src/i18n/catalog/ko/core.js +1 -0
  45. package/src/i18n/catalog/ko/core2.js +2 -0
  46. package/src/i18n/catalog/ko/ui.js +5 -0
  47. package/src/index.js +25 -7
  48. package/src/ui/prompts.js +11 -1
  49. package/src/ui/summary.js +15 -6
@@ -123,10 +123,8 @@ jobs:
123
123
  rm -rf .paw-msg
124
124
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
125
125
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
126
- {
127
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
128
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
129
- } >> "$GITHUB_ENV"
126
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
127
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
130
128
 
131
129
  - name: Add 👀 reaction to comment
132
130
  if: contains(github.event.comment.body, '/wizard') && contains(github.event.comment.body, 'server')
@@ -231,10 +229,8 @@ jobs:
231
229
  rm -rf .paw-msg
232
230
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
233
231
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
234
- {
235
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
236
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
237
- } >> "$GITHUB_ENV"
232
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
233
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
238
234
  # The deploy script runs on the remote server, where messages.py does not exist:
239
235
  # ship the messages it needs as a small shell function (m KEY name=value ...)
240
236
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -279,7 +275,7 @@ jobs:
279
275
  uses: actions/github-script@v9
280
276
  with:
281
277
  script: |
282
- const M = JSON.parse(process.env.PAW_MSG);
278
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
283
279
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
284
280
 
285
281
  const prNumber = context.issue.number;
@@ -318,6 +314,7 @@ jobs:
318
314
 
319
315
  # If a required secret or the Dockerfile is missing, report the missing items and stop instead of surfacing a third-party error such as a docker login failure
320
316
  - name: Deploy pre-check (Secrets and Dockerfile)
317
+ id: precheck
321
318
  working-directory: ${{ env.PROJECT_PATH }}
322
319
  env:
323
320
  DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -341,15 +338,21 @@ jobs:
341
338
  [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
342
339
  fi
343
340
  FAILED=false
341
+ ITEMS=""
344
342
  if [ -n "$MISSING" ]; then
345
343
  echo "::error title=$(m wf_preview.precheck_secret_title)::$(m wf_preview.precheck_secret_body missing="$MISSING")"
344
+ ITEMS="${MISSING# }"
345
+ ITEMS="${ITEMS// /, }"
346
346
  FAILED=true
347
347
  fi
348
348
  if [ ! -f "$DOCKERFILE" ]; then
349
349
  echo "::error title=$(m wf_preview.precheck_dockerfile_title)::$(m wf_preview.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
350
+ ITEMS="${ITEMS:+$ITEMS, }Dockerfile"
350
351
  FAILED=true
351
352
  fi
352
353
  if [ "$FAILED" = "true" ]; then
354
+ # The failure comment names what blocked the later steps instead of showing them as waiting
355
+ echo "missing=$ITEMS" >> "$GITHUB_OUTPUT"
353
356
  exit 1
354
357
  fi
355
358
  m wf_preview.precheck_ok
@@ -389,7 +392,7 @@ jobs:
389
392
  PAW_IN_STEPS_PROGRESS_OUTPUTS_DOCKER_START: ${{ steps.progress.outputs.docker_start }}
390
393
  with:
391
394
  script: |
392
- const M = JSON.parse(process.env.PAW_MSG);
395
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
393
396
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
394
397
 
395
398
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -578,7 +581,7 @@ jobs:
578
581
  PAW_IN_STEPS_DOCKER_PROGRESS_OUTPUTS_DEPLOY_START: ${{ steps.docker_progress.outputs.deploy_start }}
579
582
  with:
580
583
  script: |
581
- const M = JSON.parse(process.env.PAW_MSG);
584
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
582
585
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
583
586
 
584
587
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -658,9 +661,11 @@ jobs:
658
661
  env:
659
662
  PAW_IN_STEPS_DOCKER_PROGRESS_OUTPUTS_DOCKER_DURATION: ${{ steps.docker_progress.outputs.docker_duration }}
660
663
  PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID: ${{ steps.progress.outputs.comment_id }}
664
+ PAW_IN_STEPS_PRECHECK_OUTCOME: ${{ steps.precheck.outcome }}
665
+ PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING: ${{ steps.precheck.outputs.missing }}
661
666
  with:
662
667
  script: |
663
- const M = JSON.parse(process.env.PAW_MSG);
668
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
664
669
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
665
670
 
666
671
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -679,10 +684,13 @@ jobs:
679
684
  // Failed before the progress comment was created (initialization stage)
680
685
  dockerStatus = m('wf_preview.status_init_failed');
681
686
  deployStatus = '-';
687
+ } else if (process.env.PAW_IN_STEPS_PRECHECK_OUTCOME === 'failure') {
688
+ // The pre-check stopped the run before any build step: say why nothing ran
689
+ dockerStatus = deployStatus = m('wf_preview.status_precheck_blocked', { missing: process.env.PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING || '-' });
682
690
  } else if (!dockerDuration || dockerDuration === '') {
683
- // Failed at the Docker build stage
691
+ // Failed at the Docker build stage; the deploy step never ran
684
692
  dockerStatus = m('wf_preview.status_failed');
685
- deployStatus = m('wf_preview.status_waiting');
693
+ deployStatus = m('wf_preview.status_not_run');
686
694
  } else {
687
695
  // Failed at the deploy stage
688
696
  dockerStatus = m('wf_preview.status_done');
@@ -769,17 +777,15 @@ jobs:
769
777
  rm -rf .paw-msg
770
778
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
771
779
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
772
- {
773
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
774
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
775
- } >> "$GITHUB_ENV"
780
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
781
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
776
782
 
777
783
  - name: Extract branch name from issue comment
778
784
  id: extract
779
785
  uses: actions/github-script@v9
780
786
  with:
781
787
  script: |
782
- const M = JSON.parse(process.env.PAW_MSG);
788
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
783
789
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
784
790
 
785
791
  const issueNumber = context.issue.number;
@@ -824,7 +830,7 @@ jobs:
824
830
  uses: actions/github-script@v9
825
831
  with:
826
832
  script: |
827
- const M = JSON.parse(process.env.PAW_MSG);
833
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
828
834
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
829
835
 
830
836
  const issueNumber = context.issue.number;
@@ -890,10 +896,8 @@ jobs:
890
896
  rm -rf .paw-msg
891
897
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
892
898
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
893
- {
894
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
895
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
896
- } >> "$GITHUB_ENV"
899
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
900
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
897
901
  # The deploy script runs on the remote server, where messages.py does not exist:
898
902
  # ship the messages it needs as a small shell function (m KEY name=value ...)
899
903
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -926,7 +930,7 @@ jobs:
926
930
  PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_ISSUE_NUMBER: ${{ needs.get-branch-from-issue.outputs.issue_number }}
927
931
  with:
928
932
  script: |
929
- const M = JSON.parse(process.env.PAW_MSG);
933
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
930
934
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
931
935
 
932
936
  const branchName = process.env.PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_BRANCH_NAME;
@@ -1004,7 +1008,7 @@ jobs:
1004
1008
  PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_ISSUE_NUMBER: ${{ needs.get-branch-from-issue.outputs.issue_number }}
1005
1009
  with:
1006
1010
  script: |
1007
- const M = JSON.parse(process.env.PAW_MSG);
1011
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1008
1012
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1009
1013
 
1010
1014
  const issueNumber = parseInt(process.env.PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_ISSUE_NUMBER, 10) || 0;
@@ -1047,6 +1051,7 @@ jobs:
1047
1051
 
1048
1052
  # If a required secret or the Dockerfile is missing, report the missing items and stop instead of surfacing a third-party error such as a docker login failure
1049
1053
  - name: Deploy pre-check (Secrets and Dockerfile)
1054
+ id: precheck
1050
1055
  if: steps.check_branch.outputs.exists == 'true'
1051
1056
  working-directory: ${{ env.PROJECT_PATH }}
1052
1057
  env:
@@ -1071,15 +1076,21 @@ jobs:
1071
1076
  [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
1072
1077
  fi
1073
1078
  FAILED=false
1079
+ ITEMS=""
1074
1080
  if [ -n "$MISSING" ]; then
1075
1081
  echo "::error title=$(m wf_preview.precheck_secret_title)::$(m wf_preview.precheck_secret_body missing="$MISSING")"
1082
+ ITEMS="${MISSING# }"
1083
+ ITEMS="${ITEMS// /, }"
1076
1084
  FAILED=true
1077
1085
  fi
1078
1086
  if [ ! -f "$DOCKERFILE" ]; then
1079
1087
  echo "::error title=$(m wf_preview.precheck_dockerfile_title)::$(m wf_preview.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
1088
+ ITEMS="${ITEMS:+$ITEMS, }Dockerfile"
1080
1089
  FAILED=true
1081
1090
  fi
1082
1091
  if [ "$FAILED" = "true" ]; then
1092
+ # The failure comment names what blocked the later steps instead of showing them as waiting
1093
+ echo "missing=$ITEMS" >> "$GITHUB_OUTPUT"
1083
1094
  exit 1
1084
1095
  fi
1085
1096
  m wf_preview.precheck_ok
@@ -1125,7 +1136,7 @@ jobs:
1125
1136
  PAW_IN_STEPS_PROGRESS_OUTPUTS_DOCKER_START: ${{ steps.progress.outputs.docker_start }}
1126
1137
  with:
1127
1138
  script: |
1128
- const M = JSON.parse(process.env.PAW_MSG);
1139
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1129
1140
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1130
1141
 
1131
1142
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -1320,7 +1331,7 @@ jobs:
1320
1331
  PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_ISSUE_NUMBER: ${{ needs.get-branch-from-issue.outputs.issue_number }}
1321
1332
  with:
1322
1333
  script: |
1323
- const M = JSON.parse(process.env.PAW_MSG);
1334
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1324
1335
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1325
1336
 
1326
1337
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -1403,10 +1414,12 @@ jobs:
1403
1414
  PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_BRANCH_NAME: ${{ needs.get-branch-from-issue.outputs.branch_name }}
1404
1415
  PAW_IN_STEPS_DOCKER_PROGRESS_OUTPUTS_DOCKER_DURATION: ${{ steps.docker_progress.outputs.docker_duration }}
1405
1416
  PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID: ${{ steps.progress.outputs.comment_id }}
1417
+ PAW_IN_STEPS_PRECHECK_OUTCOME: ${{ steps.precheck.outcome }}
1418
+ PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING: ${{ steps.precheck.outputs.missing }}
1406
1419
  PAW_IN_NEEDS_GET_BRANCH_FROM_ISSUE_OUTPUTS_ISSUE_NUMBER: ${{ needs.get-branch-from-issue.outputs.issue_number }}
1407
1420
  with:
1408
1421
  script: |
1409
- const M = JSON.parse(process.env.PAW_MSG);
1422
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1410
1423
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1411
1424
 
1412
1425
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -1423,9 +1436,13 @@ jobs:
1423
1436
  if (!commentId) {
1424
1437
  dockerStatus = m('wf_preview.status_init_failed');
1425
1438
  deployStatus = '-';
1439
+ } else if (process.env.PAW_IN_STEPS_PRECHECK_OUTCOME === 'failure') {
1440
+ // The pre-check stopped the run before any build step: say why nothing ran
1441
+ dockerStatus = deployStatus = m('wf_preview.status_precheck_blocked', { missing: process.env.PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING || '-' });
1426
1442
  } else if (!dockerDuration || dockerDuration === '') {
1443
+ // Failed at the Docker build stage; the deploy step never ran
1427
1444
  dockerStatus = m('wf_preview.status_failed');
1428
- deployStatus = m('wf_preview.status_waiting');
1445
+ deployStatus = m('wf_preview.status_not_run');
1429
1446
  } else {
1430
1447
  dockerStatus = m('wf_preview.status_done');
1431
1448
  deployStatus = m('wf_preview.status_failed');
@@ -1508,10 +1525,8 @@ jobs:
1508
1525
  rm -rf .paw-msg
1509
1526
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
1510
1527
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
1511
- {
1512
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
1513
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
1514
- } >> "$GITHUB_ENV"
1528
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
1529
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
1515
1530
  # The deploy script runs on the remote server, where messages.py does not exist:
1516
1531
  # ship the messages it needs as a small shell function (m KEY name=value ...)
1517
1532
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -1643,7 +1658,7 @@ jobs:
1643
1658
  PAW_IN_STEPS_PR_NUMBER_OUTPUTS_NUMBER: ${{ steps.pr_number.outputs.number }}
1644
1659
  with:
1645
1660
  script: |
1646
- const M = JSON.parse(process.env.PAW_MSG);
1661
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1647
1662
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1648
1663
 
1649
1664
  const number = parseInt(process.env.PAW_IN_STEPS_PR_NUMBER_OUTPUTS_NUMBER, 10) || 0;
@@ -1700,10 +1715,8 @@ jobs:
1700
1715
  rm -rf .paw-msg
1701
1716
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
1702
1717
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
1703
- {
1704
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
1705
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
1706
- } >> "$GITHUB_ENV"
1718
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
1719
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
1707
1720
  # The deploy script runs on the remote server, where messages.py does not exist:
1708
1721
  # ship the messages it needs as a small shell function (m KEY name=value ...)
1709
1722
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -1810,7 +1823,7 @@ jobs:
1810
1823
  PAW_IN_STEPS_CHECK_RUNNING_OUTCOME: ${{ steps.check_running.outcome }}
1811
1824
  with:
1812
1825
  script: |
1813
- const M = JSON.parse(process.env.PAW_MSG);
1826
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1814
1827
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1815
1828
 
1816
1829
  const number = context.issue.number;
@@ -1900,10 +1913,8 @@ jobs:
1900
1913
  rm -rf .paw-msg
1901
1914
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
1902
1915
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
1903
- {
1904
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
1905
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
1906
- } >> "$GITHUB_ENV"
1916
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
1917
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
1907
1918
  # The deploy script runs on the remote server, where messages.py does not exist:
1908
1919
  # ship the messages it needs as a small shell function (m KEY name=value ...)
1909
1920
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -1935,7 +1946,7 @@ jobs:
1935
1946
  PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH: ${{ needs.check-command.outputs.custom_branch }}
1936
1947
  with:
1937
1948
  script: |
1938
- const M = JSON.parse(process.env.PAW_MSG);
1949
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
1939
1950
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
1940
1951
 
1941
1952
  const branchName = process.env.PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH;
@@ -2027,7 +2038,7 @@ jobs:
2027
2038
  PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH: ${{ needs.check-command.outputs.custom_branch }}
2028
2039
  with:
2029
2040
  script: |
2030
- const M = JSON.parse(process.env.PAW_MSG);
2041
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2031
2042
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2032
2043
 
2033
2044
  const issueNumber = context.issue.number;
@@ -2070,6 +2081,7 @@ jobs:
2070
2081
 
2071
2082
  # If a required secret or the Dockerfile is missing, report the missing items and stop instead of surfacing a third-party error such as a docker login failure
2072
2083
  - name: Deploy pre-check (Secrets and Dockerfile)
2084
+ id: precheck
2073
2085
  if: steps.branch_info.outputs.branch_exists == 'true'
2074
2086
  working-directory: ${{ env.PROJECT_PATH }}
2075
2087
  env:
@@ -2094,15 +2106,21 @@ jobs:
2094
2106
  [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
2095
2107
  fi
2096
2108
  FAILED=false
2109
+ ITEMS=""
2097
2110
  if [ -n "$MISSING" ]; then
2098
2111
  echo "::error title=$(m wf_preview.precheck_secret_title)::$(m wf_preview.precheck_secret_body missing="$MISSING")"
2112
+ ITEMS="${MISSING# }"
2113
+ ITEMS="${ITEMS// /, }"
2099
2114
  FAILED=true
2100
2115
  fi
2101
2116
  if [ ! -f "$DOCKERFILE" ]; then
2102
2117
  echo "::error title=$(m wf_preview.precheck_dockerfile_title)::$(m wf_preview.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
2118
+ ITEMS="${ITEMS:+$ITEMS, }Dockerfile"
2103
2119
  FAILED=true
2104
2120
  fi
2105
2121
  if [ "$FAILED" = "true" ]; then
2122
+ # The failure comment names what blocked the later steps instead of showing them as waiting
2123
+ echo "missing=$ITEMS" >> "$GITHUB_OUTPUT"
2106
2124
  exit 1
2107
2125
  fi
2108
2126
  m wf_preview.precheck_ok
@@ -2159,7 +2177,7 @@ jobs:
2159
2177
  PAW_IN_STEPS_PROGRESS_OUTPUTS_DOCKER_START: ${{ steps.progress.outputs.docker_start }}
2160
2178
  with:
2161
2179
  script: |
2162
- const M = JSON.parse(process.env.PAW_MSG);
2180
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2163
2181
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2164
2182
 
2165
2183
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -2347,7 +2365,7 @@ jobs:
2347
2365
  PAW_IN_STEPS_DOCKER_PROGRESS_OUTPUTS_DEPLOY_START: ${{ steps.docker_progress.outputs.deploy_start }}
2348
2366
  with:
2349
2367
  script: |
2350
- const M = JSON.parse(process.env.PAW_MSG);
2368
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2351
2369
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2352
2370
 
2353
2371
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -2430,9 +2448,11 @@ jobs:
2430
2448
  PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH: ${{ needs.check-command.outputs.custom_branch }}
2431
2449
  PAW_IN_STEPS_DOCKER_PROGRESS_OUTPUTS_DOCKER_DURATION: ${{ steps.docker_progress.outputs.docker_duration }}
2432
2450
  PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID: ${{ steps.progress.outputs.comment_id }}
2451
+ PAW_IN_STEPS_PRECHECK_OUTCOME: ${{ steps.precheck.outcome }}
2452
+ PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING: ${{ steps.precheck.outputs.missing }}
2433
2453
  with:
2434
2454
  script: |
2435
- const M = JSON.parse(process.env.PAW_MSG);
2455
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2436
2456
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2437
2457
 
2438
2458
  const commentId = parseInt(process.env.PAW_IN_STEPS_PROGRESS_OUTPUTS_COMMENT_ID, 10) || 0;
@@ -2447,9 +2467,13 @@ jobs:
2447
2467
  if (!commentId) {
2448
2468
  dockerStatus = m('wf_preview.status_init_failed');
2449
2469
  deployStatus = '-';
2470
+ } else if (process.env.PAW_IN_STEPS_PRECHECK_OUTCOME === 'failure') {
2471
+ // The pre-check stopped the run before any build step: say why nothing ran
2472
+ dockerStatus = deployStatus = m('wf_preview.status_precheck_blocked', { missing: process.env.PAW_IN_STEPS_PRECHECK_OUTPUTS_MISSING || '-' });
2450
2473
  } else if (!dockerDuration || dockerDuration === '') {
2474
+ // Failed at the Docker build stage; the deploy step never ran
2451
2475
  dockerStatus = m('wf_preview.status_failed');
2452
- deployStatus = m('wf_preview.status_waiting');
2476
+ deployStatus = m('wf_preview.status_not_run');
2453
2477
  } else {
2454
2478
  dockerStatus = m('wf_preview.status_done');
2455
2479
  deployStatus = m('wf_preview.status_failed');
@@ -2532,10 +2556,8 @@ jobs:
2532
2556
  rm -rf .paw-msg
2533
2557
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
2534
2558
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
2535
- {
2536
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
2537
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
2538
- } >> "$GITHUB_ENV"
2559
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
2560
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
2539
2561
  # The deploy script runs on the remote server, where messages.py does not exist:
2540
2562
  # ship the messages it needs as a small shell function (m KEY name=value ...)
2541
2563
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -2594,7 +2616,7 @@ jobs:
2594
2616
  PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH: ${{ needs.check-command.outputs.custom_branch }}
2595
2617
  with:
2596
2618
  script: |
2597
- const M = JSON.parse(process.env.PAW_MSG);
2619
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2598
2620
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2599
2621
 
2600
2622
  const branchName = process.env.PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH;
@@ -2678,7 +2700,7 @@ jobs:
2678
2700
  PAW_IN_STEPS_CONTAINER_INFO_OUTPUTS_CONTAINER_NAME: ${{ steps.container_info.outputs.container_name }}
2679
2701
  with:
2680
2702
  script: |
2681
- const M = JSON.parse(process.env.PAW_MSG);
2703
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2682
2704
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2683
2705
 
2684
2706
  const branchName = process.env.PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH;
@@ -2739,10 +2761,8 @@ jobs:
2739
2761
  rm -rf .paw-msg
2740
2762
  # The workspace root has no version.yml; pass the language resolved above to every later messages.py call
2741
2763
  export PROJECT_AUTO_WIZARD_LANG="$LANG_CODE"
2742
- {
2743
- echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE"
2744
- echo "PAW_MSG=$(python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview.)"
2745
- } >> "$GITHUB_ENV"
2764
+ python3 "$RUNNER_TEMP/paw/messages.py" dump wf_preview. > "$RUNNER_TEMP/paw-msg.json"
2765
+ echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
2746
2766
  # The deploy script runs on the remote server, where messages.py does not exist:
2747
2767
  # ship the messages it needs as a small shell function (m KEY name=value ...)
2748
2768
  python3 - >> "$GITHUB_ENV" <<'PY'
@@ -2863,7 +2883,7 @@ jobs:
2863
2883
  PAW_IN_STEPS_CONTAINER_INFO_OUTPUTS_CONTAINER_NAME: ${{ steps.container_info.outputs.container_name }}
2864
2884
  with:
2865
2885
  script: |
2866
- const M = JSON.parse(process.env.PAW_MSG);
2886
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
2867
2887
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
2868
2888
 
2869
2889
  const branchName = process.env.PAW_IN_NEEDS_CHECK_COMMAND_OUTPUTS_CUSTOM_BRANCH;
@@ -211,7 +211,8 @@ jobs:
211
211
  env:
212
212
  BUILD_STATUS: ${{ steps.build.outputs.status }}
213
213
  BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
214
- COMMIT_SHA: ${{ github.sha }}
214
+ # On a PR, github.sha is the temporary merge commit; show the PR head commit instead
215
+ COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
215
216
  run: |
216
217
  [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
217
218
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
@@ -234,7 +235,7 @@ jobs:
234
235
  m cibuild.node_result_status status="$BUILD_STATUS"
235
236
  fi
236
237
  m cibuild.node_result_branch branch="$BRANCH_NAME"
237
- m cibuild.node_result_commit sha="$COMMIT_SHA"
238
+ m cibuild.node_result_commit sha="$(echo "$COMMIT_SHA" | cut -c1-7)"
238
239
  m cibuild.node_result_time time="$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
239
240
  echo ""
240
241
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -68,7 +68,7 @@ jobs:
68
68
  id: load_messages
69
69
  run: |
70
70
  [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
71
- echo "PAW_MSG=$(python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" dump wf_spring_ci.)" >> "$GITHUB_ENV"
71
+ python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" dump wf_spring_ci. > "$RUNNER_TEMP/paw-msg.json"
72
72
 
73
73
  - name: Setup JDK
74
74
  uses: actions/setup-java@v5
@@ -216,7 +216,7 @@ jobs:
216
216
  with:
217
217
  github-token: ${{ secrets.GITHUB_TOKEN }}
218
218
  script: |
219
- const M = JSON.parse(process.env.PAW_MSG);
219
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
220
220
  const m = (k, p = {}) => M['wf_spring_ci.' + k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
221
221
  const { data: comments } = await github.rest.issues.listComments({
222
222
  owner: context.repo.owner,
@@ -278,7 +278,7 @@ jobs:
278
278
  with:
279
279
  github-token: ${{ secrets.GITHUB_TOKEN }}
280
280
  script: |
281
- const M = JSON.parse(process.env.PAW_MSG);
281
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
282
282
  const m = (k, p = {}) => M['wf_spring_ci.' + k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
283
283
  const { data: comments } = await github.rest.issues.listComments({
284
284
  owner: context.repo.owner,
@@ -362,7 +362,7 @@ jobs:
362
362
  with:
363
363
  github-token: ${{ secrets.GITHUB_TOKEN }}
364
364
  script: |
365
- const M = JSON.parse(process.env.PAW_MSG);
365
+ const M = JSON.parse(require('fs').readFileSync(`${process.env.RUNNER_TEMP}/paw-msg.json`, 'utf8'));
366
366
  const m = (k, p = {}) => M['wf_spring_ci.' + k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
367
367
  const conclusion = process.env.PAW_IN_STEPS_REPORT_OUTPUTS_OVERALL_OK === 'true' ? 'success' : 'failure';
368
368
  const summary = m(conclusion === 'success' ? 'check_summary_success' : 'check_summary_failure');
@@ -371,7 +371,8 @@ jobs:
371
371
  owner: context.repo.owner,
372
372
  repo: context.repo.repo,
373
373
  name: 'Build Verification',
374
- head_sha: context.sha,
374
+ // On a PR, context.sha is the temporary merge commit; attach the check run to the PR head commit
375
+ head_sha: context.payload.pull_request?.head?.sha ?? context.sha,
375
376
  status: 'completed',
376
377
  conclusion: conclusion,
377
378
  output: {