Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Migrate LavaMoat build visualization #29414

Closed
wants to merge 12 commits into from
12 changes: 0 additions & 12 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1413,10 +1413,6 @@ jobs:
- store_artifacts:
path: test-artifacts
destination: test-artifacts
# important: generate lavamoat viz AFTER uploading builds as artifacts
- run:
name: build:lavamoat-viz
command: ./.circleci/scripts/create-lavamoat-viz.sh
- store_artifacts:
path: build-artifacts
destination: build-artifacts
Expand All @@ -1426,14 +1422,6 @@ jobs:
- store_artifacts:
path: development/ts-migration-dashboard/build/final
destination: ts-migration-dashboard
- run:
name: Set branch parent commit env var
command: |
echo "export PARENT_COMMIT=$(git merge-base origin/HEAD HEAD)" >> $BASH_ENV
source $BASH_ENV
- run:
name: build:announce
command: ./development/metamaskbot-build-announce.js

job-publish-release:
executor: node-browsers-small
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build-lavamoat-viz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Build LavaMoat visualization

on:
workflow_call:

jobs:
build-lavamoat-viz:
name: Build LavaMoat visualization
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup environment
uses: metamask/github-tools/.github/actions/setup-environment@main

- name: Build LavaMoat visualization
run: ./development/create-lavamoat-viz.sh

- uses: actions/upload-artifact@v4
with:
name: lavamoat-build-viz
path: ./build-artifacts/build-viz/
7 changes: 7 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ jobs:
name: Wait for CircleCI workflow status
uses: ./.github/workflows/wait-for-circleci-workflow-status.yml

publish-prerelease:
name: Publish prerelease
if: ${{ github.event_name == 'pull_request' }}
uses: ./.github/workflows/publish-prerelease.yml
secrets:
PR_COMMENT_TOKEN: ${{ secrets.PR_COMMENT_TOKEN }}

all-jobs-completed:
name: All jobs completed
runs-on: ubuntu-latest
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/publish-prerelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Publish prerelease

on:
workflow_call:
secrets:
PR_COMMENT_TOKEN:
required: true

jobs:
lavamoat-build-viz:
name: Build LavaMoat build visualization
runs-on: ubuntu-latest
outputs:
ARTIFACT_ID: ${{ steps.artifact-upload-step.outputs.artifact-id }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup environment
uses: metamask/github-tools/.github/actions/setup-environment@main

- name: Build LavaMoat visualization
run: ./development/create-lavamoat-viz.sh
env:
INFURA_PROJECT_ID: 00000000000

- uses: actions/upload-artifact@v4
id: artifact-upload-step
with:
name: lavamoat-build-viz
path: ./build-artifacts/build-viz/

publish-prerelease:
name: Publish prerelease
needs: lavamoat-build-viz
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # This is needed to get merge base to calculate bundle size diff

- name: Setup environment
uses: metamask/github-tools/.github/actions/setup-environment@main

- name: Get merge base commit hash
id: get-merge-base
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
merge_base="$(git merge-base "origin/${BASE_REF}" HEAD)"
echo "Merge base is '${merge_base}'"
echo "MERGE_BASE=${merge_base}" >> "$GITHUB_OUTPUT"

- name: Get CircleCI job details
id: get-circleci-job-details
env:
REPOSITORY: ${{ github.repository }}
BRANCH: ${{ github.head_ref }}
HEAD_COMMIT_HASH: ${{ github.event.pull_request.head.sha }}
run: |
pipeline_id=$(curl --silent "https://circleci.com/api/v2/project/gh/$OWNER/$REPOSITORY/pipeline?branch=$BRANCH" | jq -r ".items | map(select(.vcs.revision == \"${HEAD_COMMIT_HASH}\" )) | first | .id")
workflow_id=$(curl --silent "https://circleci.com/api/v2/pipeline/$pipeline_id/workflow" | jq -r ".items[0].id")
job_details=$(curl --silent "https://circleci.com/api/v2/workflow/$workflow_id/job" | jq -r '.items[] | select(.name == "job-publish-prerelease")')
build_num=$(echo "$job_details" | jq -r '.job_number')

echo 'CIRCLE_BUILD_NUM='"$build_num" >> "$GITHUB_OUTPUT"
job_id=$(echo "$job_details" | jq -r '.id')
echo 'CIRCLE_WORKFLOW_JOB_ID='"$job_id" >> "$GITHUB_OUTPUT"

echo "Getting artifacts from pipeline '${pipeline_id}', workflow '${workflow_id}', build number '${build_num}', job ID '${job_id}'"

- name: Get CircleCI job artifacts
env:
CIRCLE_WORKFLOW_JOB_ID: ${{ steps.get-circleci-job-details.outputs.CIRCLE_WORKFLOW_JOB_ID }}
run: |
mkdir -p "test-artifacts/chrome/benchmark"
curl --silent --location "https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/0/test-artifacts/chrome/benchmark/pageload.json" > "test-artifacts/chrome/benchmark/pageload.json"

bundle_size=$(curl --silent --location "https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/0/test-artifacts/chrome/bundle_size.json")
mkdir -p "test-artifacts/chrome"
echo "${bundle_size}" > "test-artifacts/chrome/bundle_size.json"

stories=$(curl --silent --location "https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/0/storybook/stories.json")
mkdir "storybook-build"
echo "${stories}" > "storybook-build/stories.json"

- name: Download LavaMoat build visualization
uses: actions/download-artifact@v4
with:
name: lavamoat-build-viz
path: ./build-artifacts/build-viz/

- name: Publish prerelease
env:
PR_COMMENT_TOKEN: ${{ secrets.PR_COMMENT_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_COMMIT_HASH: ${{ github.event.pull_request.head.sha }}
MERGE_BASE_COMMIT_HASH: ${{ steps.get-merge-base.outputs.MERGE_BASE }}
CIRCLE_BUILD_NUM: ${{ steps.get-circleci-job-details.outputs.CIRCLE_BUILD_NUM }}
CIRCLE_WORKFLOW_JOB_ID: ${{ steps.get-circleci-job-details.outputs.CIRCLE_WORKFLOW_JOB_ID }}
LAVAMOAT_BUILD_VIZ_ID: ${{needs.lavamoat-build-viz.outputs.ARTIFACT_ID}}
run: ./development/metamaskbot-build-announce.js
2 changes: 1 addition & 1 deletion development/highlights/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async function getHighlights({ artifactBase }) {
// here we assume the PR base branch ("target") is `main` in lieu of doing
// a query against the github api which requires an access token
// see https://discuss.circleci.com/t/how-to-retrieve-a-pull-requests-base-branch-name-github/36911
const changedFiles = await getChangedFiles({ target: 'main' });
const changedFiles = await getChangedFiles({ target: 'origin/main' });
console.log(`detected changed files vs main:`);
for (const filename of changedFiles) {
console.log(` ${filename}`);
Expand Down
85 changes: 49 additions & 36 deletions development/metamaskbot-build-announce.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const path = require('path');
// Fetch is part of node js in future versions, thus triggering no-shadow
// eslint-disable-next-line no-shadow
const fetch = require('node-fetch');
const glob = require('fast-glob');
const VERSION = require('../package.json').version;
const { getHighlights } = require('./highlights');

Expand Down Expand Up @@ -38,29 +37,43 @@ function getPercentageChange(from, to) {
return parseFloat(((to - from) / Math.abs(from)) * 100).toFixed(2);
}

/**
* Check whether an artifact exists,
*
* @param {string} url - The URL of the artifact to check.
* @returns True if the artifact exists, false if it doesn't
*/
async function artifactExists(url) {
// Using a regular GET request here rather than HEAD because for some reason CircleCI always
// returns 404 for HEAD requests.
const response = await fetch(url);
return response.ok;
}

async function start() {
const {
GITHUB_COMMENT_TOKEN,
CIRCLE_PULL_REQUEST,
CIRCLE_SHA1,
PR_COMMENT_TOKEN,
PR_NUMBER,
HEAD_COMMIT_HASH,
MERGE_BASE_COMMIT_HASH,
CIRCLE_BUILD_NUM,
CIRCLE_WORKFLOW_JOB_ID,
PARENT_COMMIT,
LAVAMOAT_BUILD_VIZ_ID,
} = process.env;

console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST);
console.log('CIRCLE_SHA1', CIRCLE_SHA1);
console.log('PR_NUMBER', PR_NUMBER);
console.log('HEAD_COMMIT_HASH', HEAD_COMMIT_HASH);
console.log('MERGE_BASE_COMMIT_HASH', MERGE_BASE_COMMIT_HASH);
console.log('CIRCLE_BUILD_NUM', CIRCLE_BUILD_NUM);
console.log('CIRCLE_WORKFLOW_JOB_ID', CIRCLE_WORKFLOW_JOB_ID);
console.log('PARENT_COMMIT', PARENT_COMMIT);
console.log('LAVAMOAT_BUILD_VIZ_ID', LAVAMOAT_BUILD_VIZ_ID);

if (!CIRCLE_PULL_REQUEST) {
console.warn(`No pull request detected for commit "${CIRCLE_SHA1}"`);
if (!PR_NUMBER) {
console.warn(`No pull request detected for commit "${HEAD_COMMIT_HASH}"`);
return;
}

const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop();
const SHORT_SHA1 = CIRCLE_SHA1.slice(0, 7);
const SHORT_SHA1 = HEAD_COMMIT_HASH.slice(0, 7);
const BUILD_LINK_BASE = `https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/0`;
// build the github comment content

Expand Down Expand Up @@ -112,30 +125,30 @@ async function start() {

// links to bundle browser builds
const bundles = {};
const fileType = '.html';
const sourceMapRoot = '/build-artifacts/source-map-explorer/';
const bundleFiles = await glob(`.${sourceMapRoot}*${fileType}`);

bundleFiles.forEach((bundleFile) => {
const fileName = bundleFile.split(sourceMapRoot)[1];
const bundleName = fileName.split(fileType)[0];
const url = `${BUILD_LINK_BASE}${sourceMapRoot}${fileName}`;
let fileRoot = bundleName;
let fileIndex = bundleName.match(/-[0-9]{1,}$/u)?.index;

if (fileIndex) {
fileRoot = bundleName.slice(0, fileIndex);
fileIndex = bundleName.slice(fileIndex + 1, bundleName.length);
}

const link = `<a href="${url}">${fileIndex || fileRoot}</a>`;
const fileRoots = [
'background',
'common',
'ui',
'content-script',
'offscreen',
];

if (fileRoot in bundles) {
for (const fileRoot of fileRoots) {
bundles[fileRoot] = [];
let fileIndex = 0;
let url = `${BUILD_LINK_BASE}${sourceMapRoot}${fileRoot}-${fileIndex}.html`;
console.log(`Verifying ${url}`);
while (await artifactExists(url)) {
const link = `<a href="${url}">${fileIndex}</a>`;
bundles[fileRoot].push(link);
} else {
bundles[fileRoot] = [link];

fileIndex += 1;
url = `${BUILD_LINK_BASE}${sourceMapRoot}${fileRoot}-${fileIndex}.html`;
console.log(`Verifying ${url}`);
}
});
console.log(`Not found: ${url}`);
}

const bundleMarkup = `<ul>${Object.keys(bundles)
.map((key) => `<li>${key}: ${bundles[key].join(', ')}</li>`)
Expand All @@ -154,7 +167,7 @@ async function start() {
const tsMigrationDashboardLink = `<a href="${tsMigrationDashboardUrl}">Dashboard</a>`;

// links to bundle browser builds
const depVizUrl = `${BUILD_LINK_BASE}/build-artifacts/build-viz/index.html`;
const depVizUrl = `https://api.github.com/repos/MetaMask/metamask-extension/actions/artifacts/${LAVAMOAT_BUILD_VIZ_ID}/zip`;
const depVizLink = `<a href="${depVizUrl}">Build System</a>`;
const moduleInitStatsBackgroundUrl = `${BUILD_LINK_BASE}/test-artifacts/chrome/initialisation/background/index.html`;
const moduleInitStatsBackgroundLink = `<a href="${moduleInitStatsBackgroundUrl}">Background Module Init Stats</a>`;
Expand Down Expand Up @@ -328,7 +341,7 @@ async function start() {
};

const devSizes = Object.keys(prSizes).reduce((sizes, part) => {
sizes[part] = devBundleSizeStats[PARENT_COMMIT][part] || 0;
sizes[part] = devBundleSizeStats[MERGE_BASE_COMMIT_HASH][part] || 0;
return sizes;
}, {});

Expand Down Expand Up @@ -379,7 +392,7 @@ async function start() {
}

const JSON_PAYLOAD = JSON.stringify({ body: commentBody });
const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${CIRCLE_PR_NUMBER}/comments`;
const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${PR_NUMBER}/comments`;
console.log(`Announcement:\n${commentBody}`);
console.log(`Posting to: ${POST_COMMENT_URI}`);

Expand All @@ -388,7 +401,7 @@ async function start() {
body: JSON_PAYLOAD,
headers: {
'User-Agent': 'metamaskbot',
Authorization: `token ${GITHUB_COMMENT_TOKEN}`,
Authorization: `token ${PR_COMMENT_TOKEN}`,
},
});
if (!response.ok) {
Expand Down
Loading