project-auto-wizard 0.13.2 → 0.14.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.
- package/README.md +1 -1
- package/bin/project-auto-wizard.js +18 -4
- package/package.json +2 -2
- package/payload/config/breaking-changes.json +28 -14
- package/payload/config/wizard-prompts.yml +102 -46
- package/payload/flutter-app/android/fastlane/Fastfile.playstore +44 -32
- package/payload/flutter-app/ios/ExportOptions.plist +8 -8
- package/payload/flutter-app/ios/fastlane/Fastfile +57 -43
- package/payload/scripts/changelog_manager.py +296 -257
- package/payload/scripts/issue_helper.py +54 -50
- package/payload/scripts/messages.py +2279 -0
- package/payload/scripts/truncate_release_notes.py +7 -6
- package/payload/scripts/version_manager.py +64 -61
- package/payload/version.yml.template +2 -1
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +17 -12
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +59 -34
- package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +17 -15
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +17 -10
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +54 -44
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +24 -17
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +194 -184
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +226 -213
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +46 -42
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +334 -241
- package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +152 -128
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +246 -188
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +353 -238
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +96 -85
- package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -36
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +945 -508
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +155 -133
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +76 -60
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +97 -77
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +62 -47
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +942 -505
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +148 -126
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +86 -70
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +112 -92
- package/payload/workflows/spring/PROJECT-SPRING-CI.yml +112 -82
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +180 -150
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +161 -131
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +970 -549
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +193 -162
- package/src/cli/args.js +98 -52
- package/src/cli/help.js +8 -41
- package/src/commands/doctor.js +85 -89
- package/src/commands/dry-run.js +63 -43
- package/src/commands/full.js +79 -77
- package/src/commands/install-settings.js +16 -15
- package/src/commands/interactive-flutter.js +20 -19
- package/src/commands/interactive.js +135 -132
- package/src/commands/purge.js +47 -42
- package/src/commands/status.js +36 -36
- package/src/commands/uninstall.js +43 -41
- package/src/context.js +12 -11
- package/src/core/assets.js +12 -11
- package/src/core/baseline.js +27 -27
- package/src/core/branches.js +29 -27
- package/src/core/branding.js +7 -6
- package/src/core/breaking-check.js +22 -21
- package/src/core/breaking.js +15 -8
- package/src/core/copy/app-files.js +5 -4
- package/src/core/copy/flutter-app.js +8 -8
- package/src/core/copy/gitignore.js +58 -50
- package/src/core/copy/readme.js +45 -35
- package/src/core/copy/simple.js +16 -15
- package/src/core/copy/workflows.js +131 -124
- package/src/core/deploy-style.js +51 -48
- package/src/core/detect-fs.js +57 -57
- package/src/core/detect.js +67 -65
- package/src/core/errors.js +2 -2
- package/src/core/flutter-doctor.js +17 -16
- package/src/core/flutter-hooks.js +33 -32
- package/src/core/flutter-options.js +34 -33
- package/src/core/fsutil.js +12 -12
- package/src/core/installed-stores.js +4 -4
- package/src/core/logger.js +42 -40
- package/src/core/paths-resolve.js +88 -86
- package/src/core/paths.js +10 -10
- package/src/core/release-options.js +8 -8
- package/src/core/removal-exec.js +14 -13
- package/src/core/removal-plan.js +37 -37
- package/src/core/types.js +47 -47
- package/src/core/verify.js +44 -38
- package/src/core/version-yml.js +76 -68
- package/src/core/wizard-env.js +46 -46
- package/src/core/wizard-labels.js +63 -41
- package/src/i18n/catalog/en/cli.js +50 -0
- package/src/i18n/catalog/en/commands.js +187 -0
- package/src/i18n/catalog/en/copy.js +19 -0
- package/src/i18n/catalog/en/core.js +74 -0
- package/src/i18n/catalog/en/core2.js +15 -0
- package/src/i18n/catalog/en/ui.js +221 -0
- package/src/i18n/catalog/en.js +12 -0
- package/src/i18n/catalog/index.js +5 -0
- package/src/i18n/catalog/ko/cli.js +50 -0
- package/src/i18n/catalog/ko/commands.js +187 -0
- package/src/i18n/catalog/ko/copy.js +19 -0
- package/src/i18n/catalog/ko/core.js +74 -0
- package/src/i18n/catalog/ko/core2.js +15 -0
- package/src/i18n/catalog/ko/ui.js +221 -0
- package/src/i18n/catalog/ko.js +10 -0
- package/src/i18n/index.js +44 -0
- package/src/i18n/languages.js +9 -0
- package/src/index.js +128 -94
- package/src/ui/ansi.js +7 -7
- package/src/ui/banner.js +7 -6
- package/src/ui/env-plan.js +82 -81
- package/src/ui/prompts.js +97 -122
- package/src/ui/readline-engine.js +59 -58
- package/src/ui/status-cards.js +37 -31
- package/src/ui/summary.js +89 -88
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Flutter iOS
|
|
2
|
+
# Flutter iOS test TestFlight deploy workflow
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# Run this workflow manually from a feature branch
|
|
5
|
+
# to deploy a test iOS build to TestFlight.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# The config files (ios/fastlane/Fastfile, ios/ExportOptions.plist) are installed by
|
|
8
|
+
# project-auto-wizard only when missing; existing files are never overwritten.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
# - workflow_dispatch
|
|
12
|
-
# -
|
|
13
|
-
# -
|
|
14
|
-
# -
|
|
15
|
-
# - GitHub API
|
|
10
|
+
# Highlights:
|
|
11
|
+
# - Runs manually via workflow_dispatch or is triggered by repository_dispatch
|
|
12
|
+
# - The version is read dynamically from version.yml (same as production; builds are told apart by build number)
|
|
13
|
+
# - The build number is PR number + count, or GITHUB_RUN_NUMBER
|
|
14
|
+
# - Extracts the issue number from the branch name automatically (YYYYMMDD_#issue-number_title format)
|
|
15
|
+
# - Fetches the issue through the GitHub API and includes it in the release notes
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
# 1. flutter build ios --no-codesign (Flutter
|
|
19
|
-
# 2. xcodebuild archive (Xcode
|
|
20
|
-
# 3. xcodebuild -exportArchive (IPA
|
|
21
|
-
# 4. fastlane upload_testflight (ios/fastlane/Fastfile
|
|
17
|
+
# Build pipeline:
|
|
18
|
+
# 1. flutter build ios --no-codesign (Flutter build)
|
|
19
|
+
# 2. xcodebuild archive (create the Xcode archive)
|
|
20
|
+
# 3. xcodebuild -exportArchive (create the IPA using ExportOptions.plist)
|
|
21
|
+
# 4. fastlane upload_testflight (uses ios/fastlane/Fastfile)
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# Required GitHub Secrets
|
|
24
24
|
#
|
|
25
|
-
# Apple
|
|
26
|
-
# APPLE_CERTIFICATE_BASE64: .p12
|
|
27
|
-
# APPLE_CERTIFICATE_PASSWORD: .p12
|
|
28
|
-
# APPLE_PROVISIONING_PROFILE_BASE64: .mobileprovision
|
|
29
|
-
# IOS_PROVISIONING_PROFILE_NAME:
|
|
25
|
+
# Apple certificate & provisioning profile:
|
|
26
|
+
# APPLE_CERTIFICATE_BASE64: .p12 certificate (base64 encoded)
|
|
27
|
+
# APPLE_CERTIFICATE_PASSWORD: .p12 certificate password
|
|
28
|
+
# APPLE_PROVISIONING_PROFILE_BASE64: .mobileprovision file (base64 encoded)
|
|
29
|
+
# IOS_PROVISIONING_PROFILE_NAME: provisioning profile name
|
|
30
30
|
#
|
|
31
31
|
# App Store Connect API:
|
|
32
|
-
# APP_STORE_CONNECT_API_KEY_ID: API Key ID (10
|
|
33
|
-
# APP_STORE_CONNECT_ISSUER_ID: Issuer ID (UUID
|
|
34
|
-
# APP_STORE_CONNECT_API_KEY_BASE64: AuthKey_XXXXXX.p8
|
|
32
|
+
# APP_STORE_CONNECT_API_KEY_ID: API Key ID (10 characters)
|
|
33
|
+
# APP_STORE_CONNECT_ISSUER_ID: Issuer ID (UUID format)
|
|
34
|
+
# APP_STORE_CONNECT_API_KEY_BASE64: AuthKey_XXXXXX.p8 file (base64 encoded)
|
|
35
35
|
#
|
|
36
|
-
#
|
|
37
|
-
# ENV_FILE (
|
|
38
|
-
# SECRETS_XCCONFIG (
|
|
36
|
+
# Environment settings (optional):
|
|
37
|
+
# ENV_FILE (optional): contents of the .env file
|
|
38
|
+
# SECRETS_XCCONFIG (optional): contents of ios/Flutter/Secrets.xcconfig
|
|
39
39
|
|
|
40
40
|
name: PROJECT-Flutter-iOS-Test-TestFlight
|
|
41
41
|
|
|
@@ -48,30 +48,30 @@ on:
|
|
|
48
48
|
repository_dispatch:
|
|
49
49
|
types: [build-ios-app]
|
|
50
50
|
|
|
51
|
-
#
|
|
51
|
+
# Declare the needed permissions explicitly so progress comments work even when the repository default is read-only
|
|
52
52
|
permissions:
|
|
53
53
|
contents: read
|
|
54
54
|
issues: write
|
|
55
55
|
pull-requests: write
|
|
56
56
|
|
|
57
|
-
#
|
|
57
|
+
# Project settings (edit the values below)
|
|
58
58
|
env:
|
|
59
|
-
# Flutter SDK
|
|
60
|
-
#
|
|
59
|
+
# Flutter SDK version — leave empty to use the latest stable channel (matches the SDK constraint of a fresh flutter create project).
|
|
60
|
+
# To pin a version, write it like "3.38.9". It must satisfy environment.sdk in pubspec.yaml.
|
|
61
61
|
FLUTTER_VERSION: ""
|
|
62
62
|
XCODE_VERSION: "26.0"
|
|
63
63
|
PROJECT_TYPE: "flutter"
|
|
64
|
-
# Flutter
|
|
64
|
+
# Flutter root path (relative to the repo root). "." for a single repo, "app" etc. for a monorepo — set automatically at install
|
|
65
65
|
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
66
|
-
#
|
|
66
|
+
# How env vars are passed (dart-define | dotenv) — set automatically from the value chosen at install
|
|
67
67
|
ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
|
|
68
|
-
#
|
|
68
|
+
# Where to create the .env file in dotenv mode (relative to the Flutter root)
|
|
69
69
|
ENV_FILE_PATH: ".env"
|
|
70
70
|
|
|
71
71
|
jobs:
|
|
72
|
-
#
|
|
72
|
+
# Create the comment right away (runs quickly on ubuntu-latest)
|
|
73
73
|
notify-start:
|
|
74
|
-
name:
|
|
74
|
+
name: Progress notification
|
|
75
75
|
runs-on: ubuntu-latest
|
|
76
76
|
if: github.event_name == 'repository_dispatch'
|
|
77
77
|
|
|
@@ -81,18 +81,40 @@ jobs:
|
|
|
81
81
|
branch_valid: ${{ steps.verify_branch.outputs.valid }}
|
|
82
82
|
|
|
83
83
|
steps:
|
|
84
|
-
|
|
84
|
+
# Fetch the message catalog from the triggering ref first — the feature branch being built may predate it
|
|
85
|
+
- name: Checkout messages
|
|
86
|
+
uses: actions/checkout@v7
|
|
87
|
+
with:
|
|
88
|
+
path: .paw-msg
|
|
89
|
+
sparse-checkout: |
|
|
90
|
+
.github/scripts/messages.py
|
|
91
|
+
version.yml
|
|
92
|
+
sparse-checkout-cone-mode: false
|
|
93
|
+
|
|
94
|
+
- name: Load messages
|
|
95
|
+
working-directory: ${{ github.workspace }}
|
|
96
|
+
run: |
|
|
97
|
+
cp .paw-msg/.github/scripts/messages.py "$RUNNER_TEMP/paw_messages.py"
|
|
98
|
+
LANG_CODE=$(cd .paw-msg && python3 "$RUNNER_TEMP/paw_messages.py" lang)
|
|
99
|
+
rm -rf .paw-msg
|
|
100
|
+
echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
|
|
101
|
+
echo "PAW_MESSAGES_PY=$RUNNER_TEMP/paw_messages.py" >> "$GITHUB_ENV"
|
|
102
|
+
echo "PAW_MSG=$(PROJECT_AUTO_WIZARD_LANG=$LANG_CODE python3 "$RUNNER_TEMP/paw_messages.py" dump ios_test_tf.)" >> "$GITHUB_ENV"
|
|
103
|
+
|
|
104
|
+
- name: Verify the branch exists up front
|
|
85
105
|
id: verify_branch
|
|
86
106
|
uses: actions/github-script@v9
|
|
87
107
|
with:
|
|
88
108
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
89
109
|
script: |
|
|
110
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
111
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
90
112
|
const branchName = '${{ github.event.client_payload.branch_name }}';
|
|
91
113
|
const prNumber = '${{ github.event.client_payload.pr_number }}';
|
|
92
114
|
const runId = '${{ github.run_id }}';
|
|
93
115
|
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
|
|
94
116
|
|
|
95
|
-
console.log(
|
|
117
|
+
console.log(msg('ios_test_tf.branch_check_log', { branch: branchName }));
|
|
96
118
|
|
|
97
119
|
try {
|
|
98
120
|
await github.rest.repos.getBranch({
|
|
@@ -100,26 +122,26 @@ jobs:
|
|
|
100
122
|
repo: context.repo.repo,
|
|
101
123
|
branch: branchName
|
|
102
124
|
});
|
|
103
|
-
console.log(
|
|
125
|
+
console.log(msg('ios_test_tf.branch_exists_log', { branch: branchName }));
|
|
104
126
|
core.setOutput('valid', 'true');
|
|
105
127
|
} catch (error) {
|
|
106
128
|
if (error.status === 404) {
|
|
107
|
-
console.log(
|
|
129
|
+
console.log(msg('ios_test_tf.branch_not_found_log', { branch: branchName }));
|
|
108
130
|
|
|
109
131
|
const body = [
|
|
110
|
-
'
|
|
132
|
+
msg('ios_test_tf.branch_missing_title'),
|
|
111
133
|
'',
|
|
112
|
-
'
|
|
134
|
+
msg('ios_test_tf.kv_head'),
|
|
113
135
|
'|------|-----|',
|
|
114
|
-
|
|
115
|
-
|
|
136
|
+
msg('ios_test_tf.row_requested_branch', { branch: branchName }),
|
|
137
|
+
msg('ios_test_tf.row_issue_pr', { number: prNumber }),
|
|
116
138
|
'',
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
139
|
+
msg('ios_test_tf.hint_title'),
|
|
140
|
+
msg('ios_test_tf.hint_pushed'),
|
|
141
|
+
msg('ios_test_tf.hint_helper_branch'),
|
|
142
|
+
msg('ios_test_tf.hint_typo'),
|
|
121
143
|
'',
|
|
122
|
-
|
|
144
|
+
msg('ios_test_tf.workflow_log_link', { url: runUrl })
|
|
123
145
|
].join('\n');
|
|
124
146
|
|
|
125
147
|
await github.rest.issues.createComment({
|
|
@@ -129,13 +151,13 @@ jobs:
|
|
|
129
151
|
body: body
|
|
130
152
|
});
|
|
131
153
|
|
|
132
|
-
core.setFailed(
|
|
154
|
+
core.setFailed(msg('ios_test_tf.branch_not_found_fail', { branch: branchName }));
|
|
133
155
|
} else {
|
|
134
|
-
core.setFailed(
|
|
156
|
+
core.setFailed(msg('ios_test_tf.branch_check_error_fail', { error: error.message }));
|
|
135
157
|
}
|
|
136
158
|
}
|
|
137
159
|
|
|
138
|
-
- name:
|
|
160
|
+
- name: Create the progress comment
|
|
139
161
|
id: progress
|
|
140
162
|
if: github.event.client_payload.pr_number != ''
|
|
141
163
|
continue-on-error: true
|
|
@@ -143,6 +165,9 @@ jobs:
|
|
|
143
165
|
with:
|
|
144
166
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
145
167
|
script: |
|
|
168
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
169
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
170
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
146
171
|
const prNumber = parseInt('${{ github.event.client_payload.pr_number }}');
|
|
147
172
|
const branchName = '${{ github.event.client_payload.branch_name }}';
|
|
148
173
|
const buildNumber = '${{ github.event.client_payload.build_number }}';
|
|
@@ -151,23 +176,23 @@ jobs:
|
|
|
151
176
|
const startTime = Date.now();
|
|
152
177
|
|
|
153
178
|
const body = [
|
|
154
|
-
'
|
|
179
|
+
msg('ios_test_tf.title_running'),
|
|
155
180
|
'',
|
|
156
|
-
'
|
|
181
|
+
msg('ios_test_tf.table_header'),
|
|
157
182
|
'|------|------|----------|',
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
'
|
|
183
|
+
row('prepare', 'running'),
|
|
184
|
+
row('build', 'waiting'),
|
|
185
|
+
row('deploy', 'waiting'),
|
|
161
186
|
'',
|
|
162
|
-
'
|
|
187
|
+
msg('ios_test_tf.kv_head'),
|
|
163
188
|
'|------|-----|',
|
|
164
|
-
|
|
165
|
-
|
|
189
|
+
msg('ios_test_tf.row_build_number', { build: buildNumber }),
|
|
190
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
166
191
|
'',
|
|
167
|
-
|
|
192
|
+
msg('ios_test_tf.live_log_link', { url: runUrl }),
|
|
168
193
|
'',
|
|
169
194
|
'---',
|
|
170
|
-
'
|
|
195
|
+
msg('ios_test_tf.auto_update_note')
|
|
171
196
|
].join('\n');
|
|
172
197
|
|
|
173
198
|
const { data: comment } = await github.rest.issues.createComment({
|
|
@@ -177,13 +202,13 @@ jobs:
|
|
|
177
202
|
body: body
|
|
178
203
|
});
|
|
179
204
|
|
|
180
|
-
console.log(
|
|
205
|
+
console.log(msg('ios_test_tf.log_progress_created', { id: comment.id }));
|
|
181
206
|
core.setOutput('comment_id', comment.id);
|
|
182
207
|
core.setOutput('start_time', startTime);
|
|
183
208
|
|
|
184
|
-
#
|
|
209
|
+
# Prepare the test build (macOS environment setup)
|
|
185
210
|
prepare-test-build:
|
|
186
|
-
name:
|
|
211
|
+
name: Prepare test build
|
|
187
212
|
runs-on: macos-26
|
|
188
213
|
defaults:
|
|
189
214
|
run:
|
|
@@ -204,6 +229,26 @@ jobs:
|
|
|
204
229
|
commit_hash: ${{ steps.release_notes.outputs.commit_hash }}
|
|
205
230
|
|
|
206
231
|
steps:
|
|
232
|
+
# Fetch the message catalog from the triggering ref first — the feature branch being built may predate it
|
|
233
|
+
- name: Checkout messages
|
|
234
|
+
uses: actions/checkout@v7
|
|
235
|
+
with:
|
|
236
|
+
path: .paw-msg
|
|
237
|
+
sparse-checkout: |
|
|
238
|
+
.github/scripts/messages.py
|
|
239
|
+
version.yml
|
|
240
|
+
sparse-checkout-cone-mode: false
|
|
241
|
+
|
|
242
|
+
- name: Load messages
|
|
243
|
+
working-directory: ${{ github.workspace }}
|
|
244
|
+
run: |
|
|
245
|
+
cp .paw-msg/.github/scripts/messages.py "$RUNNER_TEMP/paw_messages.py"
|
|
246
|
+
LANG_CODE=$(cd .paw-msg && python3 "$RUNNER_TEMP/paw_messages.py" lang)
|
|
247
|
+
rm -rf .paw-msg
|
|
248
|
+
echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
|
|
249
|
+
echo "PAW_MESSAGES_PY=$RUNNER_TEMP/paw_messages.py" >> "$GITHUB_ENV"
|
|
250
|
+
echo "PAW_MSG=$(PROJECT_AUTO_WIZARD_LANG=$LANG_CODE python3 "$RUNNER_TEMP/paw_messages.py" dump ios_test_tf.)" >> "$GITHUB_ENV"
|
|
251
|
+
|
|
207
252
|
- name: Checkout repository
|
|
208
253
|
uses: actions/checkout@v7
|
|
209
254
|
with:
|
|
@@ -213,8 +258,8 @@ jobs:
|
|
|
213
258
|
- name: Pull latest changes
|
|
214
259
|
run: git pull origin ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}
|
|
215
260
|
|
|
216
|
-
#
|
|
217
|
-
- name:
|
|
261
|
+
# Report every missing setting up front, before importing certificates or building — otherwise the macOS runner builds for a long time and fails at an unrelated step
|
|
262
|
+
- name: Validate deploy settings
|
|
218
263
|
env:
|
|
219
264
|
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
|
220
265
|
APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
|
|
@@ -223,6 +268,7 @@ jobs:
|
|
|
223
268
|
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
|
|
224
269
|
APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
|
|
225
270
|
run: |
|
|
271
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
226
272
|
FAILED=0
|
|
227
273
|
MISSING=""
|
|
228
274
|
for name in APPLE_CERTIFICATE_BASE64 APPLE_PROVISIONING_PROFILE_BASE64 IOS_PROVISIONING_PROFILE_NAME \
|
|
@@ -230,23 +276,23 @@ jobs:
|
|
|
230
276
|
[ -n "${!name}" ] || MISSING="$MISSING $name"
|
|
231
277
|
done
|
|
232
278
|
if [ -n "$MISSING" ]; then
|
|
233
|
-
echo "::error
|
|
279
|
+
echo "::error::$(m ios_test_tf.secrets_missing missing="$MISSING")"
|
|
234
280
|
FAILED=1
|
|
235
281
|
fi
|
|
236
282
|
if [ ! -f ios/ExportOptions.plist ]; then
|
|
237
|
-
echo "::error
|
|
283
|
+
echo "::error::$(m ios_test_tf.export_options_missing)"
|
|
238
284
|
FAILED=1
|
|
239
285
|
elif grep -Eq '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist; then
|
|
240
|
-
echo "::error
|
|
286
|
+
echo "::error::$(m ios_test_tf.export_options_placeholders)"
|
|
241
287
|
grep -En '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist
|
|
242
288
|
FAILED=1
|
|
243
289
|
fi
|
|
244
290
|
if [ ! -f ios/fastlane/Fastfile ]; then
|
|
245
|
-
echo "::error
|
|
291
|
+
echo "::error::$(m ios_test_tf.fastfile_missing_error)"
|
|
246
292
|
FAILED=1
|
|
247
293
|
fi
|
|
248
294
|
[ "$FAILED" -eq 0 ] || exit 1
|
|
249
|
-
|
|
295
|
+
m ios_test_tf.preflight_ok
|
|
250
296
|
|
|
251
297
|
- name: Select Xcode version
|
|
252
298
|
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
@@ -264,81 +310,85 @@ jobs:
|
|
|
264
310
|
echo "ℹ️ Empty Secrets.xcconfig created (no secrets provided)"
|
|
265
311
|
fi
|
|
266
312
|
|
|
267
|
-
- name:
|
|
313
|
+
- name: Set test build version
|
|
268
314
|
id: test_version
|
|
269
|
-
working-directory: ${{ github.workspace }} # version_manager.py
|
|
315
|
+
working-directory: ${{ github.workspace }} # version_manager.py must read version.yml at the repo root
|
|
270
316
|
run: |
|
|
271
|
-
|
|
317
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
318
|
+
# Get the version through version_manager.py (same approach as the production workflow)
|
|
272
319
|
VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
|
|
273
320
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
274
321
|
|
|
275
|
-
# repository_dispatch
|
|
322
|
+
# For repository_dispatch events, use the PR number as the build number
|
|
276
323
|
if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
|
|
277
324
|
BUILD_NUMBER="${{ github.event.client_payload.build_number }}"
|
|
278
325
|
PR_NUMBER="${{ github.event.client_payload.pr_number }}"
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
326
|
+
m ios_test_tf.event_dispatch
|
|
327
|
+
m ios_test_tf.event_pr_number pr="$PR_NUMBER"
|
|
328
|
+
m ios_test_tf.event_build_number_pr build="$BUILD_NUMBER"
|
|
282
329
|
else
|
|
283
330
|
BUILD_NUMBER="${{ github.run_number }}"
|
|
284
331
|
PR_NUMBER=""
|
|
285
|
-
|
|
286
|
-
|
|
332
|
+
m ios_test_tf.event_workflow_dispatch
|
|
333
|
+
m ios_test_tf.event_build_number_default build="$BUILD_NUMBER"
|
|
287
334
|
fi
|
|
288
335
|
|
|
289
336
|
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
|
|
290
337
|
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
|
291
|
-
|
|
338
|
+
m ios_test_tf.test_build_log version="$VERSION" build="$BUILD_NUMBER"
|
|
292
339
|
|
|
293
|
-
- name:
|
|
340
|
+
- name: Extract issue info from the branch name
|
|
294
341
|
id: issue_info
|
|
295
342
|
run: |
|
|
296
|
-
|
|
343
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
344
|
+
# For repository_dispatch, take the branch name from client_payload
|
|
297
345
|
if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
|
|
298
346
|
BRANCH_NAME="${{ github.event.client_payload.branch_name }}"
|
|
299
347
|
ISSUE_NUMBER="${{ github.event.client_payload.issue_number }}"
|
|
300
348
|
else
|
|
301
349
|
BRANCH_NAME="${{ github.ref_name }}"
|
|
302
|
-
#
|
|
350
|
+
# Extract the issue number from the branch name (`<date>_#<number>_...` format)
|
|
303
351
|
ISSUE_NUMBER=$(echo "$BRANCH_NAME" | sed -n 's/.*#\([0-9]*\).*/\1/p')
|
|
304
352
|
fi
|
|
305
353
|
|
|
306
|
-
|
|
354
|
+
m ios_test_tf.branch_name_log branch="$BRANCH_NAME"
|
|
307
355
|
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
308
356
|
|
|
309
357
|
if [ -z "$ISSUE_NUMBER" ]; then
|
|
310
|
-
|
|
358
|
+
m ios_test_tf.issue_number_missing
|
|
311
359
|
echo "issue_url=" >> $GITHUB_OUTPUT
|
|
312
360
|
echo "issue_title=" >> $GITHUB_OUTPUT
|
|
313
361
|
echo "issue_number=" >> $GITHUB_OUTPUT
|
|
314
362
|
else
|
|
315
|
-
|
|
363
|
+
m ios_test_tf.issue_number_extracted number="$ISSUE_NUMBER"
|
|
316
364
|
ISSUE_URL="https://github.com/${{ github.repository }}/issues/$ISSUE_NUMBER"
|
|
317
365
|
echo "issue_url=$ISSUE_URL" >> $GITHUB_OUTPUT
|
|
318
366
|
echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
|
|
319
367
|
|
|
320
|
-
#
|
|
321
|
-
|
|
368
|
+
# Fetch the issue info through the GitHub API
|
|
369
|
+
m ios_test_tf.issue_fetching
|
|
322
370
|
ISSUE_RESPONSE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
323
371
|
"https://api.github.com/repos/${{ github.repository }}/issues/$ISSUE_NUMBER")
|
|
324
372
|
|
|
325
|
-
|
|
373
|
+
NO_ISSUE_INFO="$(m ios_test_tf.issue_title_default)"
|
|
374
|
+
ISSUE_TITLE=$(echo "$ISSUE_RESPONSE" | jq -r --arg d "$NO_ISSUE_INFO" '.title // $d')
|
|
326
375
|
ISSUE_STATE=$(echo "$ISSUE_RESPONSE" | jq -r '.state // "unknown"')
|
|
327
376
|
|
|
328
|
-
if [ "$ISSUE_TITLE" = "null" ] || [ "$ISSUE_TITLE" = "
|
|
329
|
-
|
|
377
|
+
if [ "$ISSUE_TITLE" = "null" ] || [ "$ISSUE_TITLE" = "$NO_ISSUE_INFO" ]; then
|
|
378
|
+
m ios_test_tf.issue_not_found number="$ISSUE_NUMBER"
|
|
330
379
|
echo "issue_title=" >> $GITHUB_OUTPUT
|
|
331
380
|
else
|
|
332
|
-
|
|
333
|
-
|
|
381
|
+
m ios_test_tf.issue_title_log title="$ISSUE_TITLE"
|
|
382
|
+
m ios_test_tf.issue_state_log state="$ISSUE_STATE"
|
|
334
383
|
echo "issue_title=$ISSUE_TITLE" >> $GITHUB_OUTPUT
|
|
335
384
|
fi
|
|
336
385
|
fi
|
|
337
386
|
|
|
338
|
-
- name:
|
|
387
|
+
- name: Generate release notes
|
|
339
388
|
id: release_notes
|
|
340
|
-
working-directory: ${{ github.workspace }} # final_release_notes.txt(upload
|
|
389
|
+
working-directory: ${{ github.workspace }} # final_release_notes.txt (paired with the upload step) is relative to the repo root
|
|
341
390
|
run: |
|
|
391
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
342
392
|
BUILD_NUMBER="${{ steps.test_version.outputs.build_number }}"
|
|
343
393
|
BRANCH_NAME="${{ steps.issue_info.outputs.branch_name }}"
|
|
344
394
|
COMMIT_SHA="${{ github.sha }}"
|
|
@@ -347,30 +397,30 @@ jobs:
|
|
|
347
397
|
|
|
348
398
|
echo "commit_hash=$COMMIT_SHORT" >> $GITHUB_OUTPUT
|
|
349
399
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
#
|
|
400
|
+
{
|
|
401
|
+
m ios_test_tf.notes_build build="$BUILD_NUMBER"
|
|
402
|
+
echo
|
|
403
|
+
m ios_test_tf.notes_branch branch="$BRANCH_NAME"
|
|
404
|
+
m ios_test_tf.notes_commit commit="$COMMIT_SHORT"
|
|
405
|
+
m ios_test_tf.notes_date date="$BUILD_DATE"
|
|
406
|
+
echo
|
|
407
|
+
} > final_release_notes.txt
|
|
408
|
+
|
|
409
|
+
# Add the issue info when available
|
|
360
410
|
if [ -n "${{ steps.issue_info.outputs.issue_number }}" ]; then
|
|
361
411
|
ISSUE_NUMBER="${{ steps.issue_info.outputs.issue_number }}"
|
|
362
412
|
ISSUE_TITLE="${{ steps.issue_info.outputs.issue_title }}"
|
|
363
413
|
ISSUE_URL="${{ steps.issue_info.outputs.issue_url }}"
|
|
364
414
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
415
|
+
{
|
|
416
|
+
m ios_test_tf.notes_issue_header
|
|
417
|
+
echo "- #$ISSUE_NUMBER: $ISSUE_TITLE"
|
|
418
|
+
echo "- URL: $ISSUE_URL"
|
|
419
|
+
echo
|
|
420
|
+
} >> final_release_notes.txt
|
|
371
421
|
fi
|
|
372
422
|
|
|
373
|
-
|
|
423
|
+
m ios_test_tf.notes_generated
|
|
374
424
|
echo "----------------------------------------"
|
|
375
425
|
cat final_release_notes.txt
|
|
376
426
|
echo "----------------------------------------"
|
|
@@ -393,12 +443,15 @@ jobs:
|
|
|
393
443
|
${{ env.FLUTTER_PROJECT_DIR }}/assets/
|
|
394
444
|
retention-days: 1
|
|
395
445
|
|
|
396
|
-
- name:
|
|
446
|
+
- name: Progress update - prepare failed
|
|
397
447
|
if: failure() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
398
448
|
uses: actions/github-script@v9
|
|
399
449
|
with:
|
|
400
450
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
401
451
|
script: |
|
|
452
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
453
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
454
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
402
455
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
403
456
|
const startTime = parseInt('${{ needs.notify-start.outputs.start_time }}');
|
|
404
457
|
const branchName = '${{ github.event.client_payload.branch_name }}';
|
|
@@ -410,25 +463,25 @@ jobs:
|
|
|
410
463
|
const elapsed = now - startTime;
|
|
411
464
|
const minutes = Math.floor(elapsed / 60000);
|
|
412
465
|
const seconds = Math.floor((elapsed % 60000) / 1000);
|
|
413
|
-
const duration = minutes > 0 ?
|
|
466
|
+
const duration = minutes > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: minutes, s: seconds }) : msg('ios_test_tf.elapsed_sec', { s: seconds });
|
|
414
467
|
|
|
415
468
|
const body = [
|
|
416
|
-
'
|
|
469
|
+
msg('ios_test_tf.title_prepare_failed'),
|
|
417
470
|
'',
|
|
418
|
-
'
|
|
471
|
+
msg('ios_test_tf.table_header'),
|
|
419
472
|
'|------|------|----------|',
|
|
420
|
-
|
|
421
|
-
'
|
|
422
|
-
'
|
|
473
|
+
row('prepare', 'failed', duration),
|
|
474
|
+
row('build', 'canceled'),
|
|
475
|
+
row('deploy', 'canceled'),
|
|
423
476
|
'',
|
|
424
|
-
'
|
|
477
|
+
msg('ios_test_tf.kv_head'),
|
|
425
478
|
'|------|-----|',
|
|
426
|
-
|
|
427
|
-
|
|
479
|
+
msg('ios_test_tf.row_build_number', { build: buildNumber }),
|
|
480
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
428
481
|
'',
|
|
429
|
-
'
|
|
482
|
+
msg('ios_test_tf.prepare_error'),
|
|
430
483
|
'',
|
|
431
|
-
|
|
484
|
+
msg('ios_test_tf.run_log_check_link', { url: runUrl })
|
|
432
485
|
].join('\n');
|
|
433
486
|
|
|
434
487
|
await github.rest.issues.updateComment({
|
|
@@ -438,11 +491,11 @@ jobs:
|
|
|
438
491
|
body: body
|
|
439
492
|
});
|
|
440
493
|
|
|
441
|
-
console.log(
|
|
494
|
+
console.log(msg('ios_test_tf.log_prepare_failed'));
|
|
442
495
|
|
|
443
|
-
# iOS
|
|
496
|
+
# iOS build (uses xcodebuild directly)
|
|
444
497
|
build-ios-test:
|
|
445
|
-
name: iOS
|
|
498
|
+
name: iOS test build
|
|
446
499
|
runs-on: macos-26
|
|
447
500
|
defaults:
|
|
448
501
|
run:
|
|
@@ -459,14 +512,37 @@ jobs:
|
|
|
459
512
|
step2_end_time: ${{ steps.progress_step2.outputs.end_time }}
|
|
460
513
|
|
|
461
514
|
steps:
|
|
462
|
-
#
|
|
463
|
-
- name:
|
|
515
|
+
# Fetch the message catalog from the triggering ref first — the feature branch being built may predate it
|
|
516
|
+
- name: Checkout messages
|
|
517
|
+
uses: actions/checkout@v7
|
|
518
|
+
with:
|
|
519
|
+
path: .paw-msg
|
|
520
|
+
sparse-checkout: |
|
|
521
|
+
.github/scripts/messages.py
|
|
522
|
+
version.yml
|
|
523
|
+
sparse-checkout-cone-mode: false
|
|
524
|
+
|
|
525
|
+
- name: Load messages
|
|
526
|
+
working-directory: ${{ github.workspace }}
|
|
527
|
+
run: |
|
|
528
|
+
cp .paw-msg/.github/scripts/messages.py "$RUNNER_TEMP/paw_messages.py"
|
|
529
|
+
LANG_CODE=$(cd .paw-msg && python3 "$RUNNER_TEMP/paw_messages.py" lang)
|
|
530
|
+
rm -rf .paw-msg
|
|
531
|
+
echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
|
|
532
|
+
echo "PAW_MESSAGES_PY=$RUNNER_TEMP/paw_messages.py" >> "$GITHUB_ENV"
|
|
533
|
+
echo "PAW_MSG=$(PROJECT_AUTO_WIZARD_LANG=$LANG_CODE python3 "$RUNNER_TEMP/paw_messages.py" dump ios_test_tf.)" >> "$GITHUB_ENV"
|
|
534
|
+
|
|
535
|
+
# Progress update - prepare done (must be updated when the build job starts so the prepare time is shown accurately)
|
|
536
|
+
- name: Progress update - prepare done
|
|
464
537
|
id: progress_step1
|
|
465
538
|
if: github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
466
539
|
uses: actions/github-script@v9
|
|
467
540
|
with:
|
|
468
541
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
469
542
|
script: |
|
|
543
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
544
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
545
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
470
546
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
471
547
|
const startTime = parseInt('${{ needs.notify-start.outputs.start_time }}');
|
|
472
548
|
const branchName = '${{ github.event.client_payload.branch_name }}';
|
|
@@ -479,26 +555,26 @@ jobs:
|
|
|
479
555
|
const elapsed = Math.round((now - startTime) / 1000);
|
|
480
556
|
const minutes = Math.floor(elapsed / 60);
|
|
481
557
|
const seconds = elapsed % 60;
|
|
482
|
-
const elapsedStr = minutes > 0 ?
|
|
558
|
+
const elapsedStr = minutes > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: minutes, s: seconds }) : msg('ios_test_tf.elapsed_sec', { s: seconds });
|
|
483
559
|
|
|
484
560
|
const body = [
|
|
485
|
-
'
|
|
561
|
+
msg('ios_test_tf.title_running'),
|
|
486
562
|
'',
|
|
487
|
-
'
|
|
563
|
+
msg('ios_test_tf.table_header'),
|
|
488
564
|
'|------|------|----------|',
|
|
489
|
-
|
|
490
|
-
'
|
|
491
|
-
'
|
|
565
|
+
row('prepare', 'done', elapsedStr),
|
|
566
|
+
row('build', 'running'),
|
|
567
|
+
row('deploy', 'waiting'),
|
|
492
568
|
'',
|
|
493
|
-
'
|
|
569
|
+
msg('ios_test_tf.kv_head'),
|
|
494
570
|
'|------|-----|',
|
|
495
|
-
|
|
496
|
-
|
|
571
|
+
msg('ios_test_tf.row_app_version', { version, build: buildNumber }),
|
|
572
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
497
573
|
'',
|
|
498
|
-
|
|
574
|
+
msg('ios_test_tf.live_log_link', { url: runUrl }),
|
|
499
575
|
'',
|
|
500
576
|
'---',
|
|
501
|
-
'
|
|
577
|
+
msg('ios_test_tf.auto_update_note')
|
|
502
578
|
].join('\n');
|
|
503
579
|
|
|
504
580
|
await github.rest.issues.updateComment({
|
|
@@ -508,7 +584,7 @@ jobs:
|
|
|
508
584
|
body: body
|
|
509
585
|
});
|
|
510
586
|
|
|
511
|
-
console.log(
|
|
587
|
+
console.log(msg('ios_test_tf.log_prepare_done', { elapsed: elapsedStr }));
|
|
512
588
|
core.setOutput('elapsed', elapsedStr);
|
|
513
589
|
core.setOutput('end_time', now.toString());
|
|
514
590
|
|
|
@@ -526,7 +602,7 @@ jobs:
|
|
|
526
602
|
|
|
527
603
|
- name: Install iOS device platform
|
|
528
604
|
env:
|
|
529
|
-
# xcode-select
|
|
605
|
+
# xcode-select alone does not propagate to subprocesses, so set it explicitly
|
|
530
606
|
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
531
607
|
run: |
|
|
532
608
|
echo "=== iPhoneOS.platform check ==="
|
|
@@ -590,12 +666,13 @@ jobs:
|
|
|
590
666
|
|
|
591
667
|
- name: Install CocoaPods
|
|
592
668
|
run: |
|
|
593
|
-
|
|
669
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
670
|
+
# A default project without plugins has no Podfile — flutter build creates one once a plugin is added
|
|
594
671
|
if [ -f ios/Podfile ]; then
|
|
595
672
|
gem install cocoapods
|
|
596
673
|
(cd ios && pod install)
|
|
597
674
|
else
|
|
598
|
-
|
|
675
|
+
m ios_test_tf.podfile_skip
|
|
599
676
|
fi
|
|
600
677
|
|
|
601
678
|
- name: Import Code-Signing Certificates
|
|
@@ -616,17 +693,18 @@ jobs:
|
|
|
616
693
|
|
|
617
694
|
- name: Verify ExportOptions.plist
|
|
618
695
|
run: |
|
|
696
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
619
697
|
cd ios
|
|
620
698
|
if [ ! -f "ExportOptions.plist" ]; then
|
|
621
|
-
|
|
622
|
-
|
|
699
|
+
m ios_test_tf.plist_missing
|
|
700
|
+
m ios_test_tf.plist_missing_hint
|
|
623
701
|
exit 1
|
|
624
702
|
fi
|
|
625
|
-
|
|
703
|
+
m ios_test_tf.plist_ok
|
|
626
704
|
|
|
627
705
|
- name: Flutter build (no codesign)
|
|
628
706
|
env:
|
|
629
|
-
# xcode-select
|
|
707
|
+
# xcode-select alone does not propagate to subprocesses, so set it explicitly
|
|
630
708
|
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
631
709
|
run: |
|
|
632
710
|
flutter build ios --release --no-codesign \
|
|
@@ -637,7 +715,7 @@ jobs:
|
|
|
637
715
|
- name: Create Archive
|
|
638
716
|
env:
|
|
639
717
|
IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
|
|
640
|
-
# xcode-select
|
|
718
|
+
# xcode-select alone does not propagate to subprocesses, so set it explicitly
|
|
641
719
|
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
642
720
|
run: |
|
|
643
721
|
cd ios
|
|
@@ -679,13 +757,16 @@ jobs:
|
|
|
679
757
|
${{ env.FLUTTER_PROJECT_DIR }}/build-metadata.json
|
|
680
758
|
retention-days: 1
|
|
681
759
|
|
|
682
|
-
- name:
|
|
760
|
+
- name: Progress update - IPA build done
|
|
683
761
|
id: progress_step2
|
|
684
762
|
if: success() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
685
763
|
uses: actions/github-script@v9
|
|
686
764
|
with:
|
|
687
765
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
688
766
|
script: |
|
|
767
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
768
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
769
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
689
770
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
690
771
|
const step1EndTime = parseInt('${{ steps.progress_step1.outputs.end_time }}');
|
|
691
772
|
const step1Elapsed = '${{ steps.progress_step1.outputs.elapsed }}';
|
|
@@ -697,30 +778,30 @@ jobs:
|
|
|
697
778
|
|
|
698
779
|
const now = Date.now();
|
|
699
780
|
|
|
700
|
-
//
|
|
781
|
+
// Build step duration (step1 end to now)
|
|
701
782
|
const elapsed = Math.round((now - step1EndTime) / 1000);
|
|
702
783
|
const buildMin = Math.floor(elapsed / 60);
|
|
703
784
|
const buildSec = elapsed % 60;
|
|
704
|
-
const elapsedStr = buildMin > 0 ?
|
|
785
|
+
const elapsedStr = buildMin > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: buildMin, s: buildSec }) : msg('ios_test_tf.elapsed_sec', { s: buildSec });
|
|
705
786
|
|
|
706
787
|
const body = [
|
|
707
|
-
'
|
|
788
|
+
msg('ios_test_tf.title_ipa_done'),
|
|
708
789
|
'',
|
|
709
|
-
'
|
|
790
|
+
msg('ios_test_tf.table_header'),
|
|
710
791
|
'|------|------|----------|',
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
'
|
|
792
|
+
row('prepare', 'done', step1Elapsed || '-'),
|
|
793
|
+
row('build', 'done', elapsedStr),
|
|
794
|
+
row('deploy', 'running'),
|
|
714
795
|
'',
|
|
715
|
-
'
|
|
796
|
+
msg('ios_test_tf.kv_head'),
|
|
716
797
|
'|------|-----|',
|
|
717
|
-
|
|
718
|
-
|
|
798
|
+
msg('ios_test_tf.row_app_version', { version, build: buildNumber }),
|
|
799
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
719
800
|
'',
|
|
720
|
-
|
|
801
|
+
msg('ios_test_tf.live_log_link', { url: runUrl }),
|
|
721
802
|
'',
|
|
722
803
|
'---',
|
|
723
|
-
'
|
|
804
|
+
msg('ios_test_tf.auto_update_note')
|
|
724
805
|
].join('\n');
|
|
725
806
|
|
|
726
807
|
await github.rest.issues.updateComment({
|
|
@@ -730,16 +811,19 @@ jobs:
|
|
|
730
811
|
body: body
|
|
731
812
|
});
|
|
732
813
|
|
|
733
|
-
console.log(
|
|
814
|
+
console.log(msg('ios_test_tf.log_ipa_done', { elapsed: elapsedStr }));
|
|
734
815
|
core.setOutput('elapsed', elapsedStr);
|
|
735
816
|
core.setOutput('end_time', now.toString());
|
|
736
817
|
|
|
737
|
-
- name:
|
|
818
|
+
- name: Update the progress comment on build failure
|
|
738
819
|
if: failure() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
739
820
|
uses: actions/github-script@v9
|
|
740
821
|
with:
|
|
741
822
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
742
823
|
script: |
|
|
824
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
825
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
826
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
743
827
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
744
828
|
const startTime = parseInt('${{ needs.notify-start.outputs.start_time }}');
|
|
745
829
|
const step1EndTime = parseInt('${{ steps.progress_step1.outputs.end_time }}') || 0;
|
|
@@ -753,33 +837,33 @@ jobs:
|
|
|
753
837
|
|
|
754
838
|
const now = Date.now();
|
|
755
839
|
|
|
756
|
-
//
|
|
840
|
+
// Build step duration (step1 end to now)
|
|
757
841
|
let step2ElapsedStr = '-';
|
|
758
842
|
if (step1EndTime > 0) {
|
|
759
843
|
const step2_s = Math.round((now - step1EndTime) / 1000);
|
|
760
844
|
const s2Min = Math.floor(step2_s / 60);
|
|
761
845
|
const s2Sec = step2_s % 60;
|
|
762
|
-
step2ElapsedStr = s2Min > 0 ?
|
|
846
|
+
step2ElapsedStr = s2Min > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: s2Min, s: s2Sec }) : msg('ios_test_tf.elapsed_sec', { s: s2Sec });
|
|
763
847
|
}
|
|
764
848
|
|
|
765
849
|
const body = [
|
|
766
|
-
'
|
|
850
|
+
msg('ios_test_tf.title_ipa_failed'),
|
|
767
851
|
'',
|
|
768
|
-
'
|
|
852
|
+
msg('ios_test_tf.table_header'),
|
|
769
853
|
'|------|------|----------|',
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
'
|
|
854
|
+
row('prepare', 'done', step1Elapsed || '-'),
|
|
855
|
+
row('build', 'failed', step2ElapsedStr),
|
|
856
|
+
row('deploy', 'canceled'),
|
|
773
857
|
'',
|
|
774
|
-
'
|
|
858
|
+
msg('ios_test_tf.kv_head'),
|
|
775
859
|
'|------|-----|',
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
860
|
+
msg('ios_test_tf.row_app_version', { version, build: buildNumber }),
|
|
861
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
862
|
+
msg('ios_test_tf.row_commit', { commit: commitHash }),
|
|
779
863
|
'',
|
|
780
|
-
'
|
|
864
|
+
msg('ios_test_tf.ipa_error'),
|
|
781
865
|
'',
|
|
782
|
-
|
|
866
|
+
msg('ios_test_tf.run_log_check_link', { url: runUrl })
|
|
783
867
|
].join('\n');
|
|
784
868
|
|
|
785
869
|
await github.rest.issues.updateComment({
|
|
@@ -789,11 +873,11 @@ jobs:
|
|
|
789
873
|
body: body
|
|
790
874
|
});
|
|
791
875
|
|
|
792
|
-
console.log(
|
|
876
|
+
console.log(msg('ios_test_tf.log_ipa_failed'));
|
|
793
877
|
|
|
794
|
-
# TestFlight
|
|
878
|
+
# TestFlight deploy (uses ios/fastlane/Fastfile)
|
|
795
879
|
deploy-testflight-test:
|
|
796
|
-
name: TestFlight
|
|
880
|
+
name: TestFlight test deploy
|
|
797
881
|
runs-on: macos-26
|
|
798
882
|
defaults:
|
|
799
883
|
run:
|
|
@@ -804,6 +888,26 @@ jobs:
|
|
|
804
888
|
needs.build-ios-test.result == 'success'
|
|
805
889
|
|
|
806
890
|
steps:
|
|
891
|
+
# Fetch the message catalog from the triggering ref first — the feature branch being built may predate it
|
|
892
|
+
- name: Checkout messages
|
|
893
|
+
uses: actions/checkout@v7
|
|
894
|
+
with:
|
|
895
|
+
path: .paw-msg
|
|
896
|
+
sparse-checkout: |
|
|
897
|
+
.github/scripts/messages.py
|
|
898
|
+
version.yml
|
|
899
|
+
sparse-checkout-cone-mode: false
|
|
900
|
+
|
|
901
|
+
- name: Load messages
|
|
902
|
+
working-directory: ${{ github.workspace }}
|
|
903
|
+
run: |
|
|
904
|
+
cp .paw-msg/.github/scripts/messages.py "$RUNNER_TEMP/paw_messages.py"
|
|
905
|
+
LANG_CODE=$(cd .paw-msg && python3 "$RUNNER_TEMP/paw_messages.py" lang)
|
|
906
|
+
rm -rf .paw-msg
|
|
907
|
+
echo "PROJECT_AUTO_WIZARD_LANG=$LANG_CODE" >> "$GITHUB_ENV"
|
|
908
|
+
echo "PAW_MESSAGES_PY=$RUNNER_TEMP/paw_messages.py" >> "$GITHUB_ENV"
|
|
909
|
+
echo "PAW_MSG=$(PROJECT_AUTO_WIZARD_LANG=$LANG_CODE python3 "$RUNNER_TEMP/paw_messages.py" dump ios_test_tf.)" >> "$GITHUB_ENV"
|
|
910
|
+
|
|
807
911
|
- name: Checkout repository
|
|
808
912
|
uses: actions/checkout@v7
|
|
809
913
|
with:
|
|
@@ -832,21 +936,23 @@ jobs:
|
|
|
832
936
|
|
|
833
937
|
- name: Verify Fastfile exists
|
|
834
938
|
run: |
|
|
939
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
835
940
|
if [ ! -f "ios/fastlane/Fastfile" ]; then
|
|
836
|
-
|
|
837
|
-
|
|
941
|
+
m ios_test_tf.fastfile_missing
|
|
942
|
+
m ios_test_tf.fastfile_missing_hint
|
|
838
943
|
exit 1
|
|
839
944
|
fi
|
|
840
|
-
|
|
945
|
+
m ios_test_tf.fastfile_ok
|
|
841
946
|
|
|
842
947
|
- name: Install Fastlane
|
|
843
948
|
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/ios
|
|
844
949
|
run: |
|
|
845
|
-
|
|
950
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
951
|
+
# Use the repository's Gemfile as-is if it lists fastlane (committing Gemfile.lock is recommended). Otherwise generate one.
|
|
846
952
|
if [ -f Gemfile ] && grep -Eq "['\"]fastlane['\"]" Gemfile; then
|
|
847
|
-
|
|
953
|
+
m ios_test_tf.gemfile_repo
|
|
848
954
|
else
|
|
849
|
-
# multi_json: google-apis transitive
|
|
955
|
+
# multi_json: works around an upstream bug where a google-apis transitive dependency omits it from its gemspec (Gem::LoadError)
|
|
850
956
|
printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
|
|
851
957
|
fi
|
|
852
958
|
bundle install
|
|
@@ -857,27 +963,28 @@ jobs:
|
|
|
857
963
|
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
858
964
|
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
|
|
859
965
|
run: |
|
|
860
|
-
|
|
966
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
967
|
+
# Find the IPA file
|
|
861
968
|
IPA_PATH=$(find "$GITHUB_WORKSPACE/${FLUTTER_PROJECT_DIR}/ios/build/ipa" -name "*.ipa" | head -1)
|
|
862
969
|
echo "📦 Found IPA at: $IPA_PATH"
|
|
863
970
|
|
|
864
971
|
if [ ! -f "$IPA_PATH" ]; then
|
|
865
|
-
|
|
972
|
+
m ios_test_tf.ipa_missing
|
|
866
973
|
ls -la ios/build/ipa/
|
|
867
974
|
exit 1
|
|
868
975
|
fi
|
|
869
976
|
|
|
870
|
-
#
|
|
977
|
+
# Prepare release notes (final_release_notes.txt is relative to the repo root — use an absolute path since working-directory is FLUTTER_PROJECT_DIR)
|
|
871
978
|
if [ -f "$GITHUB_WORKSPACE/final_release_notes.txt" ]; then
|
|
872
979
|
RELEASE_NOTES=$(cat "$GITHUB_WORKSPACE/final_release_notes.txt")
|
|
873
980
|
echo "📝 Release Notes:"
|
|
874
981
|
echo "$RELEASE_NOTES"
|
|
875
982
|
else
|
|
876
|
-
RELEASE_NOTES="
|
|
877
|
-
|
|
983
|
+
RELEASE_NOTES="$(m ios_test_tf.notes_build build="${{ needs.prepare-test-build.outputs.build_number }}")"
|
|
984
|
+
m ios_test_tf.default_notes_echo notes="$RELEASE_NOTES"
|
|
878
985
|
fi
|
|
879
986
|
|
|
880
|
-
#
|
|
987
|
+
# Set the environment variables (used by the Fastfile)
|
|
881
988
|
export API_KEY_PATH="$HOME/.appstoreconnect/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8"
|
|
882
989
|
export IPA_PATH="$IPA_PATH"
|
|
883
990
|
export RELEASE_NOTES="$RELEASE_NOTES"
|
|
@@ -886,37 +993,42 @@ jobs:
|
|
|
886
993
|
echo "🔧 IPA_PATH: $IPA_PATH"
|
|
887
994
|
echo "🔧 API_KEY_PATH: $API_KEY_PATH"
|
|
888
995
|
|
|
889
|
-
# Fastlane
|
|
996
|
+
# Run Fastlane (uses ios/fastlane/Fastfile)
|
|
890
997
|
cd ios
|
|
891
998
|
bundle exec fastlane upload_testflight
|
|
892
999
|
|
|
893
1000
|
- name: Notify TestFlight Upload Success
|
|
894
1001
|
if: success()
|
|
895
1002
|
run: |
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1003
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
1004
|
+
m ios_test_tf.upload_ok
|
|
1005
|
+
m ios_test_tf.upload_version version="${{ needs.prepare-test-build.outputs.version }}"
|
|
1006
|
+
m ios_test_tf.upload_build build="${{ needs.prepare-test-build.outputs.build_number }}"
|
|
899
1007
|
if [ -n "${{ needs.prepare-test-build.outputs.pr_number }}" ]; then
|
|
900
|
-
|
|
1008
|
+
m ios_test_tf.upload_pr pr="${{ needs.prepare-test-build.outputs.pr_number }}"
|
|
901
1009
|
fi
|
|
902
|
-
|
|
903
|
-
|
|
1010
|
+
m ios_test_tf.upload_branch branch="${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
|
|
1011
|
+
m ios_test_tf.upload_commit sha="${{ github.sha }}"
|
|
904
1012
|
if [ -n "${{ needs.prepare-test-build.outputs.issue_url }}" ]; then
|
|
905
|
-
|
|
1013
|
+
m ios_test_tf.upload_issue url="${{ needs.prepare-test-build.outputs.issue_url }}"
|
|
906
1014
|
fi
|
|
907
1015
|
|
|
908
1016
|
- name: Notify on Failure
|
|
909
1017
|
if: failure()
|
|
910
1018
|
run: |
|
|
911
|
-
|
|
912
|
-
|
|
1019
|
+
m() { python3 "$PAW_MESSAGES_PY" get "$@"; }
|
|
1020
|
+
m ios_test_tf.upload_failed
|
|
1021
|
+
m ios_test_tf.check_logs
|
|
913
1022
|
|
|
914
|
-
- name:
|
|
1023
|
+
- name: Final progress update - success
|
|
915
1024
|
if: success() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
916
1025
|
uses: actions/github-script@v9
|
|
917
1026
|
with:
|
|
918
1027
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
919
1028
|
script: |
|
|
1029
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
1030
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
1031
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
920
1032
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
921
1033
|
const startTime = parseInt('${{ needs.notify-start.outputs.start_time }}');
|
|
922
1034
|
const step2EndTime = parseInt('${{ needs.build-ios-test.outputs.step2_end_time }}');
|
|
@@ -931,37 +1043,37 @@ jobs:
|
|
|
931
1043
|
|
|
932
1044
|
const now = Date.now();
|
|
933
1045
|
|
|
934
|
-
//
|
|
1046
|
+
// Deploy step duration (step2 end to now)
|
|
935
1047
|
const step3_s = Math.round((now - step2EndTime) / 1000);
|
|
936
1048
|
const s3Min = Math.floor(step3_s / 60);
|
|
937
1049
|
const s3Sec = step3_s % 60;
|
|
938
|
-
const step3Elapsed = s3Min > 0 ?
|
|
1050
|
+
const step3Elapsed = s3Min > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: s3Min, s: s3Sec }) : msg('ios_test_tf.elapsed_sec', { s: s3Sec });
|
|
939
1051
|
|
|
940
|
-
//
|
|
1052
|
+
// Total duration
|
|
941
1053
|
const total_s = Math.round((now - startTime) / 1000);
|
|
942
1054
|
const tMin = Math.floor(total_s / 60);
|
|
943
1055
|
const tSec = total_s % 60;
|
|
944
|
-
const totalElapsed = tMin > 0 ?
|
|
1056
|
+
const totalElapsed = tMin > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: tMin, s: tSec }) : msg('ios_test_tf.elapsed_sec', { s: tSec });
|
|
945
1057
|
|
|
946
1058
|
const body = [
|
|
947
|
-
'
|
|
1059
|
+
msg('ios_test_tf.title_deploy_done'),
|
|
948
1060
|
'',
|
|
949
|
-
'
|
|
1061
|
+
msg('ios_test_tf.table_header'),
|
|
950
1062
|
'|------|------|----------|',
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1063
|
+
row('prepare', 'done', step1Elapsed || '-'),
|
|
1064
|
+
row('build', 'done', step2Elapsed || '-'),
|
|
1065
|
+
row('deploy', 'done', step3Elapsed),
|
|
954
1066
|
'',
|
|
955
|
-
'
|
|
1067
|
+
msg('ios_test_tf.kv_head'),
|
|
956
1068
|
'|------|-----|',
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1069
|
+
msg('ios_test_tf.row_app_version', { version, build: buildNumber }),
|
|
1070
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
1071
|
+
msg('ios_test_tf.row_commit', { commit: commitHash }),
|
|
1072
|
+
msg('ios_test_tf.row_total', { elapsed: totalElapsed }),
|
|
961
1073
|
'',
|
|
962
|
-
'
|
|
1074
|
+
msg('ios_test_tf.download_hint'),
|
|
963
1075
|
'',
|
|
964
|
-
|
|
1076
|
+
msg('ios_test_tf.run_log_link', { url: runUrl })
|
|
965
1077
|
].join('\n');
|
|
966
1078
|
|
|
967
1079
|
await github.rest.issues.updateComment({
|
|
@@ -971,14 +1083,17 @@ jobs:
|
|
|
971
1083
|
body: body
|
|
972
1084
|
});
|
|
973
1085
|
|
|
974
|
-
console.log(
|
|
1086
|
+
console.log(msg('ios_test_tf.log_all_done', { elapsed: totalElapsed }));
|
|
975
1087
|
|
|
976
|
-
- name:
|
|
1088
|
+
- name: Final progress update - failure
|
|
977
1089
|
if: failure() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && needs.notify-start.outputs.comment_id != ''
|
|
978
1090
|
uses: actions/github-script@v9
|
|
979
1091
|
with:
|
|
980
1092
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
981
1093
|
script: |
|
|
1094
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
1095
|
+
const msg = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
1096
|
+
const row = (name, status, elapsed = '-') => `| ${msg('ios_test_tf.step_' + name)} | ${msg('ios_test_tf.st_' + status)} | ${elapsed} |`;
|
|
982
1097
|
const commentId = parseInt('${{ needs.notify-start.outputs.comment_id }}');
|
|
983
1098
|
const startTime = parseInt('${{ needs.notify-start.outputs.start_time }}');
|
|
984
1099
|
const step2EndTime = parseInt('${{ needs.build-ios-test.outputs.step2_end_time }}') || 0;
|
|
@@ -993,33 +1108,33 @@ jobs:
|
|
|
993
1108
|
|
|
994
1109
|
const now = Date.now();
|
|
995
1110
|
|
|
996
|
-
//
|
|
1111
|
+
// Deploy step duration (step2 end to now)
|
|
997
1112
|
let step3Elapsed = '-';
|
|
998
1113
|
if (step2EndTime > 0) {
|
|
999
1114
|
const step3_s = Math.round((now - step2EndTime) / 1000);
|
|
1000
1115
|
const s3Min = Math.floor(step3_s / 60);
|
|
1001
1116
|
const s3Sec = step3_s % 60;
|
|
1002
|
-
step3Elapsed = s3Min > 0 ?
|
|
1117
|
+
step3Elapsed = s3Min > 0 ? msg('ios_test_tf.elapsed_min_sec', { m: s3Min, s: s3Sec }) : msg('ios_test_tf.elapsed_sec', { s: s3Sec });
|
|
1003
1118
|
}
|
|
1004
1119
|
|
|
1005
1120
|
const body = [
|
|
1006
|
-
'
|
|
1121
|
+
msg('ios_test_tf.title_deploy_failed'),
|
|
1007
1122
|
'',
|
|
1008
|
-
'
|
|
1123
|
+
msg('ios_test_tf.table_header'),
|
|
1009
1124
|
'|------|------|----------|',
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1125
|
+
row('prepare', 'done', step1Elapsed || '-'),
|
|
1126
|
+
row('build', 'done', step2Elapsed || '-'),
|
|
1127
|
+
row('deploy', 'failed', step3Elapsed),
|
|
1013
1128
|
'',
|
|
1014
|
-
'
|
|
1129
|
+
msg('ios_test_tf.kv_head'),
|
|
1015
1130
|
'|------|-----|',
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1131
|
+
msg('ios_test_tf.row_app_version', { version, build: buildNumber }),
|
|
1132
|
+
msg('ios_test_tf.row_branch', { branch: branchName }),
|
|
1133
|
+
msg('ios_test_tf.row_commit', { commit: commitHash }),
|
|
1019
1134
|
'',
|
|
1020
|
-
'
|
|
1135
|
+
msg('ios_test_tf.deploy_error'),
|
|
1021
1136
|
'',
|
|
1022
|
-
|
|
1137
|
+
msg('ios_test_tf.run_log_check_link', { url: runUrl })
|
|
1023
1138
|
].join('\n');
|
|
1024
1139
|
|
|
1025
1140
|
await github.rest.issues.updateComment({
|
|
@@ -1029,4 +1144,4 @@ jobs:
|
|
|
1029
1144
|
body: body
|
|
1030
1145
|
});
|
|
1031
1146
|
|
|
1032
|
-
console.log(
|
|
1147
|
+
console.log(msg('ios_test_tf.log_deploy_failed'));
|