diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..0f3a6288d7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,21 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install dependencies + run: npm ci + - name: Build Release Assets + run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -l ${GITHUB_REF_NAME} + - name: Create Release + run: node tasks/create-release.js --token ${{secrets.GITHUB_TOKEN}} --tag ${GITHUB_REF_NAME} --legacy build/${GITHUB_REF_NAME}-legacy.zip --site build/${GITHUB_REF_NAME}-site.zip diff --git a/tasks/build-website.sh b/tasks/build-website.sh index a9704127ab..3335d9d63d 100755 --- a/tasks/build-website.sh +++ b/tasks/build-website.sh @@ -78,4 +78,13 @@ mv build/legacy ${build}/en/${version}/ if [[ "${latest}" == "${version}" ]] ; then echo "Copying to en/latest" cp -r ${build}/en/${version} ${build}/en/latest + + echo "Building release artifacts" + pushd ${build} + zip -r ${OLDPWD}/build/${version}-site.zip . -x "en/${version}/*" + popd + + pushd ${build}/en/${version}/legacy + zip -r ${OLDPWD}/build/${version}-legacy.zip . + popd fi diff --git a/tasks/create-release.js b/tasks/create-release.js new file mode 100644 index 0000000000..6cb653073c --- /dev/null +++ b/tasks/create-release.js @@ -0,0 +1,107 @@ +import esMain from 'es-main'; +import yargs from 'yargs'; +import {Octokit} from '@octokit/rest'; +import {basename} from 'node:path'; +import {hideBin} from 'yargs/helpers'; +import {readFile, stat} from 'node:fs/promises'; + +/** + * @typedef {Object} ReleaseOptions + * @property {string} token The bearer token. + * @property {string} tag The tag. + * @property {boolean} draft Create a draft release. + * @property {boolean} notes Generate release notes. + * @property {string} site Path to zip archive with site contents. + * @property {string} legacy Path to zip archive with legacy build. + */ + +const owner = 'openlayers'; +const repo = 'openlayers'; + +/** + * Create a release. + * @param {ReleaseOptions} options The release options. + */ +async function createRelease(options) { + const client = new Octokit({ + auth: options.token, + }); + + const response = await client.rest.repos.createRelease({ + owner, + repo, + tag_name: options.tag, + generate_release_notes: options.notes, + draft: options.draft, + }); + + await uploadAsset( + client, + response.data, + options.site, + 'Examples and docs (zip)' + ); + + await uploadAsset( + client, + response.data, + options.legacy, + 'Legacy build (zip)' + ); +} + +async function uploadAsset(client, release, assetPath, label) { + const name = basename(assetPath); + const stats = await stat(assetPath); + const data = await readFile(assetPath); + + await client.rest.repos.uploadReleaseAsset({ + url: release.upload_url, + name, + label, + headers: { + 'content-type': 'application/zip', + 'content-length': stats.size, + }, + data, + }); +} + +if (esMain(import.meta)) { + const options = yargs(hideBin(process.argv)) + .option('token', { + describe: 'The token for auth', + type: 'string', + }) + .demandOption('token') + .option('tag', { + describe: 'The release tag (e.g. v7.0.0)', + type: 'string', + }) + .demandOption('tag') + .option('legacy-zip', { + describe: 'Path to the archive with the legacy build', + type: 'string', + }) + .demandOption('legacy') + .option('site-zip', { + describe: 'Path to the archive with the site contents', + type: 'string', + }) + .demandOption('site') + .option('draft', { + describe: 'Create a draft release', + type: 'boolean', + default: true, + }) + .option('notes', { + describe: 'Generate release notes', + type: 'boolean', + default: true, + }) + .parse(); + + createRelease(options).catch((err) => { + process.stderr.write(`${err.stack}\n`, () => process.exit(1)); + }); +} diff --git a/tasks/get-latest-release.js b/tasks/get-latest-release.js index dc971fc01a..4cdbbfa641 100644 --- a/tasks/get-latest-release.js +++ b/tasks/get-latest-release.js @@ -1,7 +1,8 @@ +import esMain from 'es-main'; import semver from 'semver'; import {Octokit} from '@octokit/rest'; -async function main() { +export async function getLatestRelease() { const client = new Octokit(); let latest = '0.0.0'; @@ -21,7 +22,15 @@ async function main() { } ); - process.stdout.write(`v${latest}\n`); + return latest; } -main(); +if (esMain(import.meta)) { + getLatestRelease() + .then((latest) => { + process.stdout.write(`v${latest}\n`, () => process.exit(0)); + }) + .catch((err) => { + process.stderr.write(`${err.message}\n`, () => process.exit(1)); + }); +}