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,45 +1,45 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Flutter CI (
|
|
2
|
+
# Flutter CI (code analysis and build verification) workflow
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# On a pull request or a push to the {{MAIN_BRANCH}} branch, this workflow
|
|
5
|
+
# verifies static analysis (flutter analyze), tests (flutter test) and the Android/iOS app builds.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
# -
|
|
9
|
-
# - {{MAIN_BRANCH}}
|
|
10
|
-
# -
|
|
11
|
-
# - test/
|
|
12
|
-
# - Android/iOS
|
|
13
|
-
# - Analyze Only
|
|
14
|
-
# - PR
|
|
15
|
-
# -
|
|
16
|
-
# - Analyze
|
|
7
|
+
# Key features:
|
|
8
|
+
# - Runs automatically when a PR is opened/updated
|
|
9
|
+
# - Runs automatically on a push to the {{MAIN_BRANCH}} branch
|
|
10
|
+
# - Checks code quality with flutter analyze
|
|
11
|
+
# - Runs flutter test when test/ contains tests (*_test.dart); skipped otherwise
|
|
12
|
+
# - Android/iOS builds can be enabled/disabled individually
|
|
13
|
+
# - Analyze Only mode: run only the code analysis, without builds
|
|
14
|
+
# - On a PR, progress is updated live in a comment
|
|
15
|
+
# - Detailed error info when a build fails
|
|
16
|
+
# - Analyze and builds run in parallel to save time
|
|
17
17
|
#
|
|
18
|
-
#
|
|
19
|
-
# 1.
|
|
20
|
-
# 2. ANALYZE_ONLY
|
|
21
|
-
# 3.
|
|
22
|
-
# 4.
|
|
18
|
+
# Usage:
|
|
19
|
+
# 1. Edit the values in the env section at the top for your project
|
|
20
|
+
# 2. Set ANALYZE_ONLY to true to run only the code analysis, without builds
|
|
21
|
+
# 3. Choose the build target platforms with ENABLE_ANDROID and ENABLE_IOS
|
|
22
|
+
# 4. Customize the env file path with ENV_FILE_PATH
|
|
23
23
|
#
|
|
24
|
-
#
|
|
24
|
+
# Project settings
|
|
25
25
|
#
|
|
26
|
-
# ANALYZE_ONLY : true
|
|
27
|
-
# ENABLE_ANDROID :
|
|
28
|
-
# ENABLE_IOS :
|
|
29
|
-
# FLUTTER_VERSION : Flutter SDK
|
|
30
|
-
# JAVA_VERSION : Java
|
|
31
|
-
# XCODE_VERSION : Xcode
|
|
32
|
-
# ENV_FILE_PATH :
|
|
33
|
-
# ENV_MODE :
|
|
34
|
-
# FLUTTER_PROJECT_DIR : Flutter
|
|
35
|
-
# PROJECT_PATH :
|
|
26
|
+
# ANALYZE_ONLY : if true, only analyze and test run and builds are skipped (default: false)
|
|
27
|
+
# ENABLE_ANDROID : whether to build Android (when ANALYZE_ONLY=false)
|
|
28
|
+
# ENABLE_IOS : whether to build iOS (when ANALYZE_ONLY=false)
|
|
29
|
+
# FLUTTER_VERSION : Flutter SDK version (empty = latest stable)
|
|
30
|
+
# JAVA_VERSION : Java version (for the Android build)
|
|
31
|
+
# XCODE_VERSION : Xcode version (for the iOS build)
|
|
32
|
+
# ENV_FILE_PATH : path where .env is created in dotenv mode (relative to the Flutter root, default: .env)
|
|
33
|
+
# ENV_MODE : how env vars are passed - dart-define (--dart-define-from-file) | dotenv (.env file)
|
|
34
|
+
# FLUTTER_PROJECT_DIR : Flutter root path (set automatically at install; app etc. for a monorepo)
|
|
35
|
+
# PROJECT_PATH : path used for change detection (set automatically at install; "." always runs)
|
|
36
36
|
#
|
|
37
|
-
#
|
|
38
|
-
# ENV_FILE (
|
|
37
|
+
# Required GitHub Secrets
|
|
38
|
+
# ENV_FILE (optional): contents of the .env file
|
|
39
39
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# (
|
|
40
|
+
# Note: CI only verifies builds, so no signing/deploy Secrets are needed
|
|
41
|
+
# Note: to use this CI as a required check in a monorepo, register only `ci-gate`, not the individual jobs.
|
|
42
|
+
# (jobs skipped because no paths changed count as Success, and ci-gate decides the final result)
|
|
43
43
|
|
|
44
44
|
name: PROJECT-Flutter-CI
|
|
45
45
|
|
|
@@ -52,15 +52,15 @@ on:
|
|
|
52
52
|
workflow_dispatch:
|
|
53
53
|
inputs:
|
|
54
54
|
analyze_only:
|
|
55
|
-
description: 'Analyze Only
|
|
55
|
+
description: 'Analyze Only mode (code analysis only, no builds)'
|
|
56
56
|
type: boolean
|
|
57
57
|
default: false
|
|
58
58
|
enable_android:
|
|
59
|
-
description: 'Android
|
|
59
|
+
description: 'Enable the Android build (when Analyze Only is false)'
|
|
60
60
|
type: boolean
|
|
61
61
|
default: true
|
|
62
62
|
enable_ios:
|
|
63
|
-
description: 'iOS
|
|
63
|
+
description: 'Enable the iOS build (when Analyze Only is false)'
|
|
64
64
|
type: boolean
|
|
65
65
|
default: true
|
|
66
66
|
|
|
@@ -68,38 +68,38 @@ permissions:
|
|
|
68
68
|
contents: read
|
|
69
69
|
pull-requests: write
|
|
70
70
|
|
|
71
|
-
#
|
|
71
|
+
# Project settings (edit the values below)
|
|
72
72
|
env:
|
|
73
|
-
#
|
|
73
|
+
# Monorepo change-detection path (set automatically at install; "." for a single repo)
|
|
74
74
|
PROJECT_PATH: "." # @wizard auto:project-path
|
|
75
|
-
# Flutter
|
|
75
|
+
# Flutter root path (relative to the repo root): "." for a single repo, "app" etc. for a monorepo - set automatically at install
|
|
76
76
|
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
77
|
-
#
|
|
77
|
+
# How env vars are passed (dart-define | dotenv) - set automatically from the install-time choice
|
|
78
78
|
ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
|
|
79
79
|
|
|
80
|
-
# CI
|
|
81
|
-
ANALYZE_ONLY: "false" # true: analyze
|
|
80
|
+
# CI mode settings
|
|
81
|
+
ANALYZE_ONLY: "false" # true: run only analyze and test (skip builds), false: analyze, test and builds
|
|
82
82
|
|
|
83
|
-
#
|
|
84
|
-
ENABLE_ANDROID: "true" # Android
|
|
85
|
-
ENABLE_IOS: "true" # iOS
|
|
83
|
+
# Build target platform settings (only applied when ANALYZE_ONLY=false)
|
|
84
|
+
ENABLE_ANDROID: "true" # enable the Android build (true/false)
|
|
85
|
+
ENABLE_IOS: "true" # enable the iOS build (true/false)
|
|
86
86
|
|
|
87
|
-
#
|
|
88
|
-
# Flutter SDK
|
|
89
|
-
#
|
|
87
|
+
# Project settings
|
|
88
|
+
# Flutter SDK version - leave empty to use the latest stable channel (matches the SDK constraint of a fresh flutter create project).
|
|
89
|
+
# To pin a version, write it like "3.38.9". It must satisfy the environment.sdk range in pubspec.yaml.
|
|
90
90
|
FLUTTER_VERSION: ""
|
|
91
91
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
|
|
92
92
|
XCODE_VERSION: "26.0"
|
|
93
|
-
ENV_FILE_PATH: ".env" #
|
|
93
|
+
ENV_FILE_PATH: ".env" # path where .env is created in dotenv mode (relative to the Flutter root, customizable)
|
|
94
94
|
|
|
95
95
|
concurrency:
|
|
96
96
|
group: flutter-ci-${{ github.ref }}
|
|
97
97
|
cancel-in-progress: true
|
|
98
98
|
|
|
99
99
|
jobs:
|
|
100
|
-
# Job 0:
|
|
100
|
+
# Job 0: change detection (later jobs run only when something under PROJECT_PATH changed)
|
|
101
101
|
changes:
|
|
102
|
-
name:
|
|
102
|
+
name: Detect changes
|
|
103
103
|
runs-on: ubuntu-latest
|
|
104
104
|
permissions:
|
|
105
105
|
contents: read
|
|
@@ -110,22 +110,22 @@ jobs:
|
|
|
110
110
|
- name: Checkout repository
|
|
111
111
|
uses: actions/checkout@v7
|
|
112
112
|
|
|
113
|
-
- name:
|
|
113
|
+
- name: Check whether the project path changed
|
|
114
114
|
id: filter
|
|
115
115
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
116
116
|
uses: dorny/paths-filter@v4
|
|
117
117
|
with:
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
118
|
+
# For a push, compare only the commits in this push (relative to the previous commit, github.event.before).
|
|
119
|
+
# If left empty, the result is based on the cumulative diff against the default branch, so skipping would effectively never happen.
|
|
120
|
+
# The first push of a new branch (before is all zeros) or a PR uses an empty value -> default behavior (compare against the default branch / PR base).
|
|
121
121
|
base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
|
|
122
122
|
filters: |
|
|
123
123
|
project:
|
|
124
124
|
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
125
125
|
|
|
126
|
-
# Job 1:
|
|
126
|
+
# Job 1: preparation
|
|
127
127
|
prepare:
|
|
128
|
-
name: CI
|
|
128
|
+
name: Prepare CI
|
|
129
129
|
needs: changes
|
|
130
130
|
if: ${{ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') }}
|
|
131
131
|
runs-on: ubuntu-latest
|
|
@@ -140,11 +140,23 @@ jobs:
|
|
|
140
140
|
enable_ios: ${{ steps.config.outputs.enable_ios }}
|
|
141
141
|
|
|
142
142
|
steps:
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
- name: Checkout messages
|
|
144
|
+
uses: actions/checkout@v7
|
|
145
|
+
with:
|
|
146
|
+
sparse-checkout: |
|
|
147
|
+
.github/scripts/messages.py
|
|
148
|
+
version.yml
|
|
149
|
+
sparse-checkout-cone-mode: false
|
|
150
|
+
|
|
151
|
+
- name: Load messages
|
|
152
|
+
run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
|
|
153
|
+
|
|
154
|
+
# Pass the settings on as outputs
|
|
155
|
+
- name: Pass settings
|
|
145
156
|
id: config
|
|
146
157
|
run: |
|
|
147
|
-
|
|
158
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
159
|
+
# Check the workflow_dispatch inputs
|
|
148
160
|
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
149
161
|
ANALYZE_ONLY="${{ github.event.inputs.analyze_only }}"
|
|
150
162
|
ENABLE_ANDROID="${{ github.event.inputs.enable_android }}"
|
|
@@ -158,15 +170,16 @@ jobs:
|
|
|
158
170
|
echo "analyze_only=$ANALYZE_ONLY" >> $GITHUB_OUTPUT
|
|
159
171
|
echo "enable_android=$ENABLE_ANDROID" >> $GITHUB_OUTPUT
|
|
160
172
|
echo "enable_ios=$ENABLE_IOS" >> $GITHUB_OUTPUT
|
|
161
|
-
|
|
173
|
+
m flutter_a.ci_config_header
|
|
162
174
|
echo " Analyze Only: $ANALYZE_ONLY"
|
|
163
175
|
echo " Android: $ENABLE_ANDROID"
|
|
164
176
|
echo " iOS: $ENABLE_IOS"
|
|
165
|
-
|
|
177
|
+
m flutter_a.ci_config_trigger event_name="${{ github.event_name }}"
|
|
166
178
|
|
|
167
|
-
- name:
|
|
179
|
+
- name: Collect build info
|
|
168
180
|
id: info
|
|
169
181
|
run: |
|
|
182
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
170
183
|
if [ "${{ github.event_name }}" == "pull_request" ]; then
|
|
171
184
|
BRANCH_NAME="${{ github.head_ref }}"
|
|
172
185
|
else
|
|
@@ -177,17 +190,19 @@ jobs:
|
|
|
177
190
|
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
178
191
|
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
|
|
179
192
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
193
|
+
m flutter_a.ci_info_header
|
|
194
|
+
m flutter_a.ci_info_branch branch_name="$BRANCH_NAME"
|
|
195
|
+
m flutter_a.ci_info_commit commit_hash="$COMMIT_HASH"
|
|
183
196
|
|
|
184
|
-
- name:
|
|
197
|
+
- name: Create progress comment
|
|
185
198
|
id: comment
|
|
186
199
|
if: github.event_name == 'pull_request'
|
|
187
200
|
uses: actions/github-script@v9
|
|
188
201
|
with:
|
|
189
202
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
190
203
|
script: |
|
|
204
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
205
|
+
const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
191
206
|
const prNumber = context.payload.pull_request.number;
|
|
192
207
|
const branchName = '${{ steps.info.outputs.branch_name }}';
|
|
193
208
|
const commitHash = '${{ steps.info.outputs.commit_hash }}';
|
|
@@ -198,47 +213,47 @@ jobs:
|
|
|
198
213
|
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
|
|
199
214
|
const startTime = Date.now();
|
|
200
215
|
|
|
201
|
-
//
|
|
216
|
+
// Decide the header
|
|
202
217
|
const header = analyzeOnly
|
|
203
|
-
? '
|
|
204
|
-
: '
|
|
218
|
+
? m('flutter_a.ci_hdr_analyze_running')
|
|
219
|
+
: m('flutter_a.ci_hdr_running');
|
|
205
220
|
|
|
206
|
-
//
|
|
221
|
+
// Decide the platform status
|
|
207
222
|
let androidStatus, iosStatus;
|
|
208
223
|
if (analyzeOnly) {
|
|
209
|
-
androidStatus = '
|
|
210
|
-
iosStatus = '
|
|
224
|
+
androidStatus = m('flutter_a.ci_status_skipped');
|
|
225
|
+
iosStatus = m('flutter_a.ci_status_skipped');
|
|
211
226
|
} else {
|
|
212
|
-
androidStatus = enableAndroid ? '
|
|
213
|
-
iosStatus = enableIos ? '
|
|
227
|
+
androidStatus = enableAndroid ? m('flutter_a.ci_status_running') : m('flutter_a.ci_status_disabled');
|
|
228
|
+
iosStatus = enableIos ? m('flutter_a.ci_status_running') : m('flutter_a.ci_status_disabled');
|
|
214
229
|
}
|
|
215
230
|
|
|
216
231
|
const bodyParts = [
|
|
217
232
|
header,
|
|
218
233
|
'',
|
|
219
|
-
'
|
|
234
|
+
m('flutter_a.ci_table_head'),
|
|
220
235
|
'|----------|------|----------|',
|
|
221
|
-
'
|
|
222
|
-
'
|
|
223
|
-
|
|
224
|
-
|
|
236
|
+
m('flutter_a.ci_row_analyze_running'),
|
|
237
|
+
m('flutter_a.ci_row_test_running'),
|
|
238
|
+
m('flutter_a.ci_row_android_pending', { status: androidStatus }),
|
|
239
|
+
m('flutter_a.ci_row_ios_pending', { status: iosStatus }),
|
|
225
240
|
'',
|
|
226
|
-
'
|
|
241
|
+
m('flutter_a.ci_info_head'),
|
|
227
242
|
'|------|-----|',
|
|
228
|
-
|
|
229
|
-
|
|
243
|
+
m('flutter_a.ci_row_branch', { branch: branchName }),
|
|
244
|
+
m('flutter_a.ci_row_commit', { commit: commitHash })
|
|
230
245
|
];
|
|
231
246
|
|
|
232
247
|
if (analyzeOnly) {
|
|
233
248
|
bodyParts.push('');
|
|
234
|
-
bodyParts.push('
|
|
249
|
+
bodyParts.push(m('flutter_a.ci_note_analyze_only_running'));
|
|
235
250
|
}
|
|
236
251
|
|
|
237
252
|
bodyParts.push('');
|
|
238
|
-
bodyParts.push(
|
|
253
|
+
bodyParts.push(m('flutter_a.ci_link_live_log', { url: runUrl }));
|
|
239
254
|
bodyParts.push('');
|
|
240
255
|
bodyParts.push('---');
|
|
241
|
-
bodyParts.push('
|
|
256
|
+
bodyParts.push(m('flutter_a.ci_footer_auto_update'));
|
|
242
257
|
|
|
243
258
|
const body = bodyParts.join('\n');
|
|
244
259
|
|
|
@@ -249,13 +264,13 @@ jobs:
|
|
|
249
264
|
body: body
|
|
250
265
|
});
|
|
251
266
|
|
|
252
|
-
console.log(
|
|
267
|
+
console.log(m('flutter_a.ci_log_comment_created', { id: comment.id }));
|
|
253
268
|
core.setOutput('comment_id', comment.id);
|
|
254
269
|
core.setOutput('start_time', startTime);
|
|
255
270
|
|
|
256
|
-
# Job 2:
|
|
271
|
+
# Job 2: code analysis (parallel)
|
|
257
272
|
analyze:
|
|
258
|
-
name:
|
|
273
|
+
name: Code analysis
|
|
259
274
|
runs-on: ubuntu-latest
|
|
260
275
|
needs: [changes, prepare]
|
|
261
276
|
if: ${{ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') }}
|
|
@@ -269,7 +284,7 @@ jobs:
|
|
|
269
284
|
duration: ${{ steps.result.outputs.duration }}
|
|
270
285
|
|
|
271
286
|
steps:
|
|
272
|
-
- name:
|
|
287
|
+
- name: Record analysis start time
|
|
273
288
|
id: analyze_start
|
|
274
289
|
uses: actions/github-script@v9
|
|
275
290
|
with:
|
|
@@ -279,7 +294,10 @@ jobs:
|
|
|
279
294
|
- name: Checkout repository
|
|
280
295
|
uses: actions/checkout@v7
|
|
281
296
|
|
|
282
|
-
|
|
297
|
+
- name: Load messages
|
|
298
|
+
run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
|
|
299
|
+
|
|
300
|
+
# Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
|
|
283
301
|
- name: Prepare env file
|
|
284
302
|
env:
|
|
285
303
|
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
@@ -325,41 +343,45 @@ jobs:
|
|
|
325
343
|
- name: Run Flutter Analyze
|
|
326
344
|
id: analyze
|
|
327
345
|
run: |
|
|
328
|
-
|
|
346
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
347
|
+
m flutter_a.ci_analyze_start
|
|
329
348
|
flutter analyze --no-fatal-infos
|
|
330
|
-
|
|
349
|
+
m flutter_a.ci_analyze_done
|
|
331
350
|
|
|
332
|
-
#
|
|
351
|
+
# Show the test result separately even if analyze fails. A failure fails the job and therefore the CI Gate too.
|
|
333
352
|
- name: Run Flutter Test
|
|
334
353
|
id: test
|
|
335
354
|
if: ${{ !cancelled() && steps.deps.outcome == 'success' }}
|
|
336
355
|
run: |
|
|
337
|
-
|
|
356
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
357
|
+
# flutter test itself fails when test/ has no *_test.dart, so skip it
|
|
338
358
|
if [ -d test ] && [ -n "$(find test -name '*_test.dart' -print -quit)" ]; then
|
|
339
|
-
|
|
359
|
+
m flutter_a.ci_test_start
|
|
340
360
|
flutter test ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
|
|
341
361
|
echo "ran=true" >> "$GITHUB_OUTPUT"
|
|
342
|
-
|
|
362
|
+
m flutter_a.ci_test_passed
|
|
343
363
|
else
|
|
344
|
-
echo "::notice title
|
|
364
|
+
echo "::notice title=$(m flutter_a.ci_test_skipped_title)::$(m flutter_a.ci_test_skipped_body)"
|
|
345
365
|
echo "ran=false" >> "$GITHUB_OUTPUT"
|
|
346
366
|
fi
|
|
347
367
|
|
|
348
|
-
- name:
|
|
368
|
+
- name: Record analysis result
|
|
349
369
|
id: result
|
|
350
370
|
if: always()
|
|
351
371
|
uses: actions/github-script@v9
|
|
352
372
|
with:
|
|
353
373
|
script: |
|
|
374
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
375
|
+
const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
354
376
|
const analyzeStart = parseInt('${{ steps.analyze_start.outputs.time }}');
|
|
355
377
|
const now = Date.now();
|
|
356
378
|
const elapsed = now - analyzeStart;
|
|
357
379
|
const minutes = Math.floor(elapsed / 60000);
|
|
358
380
|
const seconds = Math.floor((elapsed % 60000) / 1000);
|
|
359
|
-
const duration = minutes > 0 ?
|
|
381
|
+
const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
|
|
360
382
|
|
|
361
383
|
const status = '${{ steps.analyze.outcome }}' === 'success' ? 'success' : 'failure';
|
|
362
|
-
// none:
|
|
384
|
+
// none: no test files, skipped: not run because the dependency install failed
|
|
363
385
|
const testOutcome = '${{ steps.test.outcome }}';
|
|
364
386
|
const testStatus = testOutcome === 'success'
|
|
365
387
|
? ('${{ steps.test.outputs.ran }}' === 'true' ? 'success' : 'none')
|
|
@@ -368,11 +390,11 @@ jobs:
|
|
|
368
390
|
core.setOutput('status', status);
|
|
369
391
|
core.setOutput('test_status', testStatus);
|
|
370
392
|
core.setOutput('duration', duration);
|
|
371
|
-
console.log(
|
|
393
|
+
console.log(m('flutter_a.ci_log_analyze_result', { status, test: testStatus, duration }));
|
|
372
394
|
|
|
373
|
-
# Job 3: Android
|
|
395
|
+
# Job 3: Android build (conditional, parallel)
|
|
374
396
|
build-android:
|
|
375
|
-
name: Android
|
|
397
|
+
name: Android build
|
|
376
398
|
if: |
|
|
377
399
|
(github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') &&
|
|
378
400
|
needs.prepare.outputs.analyze_only != 'true' &&
|
|
@@ -388,7 +410,7 @@ jobs:
|
|
|
388
410
|
duration: ${{ steps.result.outputs.duration }}
|
|
389
411
|
|
|
390
412
|
steps:
|
|
391
|
-
- name:
|
|
413
|
+
- name: Record build start time
|
|
392
414
|
id: build_start
|
|
393
415
|
uses: actions/github-script@v9
|
|
394
416
|
with:
|
|
@@ -398,7 +420,10 @@ jobs:
|
|
|
398
420
|
- name: Checkout repository
|
|
399
421
|
uses: actions/checkout@v7
|
|
400
422
|
|
|
401
|
-
|
|
423
|
+
- name: Load messages
|
|
424
|
+
run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
|
|
425
|
+
|
|
426
|
+
# Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
|
|
402
427
|
- name: Prepare env file
|
|
403
428
|
env:
|
|
404
429
|
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
@@ -464,45 +489,49 @@ jobs:
|
|
|
464
489
|
- name: Setup Gradle
|
|
465
490
|
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
|
|
466
491
|
run: |
|
|
467
|
-
|
|
492
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
493
|
+
# The default flutter create .gitignore does not commit gradlew - flutter build creates it when missing
|
|
468
494
|
if [ -f gradlew ]; then
|
|
469
495
|
chmod +x gradlew
|
|
470
496
|
echo "✅ Gradle wrapper permissions set"
|
|
471
497
|
else
|
|
472
|
-
|
|
498
|
+
m flutter_a.gradlew_missing
|
|
473
499
|
fi
|
|
474
500
|
|
|
475
|
-
# APK
|
|
501
|
+
# APK build (CI only verifies the build, so use a debug build that needs no signing)
|
|
476
502
|
- name: Build APK
|
|
477
503
|
id: build
|
|
478
504
|
run: |
|
|
479
|
-
|
|
505
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
506
|
+
m flutter_a.ci_apk_start
|
|
480
507
|
flutter build apk --debug ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
|
|
481
|
-
|
|
508
|
+
m flutter_a.ci_apk_done
|
|
482
509
|
ls -la ./build/app/outputs/flutter-apk/ || true
|
|
483
510
|
|
|
484
|
-
- name:
|
|
511
|
+
- name: Record build result
|
|
485
512
|
id: result
|
|
486
513
|
if: always()
|
|
487
514
|
uses: actions/github-script@v9
|
|
488
515
|
with:
|
|
489
516
|
script: |
|
|
517
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
518
|
+
const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
490
519
|
const buildStart = parseInt('${{ steps.build_start.outputs.time }}');
|
|
491
520
|
const now = Date.now();
|
|
492
521
|
const elapsed = now - buildStart;
|
|
493
522
|
const minutes = Math.floor(elapsed / 60000);
|
|
494
523
|
const seconds = Math.floor((elapsed % 60000) / 1000);
|
|
495
|
-
const duration = minutes > 0 ?
|
|
524
|
+
const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
|
|
496
525
|
|
|
497
526
|
const status = '${{ steps.build.outcome }}' === 'success' ? 'success' : 'failure';
|
|
498
527
|
|
|
499
528
|
core.setOutput('status', status);
|
|
500
529
|
core.setOutput('duration', duration);
|
|
501
|
-
console.log(
|
|
530
|
+
console.log(m('flutter_a.ci_log_android_result', { status, duration }));
|
|
502
531
|
|
|
503
|
-
# Job 4: iOS
|
|
532
|
+
# Job 4: iOS build (conditional, parallel)
|
|
504
533
|
build-ios:
|
|
505
|
-
name: iOS
|
|
534
|
+
name: iOS build
|
|
506
535
|
if: |
|
|
507
536
|
(github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') &&
|
|
508
537
|
needs.prepare.outputs.analyze_only != 'true' &&
|
|
@@ -518,7 +547,7 @@ jobs:
|
|
|
518
547
|
duration: ${{ steps.result.outputs.duration }}
|
|
519
548
|
|
|
520
549
|
steps:
|
|
521
|
-
- name:
|
|
550
|
+
- name: Record build start time
|
|
522
551
|
id: build_start
|
|
523
552
|
uses: actions/github-script@v9
|
|
524
553
|
with:
|
|
@@ -528,12 +557,15 @@ jobs:
|
|
|
528
557
|
- name: Checkout repository
|
|
529
558
|
uses: actions/checkout@v7
|
|
530
559
|
|
|
560
|
+
- name: Load messages
|
|
561
|
+
run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
|
|
562
|
+
|
|
531
563
|
- name: Select Xcode version
|
|
532
564
|
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
533
565
|
|
|
534
566
|
- name: Install iOS device platform
|
|
535
567
|
env:
|
|
536
|
-
# xcode-select
|
|
568
|
+
# xcode-select alone does not propagate to subprocesses, so set it explicitly
|
|
537
569
|
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
538
570
|
run: |
|
|
539
571
|
echo "=== iPhoneOS.platform check ==="
|
|
@@ -548,7 +580,7 @@ jobs:
|
|
|
548
580
|
echo "=== Downloading iOS platform ==="
|
|
549
581
|
xcodebuild -downloadPlatform iOS
|
|
550
582
|
|
|
551
|
-
#
|
|
583
|
+
# Prepare env vars - a .env file when ENV_MODE=dotenv, a temp file outside the project (--dart-define-from-file) for dart-define
|
|
552
584
|
- name: Prepare env file
|
|
553
585
|
env:
|
|
554
586
|
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
@@ -598,54 +630,71 @@ jobs:
|
|
|
598
630
|
|
|
599
631
|
- name: Install CocoaPods
|
|
600
632
|
run: |
|
|
601
|
-
|
|
633
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
634
|
+
# A default project without plugins has no Podfile - once plugins are added, flutter build creates it
|
|
602
635
|
if [ -f ios/Podfile ]; then
|
|
603
636
|
gem install cocoapods
|
|
604
637
|
(cd ios && (pod install --repo-update || pod install))
|
|
605
638
|
echo "✅ CocoaPods installed"
|
|
606
639
|
else
|
|
607
|
-
|
|
640
|
+
m flutter_a.ci_podfile_missing
|
|
608
641
|
fi
|
|
609
642
|
|
|
610
643
|
- name: Build iOS
|
|
611
644
|
id: build
|
|
612
645
|
run: |
|
|
613
|
-
|
|
646
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
647
|
+
m flutter_a.ci_ios_start
|
|
614
648
|
flutter build ios --release --no-codesign ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
|
|
615
|
-
|
|
649
|
+
m flutter_a.ci_ios_done
|
|
616
650
|
|
|
617
|
-
- name:
|
|
651
|
+
- name: Record build result
|
|
618
652
|
id: result
|
|
619
653
|
if: always()
|
|
620
654
|
uses: actions/github-script@v9
|
|
621
655
|
with:
|
|
622
656
|
script: |
|
|
657
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
658
|
+
const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
623
659
|
const buildStart = parseInt('${{ steps.build_start.outputs.time }}');
|
|
624
660
|
const now = Date.now();
|
|
625
661
|
const elapsed = now - buildStart;
|
|
626
662
|
const minutes = Math.floor(elapsed / 60000);
|
|
627
663
|
const seconds = Math.floor((elapsed % 60000) / 1000);
|
|
628
|
-
const duration = minutes > 0 ?
|
|
664
|
+
const duration = minutes > 0 ? m('flutter_a.ci_dur_min_sec', { minutes, seconds }) : m('flutter_a.ci_dur_sec', { seconds });
|
|
629
665
|
|
|
630
666
|
const status = '${{ steps.build.outcome }}' === 'success' ? 'success' : 'failure';
|
|
631
667
|
|
|
632
668
|
core.setOutput('status', status);
|
|
633
669
|
core.setOutput('duration', duration);
|
|
634
|
-
console.log(
|
|
670
|
+
console.log(m('flutter_a.ci_log_ios_result', { status, duration }));
|
|
635
671
|
|
|
636
|
-
# Job 5:
|
|
672
|
+
# Job 5: result report
|
|
637
673
|
report:
|
|
638
|
-
name:
|
|
674
|
+
name: Report results
|
|
639
675
|
if: always() && github.event_name == 'pull_request' && needs.changes.outputs.project == 'true'
|
|
640
676
|
runs-on: ubuntu-latest
|
|
641
677
|
needs: [changes, prepare, analyze, build-android, build-ios]
|
|
642
678
|
|
|
643
679
|
steps:
|
|
644
|
-
- name:
|
|
680
|
+
- name: Checkout messages
|
|
681
|
+
uses: actions/checkout@v7
|
|
682
|
+
with:
|
|
683
|
+
sparse-checkout: |
|
|
684
|
+
.github/scripts/messages.py
|
|
685
|
+
version.yml
|
|
686
|
+
sparse-checkout-cone-mode: false
|
|
687
|
+
|
|
688
|
+
- name: Load messages
|
|
689
|
+
run: echo "PAW_MSG=$(cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py dump flutter_a.ci_)" >> "$GITHUB_ENV"
|
|
690
|
+
|
|
691
|
+
- name: Update final result comment
|
|
645
692
|
uses: actions/github-script@v9
|
|
646
693
|
with:
|
|
647
694
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
648
695
|
script: |
|
|
696
|
+
const M = JSON.parse(process.env.PAW_MSG);
|
|
697
|
+
const m = (k, p = {}) => M[k].replace(/\{(\w+)\}/g, (s, n) => (n in p ? p[n] : s));
|
|
649
698
|
const commentId = parseInt('${{ needs.prepare.outputs.comment_id }}');
|
|
650
699
|
const startTime = parseInt('${{ needs.prepare.outputs.start_time }}');
|
|
651
700
|
const branchName = '${{ needs.prepare.outputs.branch_name }}';
|
|
@@ -656,7 +705,7 @@ jobs:
|
|
|
656
705
|
const runId = '${{ github.run_id }}';
|
|
657
706
|
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
|
|
658
707
|
|
|
659
|
-
//
|
|
708
|
+
// Collect the results
|
|
660
709
|
const analyzeStatus = '${{ needs.analyze.outputs.status }}';
|
|
661
710
|
const analyzeDuration = '${{ needs.analyze.outputs.duration }}' || '-';
|
|
662
711
|
const testStatus = '${{ needs.analyze.outputs.test_status }}';
|
|
@@ -665,137 +714,137 @@ jobs:
|
|
|
665
714
|
const iosStatus = '${{ needs.build-ios.outputs.status }}';
|
|
666
715
|
const iosDuration = '${{ needs.build-ios.outputs.duration }}' || '-';
|
|
667
716
|
|
|
668
|
-
//
|
|
717
|
+
// Compute the total duration
|
|
669
718
|
const now = Date.now();
|
|
670
719
|
const totalElapsed = now - startTime;
|
|
671
720
|
const totalMin = Math.floor(totalElapsed / 60000);
|
|
672
721
|
const totalSec = Math.floor((totalElapsed % 60000) / 1000);
|
|
673
|
-
const totalDuration = totalMin > 0 ?
|
|
722
|
+
const totalDuration = totalMin > 0 ? m('flutter_a.ci_dur_min_sec', { minutes: totalMin, seconds: totalSec }) : m('flutter_a.ci_dur_sec', { seconds: totalSec });
|
|
674
723
|
|
|
675
|
-
//
|
|
724
|
+
// Decide the status
|
|
676
725
|
let overallSuccess = true;
|
|
677
726
|
let analyzeDisplay, androidDisplay, iosDisplay;
|
|
678
727
|
|
|
679
|
-
// Analyze
|
|
728
|
+
// Analyze status
|
|
680
729
|
if (analyzeStatus === 'success') {
|
|
681
|
-
analyzeDisplay = '
|
|
730
|
+
analyzeDisplay = m('flutter_a.ci_status_success');
|
|
682
731
|
} else {
|
|
683
|
-
analyzeDisplay = '
|
|
732
|
+
analyzeDisplay = m('flutter_a.ci_status_failure');
|
|
684
733
|
overallSuccess = false;
|
|
685
734
|
}
|
|
686
735
|
|
|
687
|
-
// Test
|
|
736
|
+
// Test status (no test files is not treated as a failure)
|
|
688
737
|
let testDisplay;
|
|
689
738
|
if (testStatus === 'success') {
|
|
690
|
-
testDisplay = '
|
|
739
|
+
testDisplay = m('flutter_a.ci_status_success');
|
|
691
740
|
} else if (testStatus === 'none') {
|
|
692
|
-
testDisplay = '
|
|
741
|
+
testDisplay = m('flutter_a.ci_status_no_tests');
|
|
693
742
|
} else {
|
|
694
|
-
testDisplay = testStatus === 'failure' ? '
|
|
743
|
+
testDisplay = testStatus === 'failure' ? m('flutter_a.ci_status_failure') : m('flutter_a.ci_status_not_run');
|
|
695
744
|
overallSuccess = false;
|
|
696
745
|
}
|
|
697
746
|
|
|
698
|
-
// Android
|
|
747
|
+
// Android status
|
|
699
748
|
if (analyzeOnly) {
|
|
700
|
-
androidDisplay = '
|
|
749
|
+
androidDisplay = m('flutter_a.ci_status_skipped');
|
|
701
750
|
} else if (!enableAndroid) {
|
|
702
|
-
androidDisplay = '
|
|
751
|
+
androidDisplay = m('flutter_a.ci_status_disabled');
|
|
703
752
|
} else if (androidStatus === 'success') {
|
|
704
|
-
androidDisplay = '
|
|
753
|
+
androidDisplay = m('flutter_a.ci_status_success');
|
|
705
754
|
} else {
|
|
706
|
-
androidDisplay = '
|
|
755
|
+
androidDisplay = m('flutter_a.ci_status_failure');
|
|
707
756
|
overallSuccess = false;
|
|
708
757
|
}
|
|
709
758
|
|
|
710
|
-
// iOS
|
|
759
|
+
// iOS status
|
|
711
760
|
if (analyzeOnly) {
|
|
712
|
-
iosDisplay = '
|
|
761
|
+
iosDisplay = m('flutter_a.ci_status_skipped');
|
|
713
762
|
} else if (!enableIos) {
|
|
714
|
-
iosDisplay = '
|
|
763
|
+
iosDisplay = m('flutter_a.ci_status_disabled');
|
|
715
764
|
} else if (iosStatus === 'success') {
|
|
716
|
-
iosDisplay = '
|
|
765
|
+
iosDisplay = m('flutter_a.ci_status_success');
|
|
717
766
|
} else {
|
|
718
|
-
iosDisplay = '
|
|
767
|
+
iosDisplay = m('flutter_a.ci_status_failure');
|
|
719
768
|
overallSuccess = false;
|
|
720
769
|
}
|
|
721
770
|
|
|
722
|
-
//
|
|
771
|
+
// Decide the header
|
|
723
772
|
let header;
|
|
724
773
|
if (analyzeOnly) {
|
|
725
774
|
header = overallSuccess
|
|
726
|
-
? '
|
|
727
|
-
: '
|
|
775
|
+
? m('flutter_a.ci_hdr_analyze_success')
|
|
776
|
+
: m('flutter_a.ci_hdr_analyze_failure');
|
|
728
777
|
} else {
|
|
729
778
|
header = overallSuccess
|
|
730
|
-
? '
|
|
731
|
-
: '
|
|
779
|
+
? m('flutter_a.ci_hdr_success')
|
|
780
|
+
: m('flutter_a.ci_hdr_failure');
|
|
732
781
|
}
|
|
733
782
|
|
|
734
|
-
//
|
|
783
|
+
// Build the comment body
|
|
735
784
|
const bodyParts = [
|
|
736
785
|
header,
|
|
737
786
|
'',
|
|
738
|
-
'
|
|
787
|
+
m('flutter_a.ci_table_head'),
|
|
739
788
|
'|----------|------|----------|',
|
|
740
789
|
`| 🔍 Analyze | ${analyzeDisplay} | ${analyzeDuration} |`,
|
|
741
790
|
`| 🧪 Test | ${testDisplay} | - |`,
|
|
742
|
-
|
|
743
|
-
|
|
791
|
+
m('flutter_a.ci_row_android_result', { status: androidDisplay, duration: analyzeOnly || !enableAndroid ? '-' : androidDuration }),
|
|
792
|
+
m('flutter_a.ci_row_ios_result', { status: iosDisplay, duration: analyzeOnly || !enableIos ? '-' : iosDuration }),
|
|
744
793
|
'',
|
|
745
|
-
'
|
|
794
|
+
m('flutter_a.ci_info_head'),
|
|
746
795
|
'|------|-----|',
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
796
|
+
m('flutter_a.ci_row_branch', { branch: branchName }),
|
|
797
|
+
m('flutter_a.ci_row_commit', { commit: commitHash }),
|
|
798
|
+
m('flutter_a.ci_row_total', { duration: totalDuration }),
|
|
750
799
|
''
|
|
751
800
|
];
|
|
752
801
|
|
|
753
|
-
// Analyze Only
|
|
802
|
+
// Analyze Only mode note
|
|
754
803
|
if (analyzeOnly && overallSuccess) {
|
|
755
|
-
bodyParts.push('
|
|
804
|
+
bodyParts.push(m('flutter_a.ci_note_analyze_only_done'));
|
|
756
805
|
bodyParts.push('');
|
|
757
806
|
}
|
|
758
807
|
|
|
759
|
-
//
|
|
808
|
+
// Extra guidance on failure
|
|
760
809
|
if (!overallSuccess) {
|
|
761
810
|
bodyParts.push(
|
|
762
|
-
'
|
|
811
|
+
m('flutter_a.ci_hdr_checks'),
|
|
763
812
|
''
|
|
764
813
|
);
|
|
765
814
|
|
|
766
815
|
if (analyzeStatus !== 'success') {
|
|
767
|
-
bodyParts.push('
|
|
768
|
-
bodyParts.push('
|
|
769
|
-
bodyParts.push('
|
|
816
|
+
bodyParts.push(m('flutter_a.ci_analyze_fail_title'));
|
|
817
|
+
bodyParts.push(m('flutter_a.ci_analyze_fail_lint'));
|
|
818
|
+
bodyParts.push(m('flutter_a.ci_analyze_fail_fix'));
|
|
770
819
|
bodyParts.push('');
|
|
771
820
|
}
|
|
772
821
|
|
|
773
822
|
if (testStatus === 'failure') {
|
|
774
|
-
bodyParts.push('
|
|
775
|
-
bodyParts.push('
|
|
823
|
+
bodyParts.push(m('flutter_a.ci_test_fail_title'));
|
|
824
|
+
bodyParts.push(m('flutter_a.ci_test_fail_local'));
|
|
776
825
|
bodyParts.push('');
|
|
777
826
|
}
|
|
778
827
|
|
|
779
828
|
if (!analyzeOnly && enableAndroid && androidStatus !== 'success') {
|
|
780
|
-
bodyParts.push('
|
|
781
|
-
bodyParts.push('
|
|
782
|
-
bodyParts.push('
|
|
829
|
+
bodyParts.push(m('flutter_a.ci_android_fail_title'));
|
|
830
|
+
bodyParts.push(m('flutter_a.ci_android_fail_gradle'));
|
|
831
|
+
bodyParts.push(m('flutter_a.ci_android_fail_deps'));
|
|
783
832
|
bodyParts.push('');
|
|
784
833
|
}
|
|
785
834
|
|
|
786
835
|
if (!analyzeOnly && enableIos && iosStatus !== 'success') {
|
|
787
|
-
bodyParts.push('
|
|
788
|
-
bodyParts.push('
|
|
789
|
-
bodyParts.push('
|
|
836
|
+
bodyParts.push(m('flutter_a.ci_ios_fail_title'));
|
|
837
|
+
bodyParts.push(m('flutter_a.ci_ios_fail_xcode'));
|
|
838
|
+
bodyParts.push(m('flutter_a.ci_ios_fail_pods'));
|
|
790
839
|
bodyParts.push('');
|
|
791
840
|
}
|
|
792
841
|
}
|
|
793
842
|
|
|
794
|
-
bodyParts.push(
|
|
843
|
+
bodyParts.push(m('flutter_a.ci_link_workflow_log', { url: runUrl }));
|
|
795
844
|
|
|
796
845
|
const body = bodyParts.join('\n');
|
|
797
846
|
|
|
798
|
-
//
|
|
847
|
+
// Update the comment
|
|
799
848
|
if (commentId) {
|
|
800
849
|
await github.rest.issues.updateComment({
|
|
801
850
|
owner: context.repo.owner,
|
|
@@ -803,25 +852,34 @@ jobs:
|
|
|
803
852
|
comment_id: commentId,
|
|
804
853
|
body: body
|
|
805
854
|
});
|
|
806
|
-
console.log(
|
|
855
|
+
console.log(m('flutter_a.ci_log_result_updated'));
|
|
807
856
|
} else {
|
|
808
|
-
console.log('
|
|
857
|
+
console.log(m('flutter_a.ci_log_no_comment_id'));
|
|
809
858
|
}
|
|
810
859
|
|
|
811
|
-
# Job 6: CI Gate (
|
|
812
|
-
#
|
|
860
|
+
# Job 6: CI Gate (the single entry point to register as a required check)
|
|
861
|
+
# Skipped jobs count as passed; any failure/cancelled result fails the gate.
|
|
813
862
|
ci-gate:
|
|
814
863
|
name: CI Gate
|
|
815
864
|
if: ${{ always() }}
|
|
816
865
|
needs: [changes, prepare, analyze, build-android, build-ios, report]
|
|
817
866
|
runs-on: ubuntu-latest
|
|
818
867
|
steps:
|
|
819
|
-
- name:
|
|
868
|
+
- name: Checkout messages
|
|
869
|
+
uses: actions/checkout@v7
|
|
870
|
+
with:
|
|
871
|
+
sparse-checkout: |
|
|
872
|
+
.github/scripts/messages.py
|
|
873
|
+
version.yml
|
|
874
|
+
sparse-checkout-cone-mode: false
|
|
875
|
+
|
|
876
|
+
- name: Aggregate results
|
|
820
877
|
env:
|
|
821
878
|
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
822
879
|
run: |
|
|
823
|
-
|
|
880
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
881
|
+
m flutter_a.ci_gate_results results="$RESULTS"
|
|
824
882
|
if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
|
|
825
|
-
|
|
883
|
+
m flutter_a.ci_gate_failed
|
|
826
884
|
exit 1
|
|
827
885
|
fi
|