Skip to content

[ci] Run stale cache cleanup every 6 hours #32739

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

Merged
merged 2 commits into from
Mar 25, 2025
Merged
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
21 changes: 16 additions & 5 deletions .github/workflows/runtime_build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,13 @@ jobs:
./build2.tgz
if-no-files-found: error
- uses: actions/attest-build-provenance@v2
# We don't verify builds generated from pull requests not originating from facebook/react.
# However, if the PR lands, the run on `main` will generate the attestation which can then
# be used to download a build via scripts/release/download-experimental-build.js.
#
# Note that this means that scripts/release/download-experimental-build.js must be run with
# --no-verify when downloading a build from a fork.
if: github.event.pull_request.head.repo.full_name != github.repository
with:
subject-name: artifacts_combined.zip
subject-digest: sha256:${{ steps.upload_artifacts_combined.outputs.artifact-digest }}
Expand Down Expand Up @@ -806,14 +813,18 @@ jobs:
- run: yarn --cwd scripts/release install --frozen-lockfile
if: steps.node_modules.outputs.cache-hit != 'true'
- name: Download artifacts for base revision
# The build could have been generated from a fork, so we must download the build without
# any verification. This is safe since we only use this for sizebot calculation and the
# unverified artifact is not used. Additionally this workflow runs in the pull_request
# trigger so only restricted permissions are available.
run: |
GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }})
GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }}) ${{ (github.event.pull_request.head.repo.full_name != github.repository && '--no-verify') || ''}}
mv ./build ./base-build
# TODO: The `download-experimental-build` script copies the npm
# packages into the `node_modules` directory. This is a historical
# quirk of how the release script works. Let's pretend they
# don't exist.
- name: Delete extraneous files
# TODO: The `download-experimental-build` script copies the npm
# packages into the `node_modules` directory. This is a historical
# quirk of how the release script works. Let's pretend they
# don't exist.
run: rm -rf ./base-build/node_modules
- name: Display structure of base-build from origin/main
run: ls -R base-build
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/shared_cleanup_stale_branch_caches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
name: (Shared) Cleanup Stale Branch Caches
on:
schedule:
- cron: 0 0 * * *
# Every 6 hours
- cron: 0 */6 * * *
workflow_dispatch:

permissions: {}
Expand Down
13 changes: 12 additions & 1 deletion scripts/release/download-experimental-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const argv = yargs.wrap(yargs.terminalWidth()).options({
demandOption: true,
type: 'string',
},
'no-verify': {
describe: 'Skip verification',
requiresArg: false,
type: 'boolean',
default: false,
},
}).argv;

function printSummary(commit) {
Expand All @@ -48,8 +54,13 @@ function printSummary(commit) {
}

const main = async () => {
const {commit, releaseChannel, noVerify} = argv;
try {
await downloadBuildArtifacts(argv.commit, argv.releaseChannel);
await downloadBuildArtifacts({
commit,
releaseChannel,
noVerify,
});
printSummary(argv.commit);
} catch (error) {
handleError(error);
Expand Down
8 changes: 4 additions & 4 deletions scripts/release/prepare-release-from-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ const run = async () => {
const params = await parseParams();
params.cwd = join(__dirname, '..', '..');

await downloadBuildArtifacts(
params.commit,
params.releaseChannel ?? process.env.RELEASE_CHANNEL
);
await downloadBuildArtifacts({
commit: params.commit,
releaseChannel: params.releaseChannel ?? process.env.RELEASE_CHANNEL,
});

if (!params.skipTests) {
await testPackagingFixture(params);
Expand Down
52 changes: 29 additions & 23 deletions scripts/release/shared-commands/download-build-artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async function getArtifact(workflowRunId, artifactName) {
return artifact;
}

async function processArtifact(artifact, commit, releaseChannel) {
async function processArtifact(artifact, opts) {
// Download and extract artifact
const cwd = join(__dirname, '..', '..', '..');
const tmpDir = mkdtempSync(join(os.tmpdir(), 'react_'));
Expand All @@ -97,14 +97,18 @@ async function processArtifact(artifact, commit, releaseChannel) {
}
);

// Use https://cli.github.com/manual/gh_attestation_verify to verify artifact
if (executableIsAvailable('gh')) {
await exec(
`gh attestation verify artifacts_combined.zip --repo=${OWNER}/${REPO}`,
{
cwd: tmpDir,
}
);
if (opts.noVerify === true) {
console.log(theme`{caution Skipping verification of build artifact.}`);
} else {
// Use https://cli.github.com/manual/gh_attestation_verify to verify artifact
if (executableIsAvailable('gh')) {
await exec(
`gh attestation verify artifacts_combined.zip --repo=${OWNER}/${REPO}`,
{
cwd: tmpDir,
}
);
}
}

await exec(
Expand All @@ -124,17 +128,19 @@ async function processArtifact(artifact, commit, releaseChannel) {
}
let sourceDir;
// TODO: Rename release channel to `next`
if (releaseChannel === 'stable') {
if (opts.releaseChannel === 'stable') {
sourceDir = 'oss-stable';
} else if (releaseChannel === 'experimental') {
} else if (opts.releaseChannel === 'experimental') {
sourceDir = 'oss-experimental';
} else if (releaseChannel === 'rc') {
} else if (opts.releaseChannel === 'rc') {
sourceDir = 'oss-stable-rc';
} else if (releaseChannel === 'latest') {
} else if (opts.releaseChannel === 'latest') {
sourceDir = 'oss-stable-semver';
} else {
console.error('Internal error: Invalid release channel: ' + releaseChannel);
process.exit(releaseChannel);
console.error(
'Internal error: Invalid release channel: ' + opts.releaseChannel
);
process.exit(opts.releaseChannel);
}
await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {
cwd,
Expand All @@ -145,19 +151,19 @@ async function processArtifact(artifact, commit, releaseChannel) {
/[\u0000-\u001F\u007F-\u009F]/g,
''
);
if (buildSha !== commit) {
if (buildSha !== opts.commit) {
throw new Error(
`Requested commit sha does not match downloaded artifact. Expected: ${commit}, got: ${buildSha}`
`Requested commit sha does not match downloaded artifact. Expected: ${opts.commit}, got: ${buildSha}`
);
}
}

async function downloadArtifactsFromGitHub(commit, releaseChannel) {
async function downloadArtifactsFromGitHub(opts) {
let workflowRun;
let retries = 0;
// wait up to 10 mins for build to finish: 10 * 60 * 1_000) / 30_000 = 20
while (retries < 20) {
workflowRun = await getWorkflowRun(commit);
workflowRun = await getWorkflowRun(opts.commit);
if (typeof workflowRun.status === 'string') {
switch (workflowRun.status) {
case 'queued':
Expand All @@ -174,7 +180,7 @@ async function downloadArtifactsFromGitHub(commit, releaseChannel) {
workflowRun.id,
'artifacts_combined'
);
await processArtifact(artifact, commit, releaseChannel);
await processArtifact(artifact, opts);
return;
} else {
console.log(
Expand Down Expand Up @@ -207,10 +213,10 @@ ${workflowRun != null ? JSON.stringify(workflowRun, null, '\t') : workflowRun}`
process.exit(1);
}

async function downloadBuildArtifacts(commit, releaseChannel) {
const label = theme`commit {commit ${commit}})`;
async function downloadBuildArtifacts(opts) {
const label = theme`commit {commit ${opts.commit}})`;
return logPromise(
downloadArtifactsFromGitHub(commit, releaseChannel),
downloadArtifactsFromGitHub(opts),
theme`Downloading artifacts from GitHub for ${label}`
);
}
Expand Down
Loading