project-auto-wizard 0.15.0 โ†’ 0.16.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 (65) hide show
  1. package/README.ja.md +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +2 -2
  4. package/README.zh-CN.md +1 -1
  5. package/package.json +1 -1
  6. package/payload/config/breaking-changes.json +31 -5
  7. package/payload/config/wizard-prompts.yml +0 -4
  8. package/payload/scripts/messages.py +162 -26
  9. package/payload/scripts/version_manager.py +69 -40
  10. package/payload/version.yml.template +6 -2
  11. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +3 -0
  12. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +64 -1
  13. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +61 -12
  14. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +46 -10
  15. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +40 -11
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +24 -8
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +29 -10
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +57 -23
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +155 -72
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +49 -20
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +162 -62
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +184 -87
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +38 -15
  24. package/payload/workflows/go/PROJECT-GO-CI.yaml +14 -5
  25. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +375 -120
  26. package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +9 -4
  27. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +12 -4
  28. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +375 -120
  29. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +9 -4
  30. package/payload/workflows/react/PROJECT-REACT-CI.yaml +38 -9
  31. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +19 -5
  32. package/payload/workflows/spring/PROJECT-SPRING-CI.yml +71 -35
  33. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +9 -6
  34. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +7 -4
  35. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +425 -144
  36. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +7 -4
  37. package/src/cli/args.js +32 -22
  38. package/src/commands/doctor.js +20 -0
  39. package/src/commands/dry-run.js +9 -1
  40. package/src/commands/full.js +9 -4
  41. package/src/commands/interactive.js +29 -30
  42. package/src/commands/status.js +19 -5
  43. package/src/context.js +4 -2
  44. package/src/core/copy/simple.js +31 -0
  45. package/src/core/copy/workflows.js +2 -2
  46. package/src/core/deploy-style.js +1 -1
  47. package/src/core/detect.js +3 -2
  48. package/src/core/options.js +88 -0
  49. package/src/core/release-options.js +6 -23
  50. package/src/core/types.js +15 -8
  51. package/src/core/version-yml.js +68 -26
  52. package/src/i18n/catalog/en/cli.js +3 -1
  53. package/src/i18n/catalog/en/commands.js +15 -0
  54. package/src/i18n/catalog/en/core2.js +3 -0
  55. package/src/i18n/catalog/en/ui.js +1 -0
  56. package/src/i18n/catalog/ko/cli.js +3 -1
  57. package/src/i18n/catalog/ko/commands.js +15 -0
  58. package/src/i18n/catalog/ko/core2.js +3 -0
  59. package/src/i18n/catalog/ko/ui.js +1 -0
  60. package/src/index.js +7 -3
  61. package/src/ui/env-plan.js +1 -1
  62. package/src/ui/prompts.js +9 -5
  63. package/src/ui/status-cards.js +2 -2
  64. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +0 -241
  65. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +0 -330
@@ -149,12 +149,15 @@ jobs:
149
149
  sparse-checkout-cone-mode: false
150
150
 
151
151
  - name: Load messages
152
- run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
152
+ run: |
153
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
154
+ echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
153
155
 
154
156
  # Pass the settings on as outputs
155
157
  - name: Pass settings
156
158
  id: config
157
159
  run: |
160
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
158
161
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
159
162
  # Check the workflow_dispatch inputs
160
163
  if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
@@ -171,19 +174,25 @@ jobs:
171
174
  echo "enable_android=$ENABLE_ANDROID" >> $GITHUB_OUTPUT
172
175
  echo "enable_ios=$ENABLE_IOS" >> $GITHUB_OUTPUT
173
176
  m flutter_a.ci_config_header
174
- echo " Analyze Only: $ANALYZE_ONLY"
177
+ m wf_flutter.ci_analyze_only value="$ANALYZE_ONLY"
175
178
  echo " Android: $ENABLE_ANDROID"
176
179
  echo " iOS: $ENABLE_IOS"
177
180
  m flutter_a.ci_config_trigger event_name="${{ github.event_name }}"
178
181
 
179
182
  - name: Collect build info
180
183
  id: info
184
+ env:
185
+ # Branch names are passed through env so shell metacharacters in them are never parsed as commands
186
+ PAW_EVENT_NAME: ${{ github.event_name }}
187
+ PAW_HEAD_REF: ${{ github.head_ref }}
188
+ PAW_REF_NAME: ${{ github.ref_name }}
181
189
  run: |
190
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
182
191
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
183
- if [ "${{ github.event_name }}" == "pull_request" ]; then
184
- BRANCH_NAME="${{ github.head_ref }}"
192
+ if [ "$PAW_EVENT_NAME" == "pull_request" ]; then
193
+ BRANCH_NAME="$PAW_HEAD_REF"
185
194
  else
186
- BRANCH_NAME="${{ github.ref_name }}"
195
+ BRANCH_NAME="$PAW_REF_NAME"
187
196
  fi
188
197
  COMMIT_HASH=$(echo "${{ github.sha }}" | cut -c1-7)
189
198
 
@@ -198,18 +207,25 @@ jobs:
198
207
  id: comment
199
208
  if: github.event_name == 'pull_request'
200
209
  uses: actions/github-script@v9
210
+ env:
211
+ PAW_IN_GITHUB_RUN_ID: ${{ github.run_id }}
212
+ PAW_IN_STEPS_CONFIG_OUTPUTS_ANALYZE_ONLY: ${{ steps.config.outputs.analyze_only }}
213
+ PAW_IN_STEPS_CONFIG_OUTPUTS_ENABLE_ANDROID: ${{ steps.config.outputs.enable_android }}
214
+ PAW_IN_STEPS_CONFIG_OUTPUTS_ENABLE_IOS: ${{ steps.config.outputs.enable_ios }}
215
+ PAW_IN_STEPS_INFO_OUTPUTS_BRANCH_NAME: ${{ steps.info.outputs.branch_name }}
216
+ PAW_IN_STEPS_INFO_OUTPUTS_COMMIT_HASH: ${{ steps.info.outputs.commit_hash }}
201
217
  with:
202
218
  github-token: ${{ secrets.GITHUB_TOKEN }}
203
219
  script: |
204
220
  const M = JSON.parse(process.env.PAW_MSG);
205
221
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
206
222
  const prNumber = context.payload.pull_request.number;
207
- const branchName = '${{ steps.info.outputs.branch_name }}';
208
- const commitHash = '${{ steps.info.outputs.commit_hash }}';
209
- const analyzeOnly = '${{ steps.config.outputs.analyze_only }}' === 'true';
210
- const enableAndroid = '${{ steps.config.outputs.enable_android }}' === 'true';
211
- const enableIos = '${{ steps.config.outputs.enable_ios }}' === 'true';
212
- const runId = '${{ github.run_id }}';
223
+ const branchName = process.env.PAW_IN_STEPS_INFO_OUTPUTS_BRANCH_NAME;
224
+ const commitHash = process.env.PAW_IN_STEPS_INFO_OUTPUTS_COMMIT_HASH;
225
+ const analyzeOnly = process.env.PAW_IN_STEPS_CONFIG_OUTPUTS_ANALYZE_ONLY === 'true';
226
+ const enableAndroid = process.env.PAW_IN_STEPS_CONFIG_OUTPUTS_ENABLE_ANDROID === 'true';
227
+ const enableIos = process.env.PAW_IN_STEPS_CONFIG_OUTPUTS_ENABLE_IOS === 'true';
228
+ const runId = process.env.PAW_IN_GITHUB_RUN_ID;
213
229
  const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
214
230
  const startTime = Date.now();
215
231
 
@@ -281,6 +297,7 @@ jobs:
281
297
  outputs:
282
298
  status: ${{ steps.result.outputs.status }}
283
299
  test_status: ${{ steps.result.outputs.test_status }}
300
+ test_duration: ${{ steps.result.outputs.test_duration }}
284
301
  duration: ${{ steps.result.outputs.duration }}
285
302
 
286
303
  steps:
@@ -295,21 +312,26 @@ jobs:
295
312
  uses: actions/checkout@v7
296
313
 
297
314
  - name: Load messages
298
- run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
315
+ id: load_messages
316
+ run: |
317
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
318
+ echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
299
319
 
300
320
  # Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
301
321
  - name: Prepare env file
302
322
  env:
303
323
  ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
304
324
  run: |
325
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
326
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
305
327
  if [ "$ENV_MODE" = "dotenv" ]; then
306
328
  printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
307
- echo "โœ… $ENV_FILE_PATH file created"
329
+ m wf_flutter.env_file_created file="$ENV_FILE_PATH"
308
330
  elif [ -n "$ENV_CONTENT" ]; then
309
331
  DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
310
332
  printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
311
333
  echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
312
- echo "dart-define file prepared"
334
+ m wf_flutter.dart_define_prepared
313
335
  fi
314
336
 
315
337
  - name: Set up Flutter
@@ -321,7 +343,9 @@ jobs:
321
343
 
322
344
  - name: Verify Flutter version
323
345
  run: |
324
- echo "โœ… Flutter setup completed"
346
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
347
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
348
+ m wf_flutter.flutter_setup_done_ok
325
349
  flutter --version
326
350
 
327
351
  - name: Cache Flutter dependencies
@@ -334,15 +358,18 @@ jobs:
334
358
  - name: Install dependencies
335
359
  id: deps
336
360
  run: |
361
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
362
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
337
363
  flutter pub get
338
364
  if grep -q "build_runner" pubspec.yaml; then
339
365
  dart run build_runner build --delete-conflicting-outputs
340
366
  fi
341
- echo "โœ… Dependencies installed"
367
+ m wf_flutter.deps_installed_ok
342
368
 
343
369
  - name: Run Flutter Analyze
344
370
  id: analyze
345
371
  run: |
372
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
346
373
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
347
374
  m flutter_a.ci_analyze_start
348
375
  flutter analyze --no-fatal-infos
@@ -353,11 +380,14 @@ jobs:
353
380
  id: test
354
381
  if: ${{ !cancelled() && steps.deps.outcome == 'success' }}
355
382
  run: |
383
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
356
384
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
357
385
  # flutter test itself fails when test/ has no *_test.dart, so skip it
358
386
  if [ -d test ] && [ -n "$(find test -name '*_test.dart' -print -quit)" ]; then
359
387
  m flutter_a.ci_test_start
388
+ TEST_STARTED=$(date +%s)
360
389
  flutter test ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
390
+ echo "seconds=$(( $(date +%s) - TEST_STARTED ))" >> "$GITHUB_OUTPUT"
361
391
  echo "ran=true" >> "$GITHUB_OUTPUT"
362
392
  m flutter_a.ci_test_passed
363
393
  else
@@ -367,28 +397,41 @@ jobs:
367
397
 
368
398
  - name: Record analysis result
369
399
  id: result
370
- if: always()
400
+ if: always() && steps.load_messages.outcome == 'success'
371
401
  uses: actions/github-script@v9
402
+ env:
403
+ PAW_IN_STEPS_ANALYZE_OUTCOME: ${{ steps.analyze.outcome }}
404
+ PAW_IN_STEPS_ANALYZE_START_OUTPUTS_TIME: ${{ steps.analyze_start.outputs.time }}
405
+ PAW_IN_STEPS_TEST_OUTCOME: ${{ steps.test.outcome }}
406
+ PAW_IN_STEPS_TEST_OUTPUTS_RAN: ${{ steps.test.outputs.ran }}
407
+ PAW_IN_STEPS_TEST_OUTPUTS_SECONDS: ${{ steps.test.outputs.seconds }}
372
408
  with:
373
409
  script: |
374
410
  const M = JSON.parse(process.env.PAW_MSG);
375
411
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
376
- const analyzeStart = parseInt('${{ steps.analyze_start.outputs.time }}');
412
+ const analyzeStart = parseInt(process.env.PAW_IN_STEPS_ANALYZE_START_OUTPUTS_TIME);
377
413
  const now = Date.now();
378
414
  const elapsed = now - analyzeStart;
379
415
  const minutes = Math.floor(elapsed / 60000);
380
416
  const seconds = Math.floor((elapsed % 60000) / 1000);
381
417
  const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
382
418
 
383
- const status = '${{ steps.analyze.outcome }}' === 'success' ? 'success' : 'failure';
419
+ const status = process.env.PAW_IN_STEPS_ANALYZE_OUTCOME === 'success' ? 'success' : 'failure';
384
420
  // none: no test files, skipped: not run because the dependency install failed
385
- const testOutcome = '${{ steps.test.outcome }}';
421
+ const testOutcome = process.env.PAW_IN_STEPS_TEST_OUTCOME;
386
422
  const testStatus = testOutcome === 'success'
387
- ? ('${{ steps.test.outputs.ran }}' === 'true' ? 'success' : 'none')
423
+ ? (process.env.PAW_IN_STEPS_TEST_OUTPUTS_RAN === 'true' ? 'success' : 'none')
388
424
  : (testOutcome === 'failure' ? 'failure' : 'skipped');
389
425
 
390
426
  core.setOutput('status', status);
427
+ // Only a run that finished has a measured time; otherwise leave it empty (shown as -)
428
+ const testSeconds = parseInt(process.env.PAW_IN_STEPS_TEST_OUTPUTS_SECONDS, 10);
429
+ const testDuration = Number.isNaN(testSeconds) ? '' : (testSeconds >= 60
430
+ ? m('flutter_a.ci_dur_min_sec', { minutes: Math.floor(testSeconds / 60), seconds: testSeconds % 60 })
431
+ : m('flutter_a.ci_dur_sec', { seconds: testSeconds }));
432
+
391
433
  core.setOutput('test_status', testStatus);
434
+ core.setOutput('test_duration', testDuration);
392
435
  core.setOutput('duration', duration);
393
436
  console.log(m('flutter_a.ci_log_analyze_result', { status, test: testStatus, duration }));
394
437
 
@@ -421,21 +464,26 @@ jobs:
421
464
  uses: actions/checkout@v7
422
465
 
423
466
  - name: Load messages
424
- run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
467
+ id: load_messages
468
+ run: |
469
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
470
+ echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
425
471
 
426
472
  # Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
427
473
  - name: Prepare env file
428
474
  env:
429
475
  ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
430
476
  run: |
477
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
478
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
431
479
  if [ "$ENV_MODE" = "dotenv" ]; then
432
480
  printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
433
- echo "โœ… $ENV_FILE_PATH file created"
481
+ m wf_flutter.env_file_created file="$ENV_FILE_PATH"
434
482
  elif [ -n "$ENV_CONTENT" ]; then
435
483
  DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
436
484
  printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
437
485
  echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
438
- echo "dart-define file prepared"
486
+ m wf_flutter.dart_define_prepared
439
487
  fi
440
488
 
441
489
  - name: Set up Flutter
@@ -447,7 +495,9 @@ jobs:
447
495
 
448
496
  - name: Verify Flutter version
449
497
  run: |
450
- echo "โœ… Flutter setup completed"
498
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
499
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
500
+ m wf_flutter.flutter_setup_done_ok
451
501
  flutter --version
452
502
 
453
503
  - name: Cache Flutter dependencies
@@ -469,11 +519,13 @@ jobs:
469
519
 
470
520
  - name: Install dependencies
471
521
  run: |
522
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
523
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
472
524
  flutter pub get
473
525
  if grep -q "build_runner" pubspec.yaml; then
474
526
  dart run build_runner build --delete-conflicting-outputs
475
527
  fi
476
- echo "โœ… Dependencies installed"
528
+ m wf_flutter.deps_installed_ok
477
529
 
478
530
  - name: Set up Java
479
531
  uses: actions/setup-java@v5
@@ -483,17 +535,20 @@ jobs:
483
535
 
484
536
  - name: Verify Java version
485
537
  run: |
486
- echo "โœ… Java setup completed"
538
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
539
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
540
+ m wf_flutter.java_setup_done_ok
487
541
  java -version
488
542
 
489
543
  - name: Setup Gradle
490
544
  working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
491
545
  run: |
546
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
492
547
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
493
548
  # The default flutter create .gitignore does not commit gradlew - flutter build creates it when missing
494
549
  if [ -f gradlew ]; then
495
550
  chmod +x gradlew
496
- echo "โœ… Gradle wrapper permissions set"
551
+ m wf_flutter.gradle_perm_ok
497
552
  else
498
553
  m flutter_a.gradlew_missing
499
554
  fi
@@ -502,6 +557,7 @@ jobs:
502
557
  - name: Build APK
503
558
  id: build
504
559
  run: |
560
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
505
561
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
506
562
  m flutter_a.ci_apk_start
507
563
  flutter build apk --debug ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
@@ -510,20 +566,23 @@ jobs:
510
566
 
511
567
  - name: Record build result
512
568
  id: result
513
- if: always()
569
+ if: always() && steps.load_messages.outcome == 'success'
514
570
  uses: actions/github-script@v9
571
+ env:
572
+ PAW_IN_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
573
+ PAW_IN_STEPS_BUILD_START_OUTPUTS_TIME: ${{ steps.build_start.outputs.time }}
515
574
  with:
516
575
  script: |
517
576
  const M = JSON.parse(process.env.PAW_MSG);
518
577
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
519
- const buildStart = parseInt('${{ steps.build_start.outputs.time }}');
578
+ const buildStart = parseInt(process.env.PAW_IN_STEPS_BUILD_START_OUTPUTS_TIME);
520
579
  const now = Date.now();
521
580
  const elapsed = now - buildStart;
522
581
  const minutes = Math.floor(elapsed / 60000);
523
582
  const seconds = Math.floor((elapsed % 60000) / 1000);
524
583
  const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
525
584
 
526
- const status = '${{ steps.build.outcome }}' === 'success' ? 'success' : 'failure';
585
+ const status = process.env.PAW_IN_STEPS_BUILD_OUTCOME === 'success' ? 'success' : 'failure';
527
586
 
528
587
  core.setOutput('status', status);
529
588
  core.setOutput('duration', duration);
@@ -558,7 +617,10 @@ jobs:
558
617
  uses: actions/checkout@v7
559
618
 
560
619
  - name: Load messages
561
- run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
620
+ id: load_messages
621
+ run: |
622
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
623
+ echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
562
624
 
563
625
  - name: Select Xcode version
564
626
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
@@ -568,16 +630,22 @@ jobs:
568
630
  # xcode-select alone does not propagate to subprocesses, so set it explicitly
569
631
  DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
570
632
  run: |
571
- echo "=== iPhoneOS.platform check ==="
572
- ls "$DEVELOPER_DIR/Platforms/iPhoneOS.platform/" 2>/dev/null && echo "iPhoneOS.platform EXISTS" || echo "iPhoneOS.platform NOT FOUND"
573
- echo "=== Installing MobileDevice packages ==="
633
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
634
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
635
+ m wf_flutter.xcode_platform_check
636
+ if ls "$DEVELOPER_DIR/Platforms/iPhoneOS.platform/" 2>/dev/null; then
637
+ m wf_flutter.xcode_platform_exists
638
+ else
639
+ m wf_flutter.xcode_platform_missing
640
+ fi
641
+ m wf_flutter.xcode_install_pkgs
574
642
  sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDevice.pkg -target /
575
643
  sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg -target /
576
- echo "=== Accepting Xcode license ==="
644
+ m wf_flutter.xcode_license
577
645
  sudo xcodebuild -license accept
578
- echo "=== Initializing Xcode components ==="
646
+ m wf_flutter.xcode_init
579
647
  xcrun simctl list >/dev/null 2>&1 || true
580
- echo "=== Downloading iOS platform ==="
648
+ m wf_flutter.xcode_download
581
649
  xcodebuild -downloadPlatform iOS
582
650
 
583
651
  # Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
@@ -585,14 +653,16 @@ jobs:
585
653
  env:
586
654
  ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
587
655
  run: |
656
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
657
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
588
658
  if [ "$ENV_MODE" = "dotenv" ]; then
589
659
  printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
590
- echo "โœ… $ENV_FILE_PATH file created"
660
+ m wf_flutter.env_file_created file="$ENV_FILE_PATH"
591
661
  elif [ -n "$ENV_CONTENT" ]; then
592
662
  DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
593
663
  printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
594
664
  echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
595
- echo "dart-define file prepared"
665
+ m wf_flutter.dart_define_prepared
596
666
  fi
597
667
 
598
668
  - name: Set up Flutter
@@ -604,7 +674,9 @@ jobs:
604
674
 
605
675
  - name: Verify Flutter version
606
676
  run: |
607
- echo "โœ… Flutter setup completed"
677
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
678
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
679
+ m wf_flutter.flutter_setup_done_ok
608
680
  flutter --version
609
681
 
610
682
  - name: Cache Flutter dependencies
@@ -616,11 +688,13 @@ jobs:
616
688
 
617
689
  - name: Install dependencies
618
690
  run: |
691
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
692
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
619
693
  flutter pub get
620
694
  if grep -q "build_runner" pubspec.yaml; then
621
695
  dart run build_runner build --delete-conflicting-outputs
622
696
  fi
623
- echo "โœ… Dependencies installed"
697
+ m wf_flutter.deps_installed_ok
624
698
 
625
699
  - name: Set up Ruby
626
700
  uses: ruby/setup-ruby@v1
@@ -630,6 +704,7 @@ jobs:
630
704
 
631
705
  - name: Install CocoaPods
632
706
  run: |
707
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
633
708
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
634
709
  # A default project without plugins has no Podfile - once plugins are added, flutter build creates it
635
710
  if [ -f ios/Podfile ]; then
@@ -643,6 +718,7 @@ jobs:
643
718
  - name: Build iOS
644
719
  id: build
645
720
  run: |
721
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
646
722
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
647
723
  m flutter_a.ci_ios_start
648
724
  flutter build ios --release --no-codesign ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
@@ -650,20 +726,23 @@ jobs:
650
726
 
651
727
  - name: Record build result
652
728
  id: result
653
- if: always()
729
+ if: always() && steps.load_messages.outcome == 'success'
654
730
  uses: actions/github-script@v9
731
+ env:
732
+ PAW_IN_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
733
+ PAW_IN_STEPS_BUILD_START_OUTPUTS_TIME: ${{ steps.build_start.outputs.time }}
655
734
  with:
656
735
  script: |
657
736
  const M = JSON.parse(process.env.PAW_MSG);
658
737
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
659
- const buildStart = parseInt('${{ steps.build_start.outputs.time }}');
738
+ const buildStart = parseInt(process.env.PAW_IN_STEPS_BUILD_START_OUTPUTS_TIME);
660
739
  const now = Date.now();
661
740
  const elapsed = now - buildStart;
662
741
  const minutes = Math.floor(elapsed / 60000);
663
742
  const seconds = Math.floor((elapsed % 60000) / 1000);
664
743
  const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
665
744
 
666
- const status = '${{ steps.build.outcome }}' === 'success' ? 'success' : 'failure';
745
+ const status = process.env.PAW_IN_STEPS_BUILD_OUTCOME === 'success' ? 'success' : 'failure';
667
746
 
668
747
  core.setOutput('status', status);
669
748
  core.setOutput('duration', duration);
@@ -686,33 +765,53 @@ jobs:
686
765
  sparse-checkout-cone-mode: false
687
766
 
688
767
  - name: Load messages
689
- run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
768
+ run: |
769
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
770
+ echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
690
771
 
691
772
  - name: Update final result comment
692
773
  uses: actions/github-script@v9
774
+ env:
775
+ PAW_IN_GITHUB_RUN_ID: ${{ github.run_id }}
776
+ PAW_IN_NEEDS_ANALYZE_OUTPUTS_DURATION: ${{ needs.analyze.outputs.duration }}
777
+ PAW_IN_NEEDS_ANALYZE_OUTPUTS_STATUS: ${{ needs.analyze.outputs.status }}
778
+ PAW_IN_NEEDS_ANALYZE_OUTPUTS_TEST_STATUS: ${{ needs.analyze.outputs.test_status }}
779
+ PAW_IN_NEEDS_ANALYZE_OUTPUTS_TEST_DURATION: ${{ needs.analyze.outputs.test_duration }}
780
+ PAW_IN_NEEDS_BUILD_ANDROID_OUTPUTS_DURATION: ${{ needs.build-android.outputs.duration }}
781
+ PAW_IN_NEEDS_BUILD_ANDROID_OUTPUTS_STATUS: ${{ needs.build-android.outputs.status }}
782
+ PAW_IN_NEEDS_BUILD_IOS_OUTPUTS_DURATION: ${{ needs.build-ios.outputs.duration }}
783
+ PAW_IN_NEEDS_BUILD_IOS_OUTPUTS_STATUS: ${{ needs.build-ios.outputs.status }}
784
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_ANALYZE_ONLY: ${{ needs.prepare.outputs.analyze_only }}
785
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_BRANCH_NAME: ${{ needs.prepare.outputs.branch_name }}
786
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_COMMENT_ID: ${{ needs.prepare.outputs.comment_id }}
787
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_COMMIT_HASH: ${{ needs.prepare.outputs.commit_hash }}
788
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_ENABLE_ANDROID: ${{ needs.prepare.outputs.enable_android }}
789
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_ENABLE_IOS: ${{ needs.prepare.outputs.enable_ios }}
790
+ PAW_IN_NEEDS_PREPARE_OUTPUTS_START_TIME: ${{ needs.prepare.outputs.start_time }}
693
791
  with:
694
792
  github-token: ${{ secrets.GITHUB_TOKEN }}
695
793
  script: |
696
794
  const M = JSON.parse(process.env.PAW_MSG);
697
795
  const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
698
- const commentId = parseInt('${{ needs.prepare.outputs.comment_id }}');
699
- const startTime = parseInt('${{ needs.prepare.outputs.start_time }}');
700
- const branchName = '${{ needs.prepare.outputs.branch_name }}';
701
- const commitHash = '${{ needs.prepare.outputs.commit_hash }}';
702
- const analyzeOnly = '${{ needs.prepare.outputs.analyze_only }}' === 'true';
703
- const enableAndroid = '${{ needs.prepare.outputs.enable_android }}' === 'true';
704
- const enableIos = '${{ needs.prepare.outputs.enable_ios }}' === 'true';
705
- const runId = '${{ github.run_id }}';
796
+ const commentId = parseInt(process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_COMMENT_ID);
797
+ const startTime = parseInt(process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_START_TIME);
798
+ const branchName = process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_BRANCH_NAME;
799
+ const commitHash = process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_COMMIT_HASH;
800
+ const analyzeOnly = process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_ANALYZE_ONLY === 'true';
801
+ const enableAndroid = process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_ENABLE_ANDROID === 'true';
802
+ const enableIos = process.env.PAW_IN_NEEDS_PREPARE_OUTPUTS_ENABLE_IOS === 'true';
803
+ const runId = process.env.PAW_IN_GITHUB_RUN_ID;
706
804
  const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
707
805
 
708
806
  // Collect the results
709
- const analyzeStatus = '${{ needs.analyze.outputs.status }}';
710
- const analyzeDuration = '${{ needs.analyze.outputs.duration }}' || '-';
711
- const testStatus = '${{ needs.analyze.outputs.test_status }}';
712
- const androidStatus = '${{ needs.build-android.outputs.status }}';
713
- const androidDuration = '${{ needs.build-android.outputs.duration }}' || '-';
714
- const iosStatus = '${{ needs.build-ios.outputs.status }}';
715
- const iosDuration = '${{ needs.build-ios.outputs.duration }}' || '-';
807
+ const analyzeStatus = process.env.PAW_IN_NEEDS_ANALYZE_OUTPUTS_STATUS;
808
+ const analyzeDuration = process.env.PAW_IN_NEEDS_ANALYZE_OUTPUTS_DURATION || '-';
809
+ const testStatus = process.env.PAW_IN_NEEDS_ANALYZE_OUTPUTS_TEST_STATUS;
810
+ const testDuration = process.env.PAW_IN_NEEDS_ANALYZE_OUTPUTS_TEST_DURATION || '-';
811
+ const androidStatus = process.env.PAW_IN_NEEDS_BUILD_ANDROID_OUTPUTS_STATUS;
812
+ const androidDuration = process.env.PAW_IN_NEEDS_BUILD_ANDROID_OUTPUTS_DURATION || '-';
813
+ const iosStatus = process.env.PAW_IN_NEEDS_BUILD_IOS_OUTPUTS_STATUS;
814
+ const iosDuration = process.env.PAW_IN_NEEDS_BUILD_IOS_OUTPUTS_DURATION || '-';
716
815
 
717
816
  // Compute the total duration
718
817
  const now = Date.now();
@@ -787,7 +886,7 @@ jobs:
787
886
  m('flutter_a.ci_table_head'),
788
887
  '|----------|------|----------|',
789
888
  `| ๐Ÿ” Analyze | ${analyzeDisplay} | ${analyzeDuration} |`,
790
- `| ๐Ÿงช Test | ${testDisplay} | - |`,
889
+ `| ๐Ÿงช Test | ${testDisplay} | ${testDuration} |`,
791
890
  m('flutter_a.ci_row_android_result', { status: androidDisplay, duration: analyzeOnly || !enableAndroid ? '-' : androidDuration }),
792
891
  m('flutter_a.ci_row_ios_result', { status: iosDisplay, duration: analyzeOnly || !enableIos ? '-' : iosDuration }),
793
892
  '',
@@ -877,6 +976,7 @@ jobs:
877
976
  env:
878
977
  RESULTS: ${{ toJSON(needs.*.result) }}
879
978
  run: |
979
+ [ -f "$GITHUB_WORKSPACE/.github/scripts/messages.py" ] || { echo "::error::messages.py is missing; rerun \`npx project-auto-wizard --mode full --force\`"; exit 1; }
880
980
  m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
881
981
  m flutter_a.ci_gate_results results="$RESULTS"
882
982
  if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then