Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build-rc-auto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ needs.validate-and-find-pr.outputs.branch-name }}
fetch-depth: 50
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
Expand Down Expand Up @@ -199,4 +199,5 @@ jobs:
semver: ${{ needs.trigger-ios-rc-build.outputs.semantic_version }}
ios_build_number: ${{ needs.trigger-ios-rc-build.outputs.ios_version_code }}
android_build_number: ${{ needs.trigger-android-rc-build.outputs.android_version_code }}
pr_number: ${{ needs.validate-and-find-pr.outputs.pr-number }}
secrets: inherit
2 changes: 0 additions & 2 deletions .github/workflows/prod-build-env-notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ jobs:
production) REMOTE_FF_ENV="prod" ;;
rc) REMOTE_FF_ENV="rc" ;;
beta) REMOTE_FF_ENV="beta" ;;
test|e2e) REMOTE_FF_ENV="test" ;;
exp) REMOTE_FF_ENV="exp" ;;
*) REMOTE_FF_ENV="dev" ;;
esac

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/slack-rc-notification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ on:
required: false
type: string
default: ''
pr_number:
description: 'PR number for linking to cherry-picks section in release PR comment'
required: false
type: string
default: ''

jobs:
slack-notification:
Expand Down Expand Up @@ -65,3 +70,4 @@ jobs:
BUILD_PIPELINE_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ANDROID_PUBLIC_URL: ${{ secrets.ANDROID_PUBLIC_BUCKET_URL }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
PR_NUMBER: ${{ inputs.pr_number }}
105 changes: 105 additions & 0 deletions scripts/build-announce/cherry-picks-section.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Extracts commits from release branch and builds collapsible markdown section.
*/

import { execFileSync } from 'child_process';

const REPO_URL = process.env.GITHUB_REPOSITORY
? `https://github.com/${process.env.GITHUB_REPOSITORY}`
: 'https://github.com/MetaMask/metamask-mobile';

const SKIP_CI_BUMP_VERSION_SUBJECT = /^\[skip ci\] Bump version number to/;

function getMergeBase(headRef: string, baseRef: string): string {
const out = execFileSync('git', ['merge-base', headRef, baseRef], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
const sha = out.trim();
if (!sha) {
throw new Error(`git merge-base returned empty for ${headRef} and ${baseRef}`);
}
return sha;
}

function getAncestryPathCommits(
mergeBaseHash: string,
headRef: string,
): { hash: string; subject: string }[] {
const out = execFileSync(
'git',
['log', '--ancestry-path', `${mergeBaseHash}..${headRef}`, '--pretty=format:%h\t%s'],
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
);

if (!out.trim()) return [];

return out
.trim()
.split('\n')
.filter((line) => {
const tab = line.indexOf('\t');
const subject = tab >= 0 ? line.slice(tab + 1) : line;
return !SKIP_CI_BUMP_VERSION_SUBJECT.test(subject.trim());
})
.map((line) => {
const tab = line.indexOf('\t');
return {
hash: tab >= 0 ? line.slice(0, tab) : '',
subject: tab >= 0 ? line.slice(tab + 1) : line,
};
});
}

function formatSubjectWithPrLinks(subject: string): string {
return subject
.replace(/\(#(\d+)\)/g, (_, n) => `([#${n}](${REPO_URL}/pull/${n}))`)
.replace(/(^|\s)#(\d+)\b/g, (_, lead, n) => `${lead}[#${n}](${REPO_URL}/pull/${n})`);
}

export function extractCherryPicks(): { hash: string; subject: string }[] {
const baseRef = process.env.MERGE_BASE_REF ?? 'origin/main';
const headRef = (process.env.HEAD_REF ?? '').trim() || 'HEAD';

const mergeBase = getMergeBase(headRef, baseRef);
Comment thread
sleepytanya marked this conversation as resolved.
const commits = getAncestryPathCommits(mergeBase, headRef);
console.log(`[cherry-picks] Found ${commits.length} commit(s)`);
return commits;
}

export function buildCherryPicksSection(
commits: { hash: string; subject: string }[],
): string {
if (commits.length === 0) return '';

const lines: string[] = [
'<a id="cherry-picks"></a>',
'### :cherries: What\'s in this RC\n',
'<details>',
`<summary>${commits.length} commit(s) in this release</summary>\n`,
'| Commit | Description |',
'| :--- | :--- |',
];

for (const commit of commits) {
const linkedSubject = formatSubjectWithPrLinks(commit.subject);
const commitLink = `[\`${commit.hash}\`](${REPO_URL}/commit/${commit.hash})`;
lines.push(`| ${commitLink} | ${linkedSubject} |`);
}

lines.push('\n</details>\n');
return lines.join('\n');
}

export function buildCherryPicksFailureSection(error?: string): string {
let section = `<a id="cherry-picks"></a>
### :cherries: What's in this RC

_Could not extract commit list._`;

if (error) {
section += `\n\n<details>\n<summary>Details</summary>\n\n${error}\n\n</details>`;
}

return section + '\n';
}
6 changes: 0 additions & 6 deletions scripts/build-announce/env-validation-section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ function getRemoteFFEnv(env: string | undefined): string {
return 'rc';
case 'beta':
return 'beta';
case 'test':
case 'e2e':
return 'test';
case 'exp':
return 'exp';
case 'dev':
default:
return 'dev';
}
Expand Down
34 changes: 33 additions & 1 deletion scripts/build-announce/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import {
buildEnvValidationSection,
buildEnvValidationFailureSection,
} from './env-validation-section';
import {
extractCherryPicks,
buildCherryPicksSection,
buildCherryPicksFailureSection,
} from './cherry-picks-section';
import { validateEnv } from './validate-env';
import type { BuildInfo, TestPlanResult, EnvValidationResult } from './types';

Expand Down Expand Up @@ -151,6 +156,10 @@ function buildCommentBody(
iosResult?: EnvValidationResult;
error?: string;
},
cherryPicks: {
commits: { hash: string; subject: string }[];
error?: string;
},
testPlanError?: string,
): string {
let body = `${RC_BUILD_COMMENT_MARKER}
Expand All @@ -171,6 +180,15 @@ ${buildMoreInfoSection(buildInfo)}
body += buildEnvValidationFailureSection(envValidation.error);
}

// Add cherry-picks section
if (cherryPicks.commits.length > 0) {
body += `---\n\n`;
body += buildCherryPicksSection(cherryPicks.commits);
} else if (cherryPicks.error) {
body += `---\n\n`;
body += buildCherryPicksFailureSection(cherryPicks.error);
}

// Add test plan section
if (testPlan) {
body += `---\n\n`;
Expand Down Expand Up @@ -261,8 +279,22 @@ async function main(): Promise<void> {
console.log(' - No build-env artifacts found');
}

// Extract cherry-picks from git history
console.log('\n=== Cherry-picks ===\n');
const cherryPicks: { commits: { hash: string; subject: string }[]; error?: string } = {
commits: [],
};

try {
cherryPicks.commits = extractCherryPicks();
console.log(` - Found ${cherryPicks.commits.length} commit(s)`);
} catch (error) {
cherryPicks.error = error instanceof Error ? error.message : String(error);
console.error(` - Error: ${cherryPicks.error}`);
}
Comment thread
cursor[bot] marked this conversation as resolved.

// Build the comment body
const commentBody = buildCommentBody(buildInfo, testPlan, envValidation, testPlanError);
const commentBody = buildCommentBody(buildInfo, testPlan, envValidation, cherryPicks, testPlanError);

// Post comment and minimize old ones
console.log(`\n=== Posting Comment to PR #${prNumber} ===\n`);
Expand Down
Loading
Loading