Skip to content

Commit

Permalink
actions: promote release action (#142)
Browse files Browse the repository at this point in the history
Signed-off-by: flakey5 <[email protected]>
  • Loading branch information
flakey5 authored Oct 16, 2024
1 parent 09a0891 commit 95e02b5
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 2 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/promote.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Promote Release

on:
# Triggered by the dist server
workflow_dispatch:
inputs:
path:
description: 'path to promote'
required: true
recursive:
description: 'is the path a directory'
type: boolean
required: true

jobs:
promote-release:
name: Promote Release
runs-on: ubuntu-latest

steps:
- name: Git Checkout
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'npm'

- name: Promote Files
env:
CF_ACCESS_KEY_ID: ${{ secrets.CF_ACCESS_KEY_ID }}
CF_SECRET_ACCESS_KEY: ${{ secrets.CF_SECRET_ACCESS_KEY }}
run: |
node scripts/promote-release.mjs ${{ inputs.path }} ${{ inputs.recursive == true && '--recursive' || '' }}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"type": "module",
"scripts": {
"start": "wrangler dev --remote",
"format": "prettier --check --write \"**/*.{ts,js,json,md}\"",
"prettier": "prettier --check \"**/*.{ts,js,json,md}\"",
"format": "prettier --check --write \"**/*.{ts,js,mjs,json,md}\"",
"prettier": "prettier --check \"**/*.{ts,js,mjs,json,md}\"",
"lint": "eslint ./src",
"test": "npm run test:unit && npm run test:e2e",
"test:unit": "node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --import=tsx ./tests/unit/index.test.ts",
Expand Down
11 changes: 11 additions & 0 deletions scripts/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

export const ENDPOINT =
process.env.ENDPOINT ??
'https://07be8d2fbc940503ca1be344714cb0d1.r2.cloudflarestorage.com';

export const PROD_BUCKET = process.env.PROD_BUCKET ?? 'dist-prod';

export const STAGING_BUCKET = process.env.STAGING_BUCKET ?? 'dist-staging';

export const R2_RETRY_COUNT = 3;
135 changes: 135 additions & 0 deletions scripts/promote-release.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env node

/**
* Usage: `promote-release <prefix in dist-staging to promote> [--recursive]`
* ex/ `promote-release nodejs/release/v20.0.0/ --recursive`
*/

import {
S3Client,
ListObjectsV2Command,
CopyObjectCommand,
} from '@aws-sdk/client-s3';
import {
ENDPOINT,
PROD_BUCKET,
STAGING_BUCKET,
R2_RETRY_COUNT,
} from './constants.mjs';

if (process.argv.length !== 3 && process.argv.length !== 4) {
console.error(
`usage: promote-release <prefix in dist-staging to promote> [--recursive]`
);
process.exit(1);
}

if (!process.env.CF_ACCESS_KEY_ID) {
console.error('CF_ACCESS_KEY_ID missing');
process.exit(1);
}

if (!process.env.CF_SECRET_ACCESS_KEY) {
console.error('CF_SECRET_ACCESS_KEY missing');
process.exit(1);
}

const client = new S3Client({
endpoint: ENDPOINT,
region: 'auto',
credentials: {
accessKeyId: process.env.CF_ACCESS_KEY_ID,
secretAccessKey: process.env.CF_SECRET_ACCESS_KEY,
},
});

const path = process.argv[2];
const recursive =
process.argv.length === 4 && process.argv[3] === '--recursive';

if (recursive) {
const files = await getFilesToPromote(path);

for (const file of files) {
await promoteFile(file);
}
} else {
await promoteFile(file);
}

/**
* @param {string} path
* @returns {string[]}
*/
async function getFilesToPromote(path) {
let paths = [];

let truncated = true;
let continuationToken;
while (truncated) {
const data = await retryWrapper(async () => {
return await client.send(
new ListObjectsV2Command({
Bucket: STAGING_BUCKET,
Delimiter: '/',
Prefix: path,
ContinuationToken: continuationToken,
})
);
});

if (data.CommonPrefixes) {
for (const directory of data.CommonPrefixes) {
paths.push(...(await getFilesToPromote(directory.Prefix)));
}
}

if (data.Contents) {
for (const object of data.Contents) {
paths.push(object.Key);
}
}

truncated = data.IsTruncated ?? false;
continuationToken = data.NextContinuationToken;
}

return paths;
}

/**
* @param {string} file
*/
async function promoteFile(file) {
console.log(`Promoting ${file}`);

await retryWrapper(async () => {
return await client.send(
new CopyObjectCommand({
Bucket: PROD_BUCKET,
CopySource: `${STAGING_BUCKET}/${file}`,
Key: file,
})
);
}, R2_RETRY_COUNT);
}

/**
* @param {() => Promise<T>} request
* @returns {Promise<T>}
*/
async function retryWrapper(request, retryLimit) {
let r2Error;

for (let i = 0; i < R2_RETRY_COUNT; i++) {
try {
const result = await request();
return result;
} catch (err) {
r2Error = err;
process.emitWarning(`error when contacting r2: ${err}`);
}
}

throw r2Error;
}

0 comments on commit 95e02b5

Please sign in to comment.