Skip to content

Commit 38d122b

Browse files
runway-github[bot]sleepytanyachloeYue
authored
chore(runway): cherry-pick test: add automated build environment display to RC and production builds. (#29731)
- test: add automated build environment display to RC and production builds. (#29390) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Adds automated build environment display to RC and production builds. Captures actual environment values used during builds (METAMASK_ENVIRONMENT, METAMASK_BUILD_TYPE, API URLs, etc.) and displays them: - RC builds: In PR comment alongside build links - Prod builds: Extracted via new workflow (Slack notification in follow-up PR). **Changes:** - scripts/apply-build-config.js: Add --write-build-env flag to generate build-env.json with actual env values - build.yml: Generate and upload build-env.json as artifact during builds (used by both RC and prod) - build-rc-auto.yml: Download build-env artifacts in post-rc-build-comment job - scripts/build-announce/: Add env section to RC build PR comments - prod-build-env-notify.yml: New workflow triggered on prod build completion, extracts env values (Slack posting in follow-up PR) <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/MMQA-1108 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes CI build workflows and the RC PR-comment pipeline to generate, upload, download, and parse a new `build-env.json` artifact; misconfiguration could break build jobs or PR comment posting. Risk is limited to CI/reporting paths and is largely non-blocking (`continue-on-error`/`|| true`) but still touches release build automation. > > **Overview** > Adds generation of a `build-env.json` file during CI builds (via `scripts/apply-build-config.js --write-build-env`) and uploads it as a build artifact for each platform. > > Updates RC automation to download these artifacts and extends `scripts/build-announce` to parse them and include a **Build Environment** section (env/build type, remote FF mapping, key API URLs/flags) in the RC PR comment, with a fallback “not available” section on extraction failure. > > Introduces a new `Prod Build Env Notify` workflow that triggers on successful production workflow runs, downloads the `build-env` artifact, extracts key values with `jq`, and currently prints a placeholder for a future Slack notification; also ignores local `build-env.json` via `.gitignore`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0f0c4b5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [862bf76](862bf76) Co-authored-by: sleepytanya <104780023+sleepytanya@users.noreply.github.com> Co-authored-by: chloeYue <105063779+chloeYue@users.noreply.github.com>
1 parent 8676634 commit 38d122b

9 files changed

Lines changed: 487 additions & 5 deletions

File tree

.github/workflows/build-rc-auto.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ jobs:
151151
cache: yarn
152152
- name: Install dependencies
153153
run: yarn install --immutable
154+
155+
- name: Download build environment artifacts
156+
continue-on-error: true
157+
uses: actions/download-artifact@v4
158+
with:
159+
pattern: build-env-main-rc-*
160+
path: build-env-artifacts
161+
merge-multiple: false
162+
154163
- name: Post RC Build Comment with Test Plan
155164
run: node -r esbuild-register scripts/build-announce/index.ts
156165
timeout-minutes: 8

.github/workflows/build.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ jobs:
305305
eval "$(node scripts/apply-build-config.js ${{ inputs.build_name }} --export)"
306306
# Persist to GITHUB_ENV so later steps (e.g. Build) see CONFIGURATION, IS_SIM_BUILD, etc.
307307
node scripts/apply-build-config.js ${{ inputs.build_name }} --export-github-env >> "$GITHUB_ENV"
308+
# Generate build-env.json capturing actual env values used in this build (non-critical)
309+
node scripts/apply-build-config.js ${{ inputs.build_name }} --write-build-env || true
308310
309311
- name: Validate secrets
310312
env:
@@ -497,6 +499,14 @@ jobs:
497499
path: ${{ steps.rename.outputs.android_sourcemap_dir }}
498500
if-no-files-found: warn
499501

502+
- name: Upload build environment JSON
503+
if: success()
504+
uses: actions/upload-artifact@v4
505+
with:
506+
name: build-env-${{ inputs.build_name }}-${{ matrix.platform }}
507+
path: build-env.json
508+
if-no-files-found: warn
509+
500510
# Single fan-in job so workflow_call outputs work with matrix `build` (matrix jobs cannot feed workflow outputs directly).
501511
emit-build-metadata:
502512
name: Emit build metadata
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
##############################################################################################
2+
#
3+
# Production Build Environment Notification
4+
#
5+
# Automatically triggers when production builds complete.
6+
# Downloads build-env.json artifact and extracts environment values.
7+
# TODO: Post to Slack (follow-up PR)
8+
#
9+
##############################################################################################
10+
name: Prod Build Env Notify
11+
12+
on:
13+
workflow_run:
14+
workflows: ["Runway iOS Production", "Runway Android Production"]
15+
types: [completed]
16+
17+
jobs:
18+
notify-env:
19+
name: Post Environment Info
20+
runs-on: ubuntu-latest
21+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- uses: actions/setup-node@v6
26+
with:
27+
node-version-file: '.nvmrc'
28+
29+
- name: Download build-env artifact from triggering workflow
30+
continue-on-error: true
31+
id: download-artifact
32+
uses: actions/download-artifact@v6
33+
with:
34+
name: build-env-main-prod-${{ github.event.workflow_run.name == 'Runway iOS Production' && 'ios' || 'android' }}
35+
path: build-env-artifacts
36+
run-id: ${{ github.event.workflow_run.id }}
37+
github-token: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Extract and display environment values
40+
if: steps.download-artifact.outcome == 'success'
41+
id: extract-env
42+
run: |
43+
JSON_FILE="build-env-artifacts/build-env.json"
44+
45+
if [ ! -f "$JSON_FILE" ]; then
46+
echo "ERROR: build-env.json not found"
47+
exit 1
48+
fi
49+
50+
echo "=== Build Environment Values ==="
51+
cat "$JSON_FILE"
52+
echo ""
53+
54+
# Extract values
55+
BUILD_NAME=$(jq -r '.buildName' "$JSON_FILE")
56+
METAMASK_ENV=$(jq -r '.env.METAMASK_ENVIRONMENT' "$JSON_FILE")
57+
BUILD_TYPE=$(jq -r '.env.METAMASK_BUILD_TYPE' "$JSON_FILE")
58+
REWARDS_URL=$(jq -r '.env.REWARDS_API_URL' "$JSON_FILE")
59+
PORTFOLIO_URL=$(jq -r '.env.MM_PORTFOLIO_URL' "$JSON_FILE")
60+
RAMPS_ENV=$(jq -r '.env.RAMPS_ENVIRONMENT' "$JSON_FILE")
61+
62+
# Map to Remote Feature Flag values
63+
case "$METAMASK_ENV" in
64+
production) REMOTE_FF_ENV="prod" ;;
65+
rc) REMOTE_FF_ENV="rc" ;;
66+
beta) REMOTE_FF_ENV="beta" ;;
67+
test|e2e) REMOTE_FF_ENV="test" ;;
68+
exp) REMOTE_FF_ENV="exp" ;;
69+
*) REMOTE_FF_ENV="dev" ;;
70+
esac
71+
72+
case "$BUILD_TYPE" in
73+
flask) REMOTE_FF_DIST="flask" ;;
74+
*) REMOTE_FF_DIST="main" ;;
75+
esac
76+
77+
# Output for next steps
78+
{
79+
echo "build_name=$BUILD_NAME"
80+
echo "environment=$METAMASK_ENV"
81+
echo "build_type=$BUILD_TYPE"
82+
echo "remote_ff_env=$REMOTE_FF_ENV"
83+
echo "remote_ff_dist=$REMOTE_FF_DIST"
84+
echo "rewards_url=$REWARDS_URL"
85+
echo "portfolio_url=$PORTFOLIO_URL"
86+
echo "ramps_env=$RAMPS_ENV"
87+
} >> "$GITHUB_OUTPUT"
88+
89+
echo ""
90+
echo "=== Extracted Values ==="
91+
echo "Environment: $METAMASK_ENV"
92+
echo "Build Type: $BUILD_TYPE"
93+
echo "Remote Feature Flag Env: $REMOTE_FF_ENV"
94+
echo "Remote Feature Flag Distribution: $REMOTE_FF_DIST"
95+
echo "Rewards API URL: $REWARDS_URL"
96+
echo "MM_PORTFOLIO_URL: $PORTFOLIO_URL"
97+
echo "Ramps Environment: $RAMPS_ENV"
98+
99+
- name: Post to Slack
100+
if: steps.download-artifact.outcome == 'success'
101+
env:
102+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
103+
PLATFORM: ${{ github.event.workflow_run.name == 'Runway iOS Production' && 'iOS' || 'Android' }}
104+
ENVIRONMENT: ${{ steps.extract-env.outputs.environment }}
105+
BUILD_TYPE: ${{ steps.extract-env.outputs.build_type }}
106+
REMOTE_FF_ENV: ${{ steps.extract-env.outputs.remote_ff_env }}
107+
REMOTE_FF_DIST: ${{ steps.extract-env.outputs.remote_ff_dist }}
108+
REWARDS_URL: ${{ steps.extract-env.outputs.rewards_url }}
109+
PORTFOLIO_URL: ${{ steps.extract-env.outputs.portfolio_url }}
110+
RAMPS_ENV: ${{ steps.extract-env.outputs.ramps_env }}
111+
WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
112+
run: |
113+
echo "TODO: Post to Slack"
114+
echo "Platform: $PLATFORM"
115+
echo "Environment: $ENVIRONMENT"
116+
echo "Build Type: $BUILD_TYPE"
117+
echo "Remote FF Env: $REMOTE_FF_ENV"
118+
echo "Remote FF Distribution: $REMOTE_FF_DIST"
119+
echo "Rewards URL: $REWARDS_URL"
120+
echo "Portfolio URL: $PORTFOLIO_URL"
121+
echo "Ramps Environment: $RAMPS_ENV"
122+
echo "Workflow URL: $WORKFLOW_URL"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,6 @@ runway-artifacts/
196196
release-test-plan.json
197197
release-delta.json
198198
release-signoffs.json
199+
200+
# Build environment validation
201+
build-env.json

scripts/apply-build-config.js

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const path = require('path');
1818
const yaml = require('js-yaml');
1919

2020
const BUILDS_PATH = path.join(__dirname, '../builds.yml');
21+
const BUILD_ENV_PATH = path.join(__dirname, '../build-env.json');
2122

2223
function loadConfig(buildName) {
2324
if (!fs.existsSync(BUILDS_PATH)) {
@@ -106,16 +107,68 @@ function exportForGitHubEnv(buildName) {
106107
return lines.join('\n');
107108
}
108109

110+
/**
111+
* Write build environment to JSON file for display in PR comments.
112+
* This captures the actual env values used during build time.
113+
*/
114+
function writeBuildEnvJson(buildName) {
115+
const config = loadConfig(buildName);
116+
117+
const envVarsToCapture = [
118+
'METAMASK_ENVIRONMENT',
119+
'METAMASK_BUILD_TYPE',
120+
'REWARDS_API_URL',
121+
'MM_PORTFOLIO_URL',
122+
'RAMPS_ENVIRONMENT',
123+
'IS_TEST',
124+
// Additional env vars for full environment info
125+
'PORTFOLIO_API_URL',
126+
'SECURITY_ALERTS_API_URL',
127+
'DECODING_API_URL',
128+
'AUTH_SERVICE_URL',
129+
'DIGEST_API_URL',
130+
'SOCIAL_API_URL',
131+
'BAANX_API_URL',
132+
'RAMP_DEV_BUILD',
133+
'BRIDGE_USE_DEV_APIS',
134+
'RAMP_INTERNAL_BUILD',
135+
];
136+
137+
const buildEnv = {
138+
buildName,
139+
buildTime: new Date().toISOString(),
140+
env: {},
141+
};
142+
143+
if (config.env) {
144+
envVarsToCapture.forEach((key) => {
145+
if (config.env[key] !== undefined) {
146+
buildEnv.env[key] = String(config.env[key]);
147+
}
148+
});
149+
}
150+
151+
if (config.code_fencing) {
152+
buildEnv.codeFencing = config.code_fencing;
153+
}
154+
155+
fs.writeFileSync(BUILD_ENV_PATH, JSON.stringify(buildEnv, null, 2));
156+
console.log(`Wrote build environment to ${BUILD_ENV_PATH}`);
157+
158+
return buildEnv;
159+
}
160+
109161
// CLI
110162
if (require.main === module) {
111163
const args = process.argv.slice(2);
112164
const buildName = args.find((a) => !a.startsWith('--'));
113165
const exportMode = args.includes('--export');
114166
const exportGitHubEnvMode = args.includes('--export-github-env');
167+
const writeBuildEnvMode = args.includes('--write-build-env');
115168

116169
if (!buildName) {
117170
console.error(
118-
'Usage: node apply-build-config.js <build-name> [--export | --export-github-env]',
171+
'Usage: node apply-build-config.js <build-name> [--export | --export-github-env | --write-build-env]',
119172
);
120173
console.error('Example: node apply-build-config.js main-prod');
121174
process.exit(1);
@@ -126,12 +179,14 @@ if (require.main === module) {
126179
console.log(exportForGitHubEnv(buildName));
127180
} else if (exportMode) {
128181
console.log(exportForShell(buildName));
182+
} else if (writeBuildEnvMode) {
183+
writeBuildEnvJson(buildName);
129184
} else {
130185
applyConfig(buildName);
131-
console.log(`Applied config for ${buildName}`);
186+
console.log(`Applied config for ${buildName}`);
132187
}
133188
} catch (error) {
134-
console.error(` ${error.message}`);
189+
console.error(`Error: ${error.message}`);
135190
process.exit(1);
136191
}
137192
}
@@ -141,4 +196,5 @@ module.exports = {
141196
applyConfig,
142197
exportForShell,
143198
exportForGitHubEnv,
199+
writeBuildEnvJson,
144200
};

0 commit comments

Comments
 (0)