From 293f128558bd98cfb2fe6a36f9266c22ec454f4a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 18 Aug 2022 15:09:17 -0600 Subject: [PATCH 1/2] Add a job to create a release --- .github/workflows/release.yml | 21 +++++++ tasks/build-website.sh | 9 +++ tasks/create-release.js | 107 ++++++++++++++++++++++++++++++++++ tasks/get-latest-release.js | 15 ++++- 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 tasks/create-release.js 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)); + }); +} From 6db24d214b97934654d7e6b3fd75fa5bce7f8b86 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 18 Aug 2022 16:11:10 -0600 Subject: [PATCH 2/2] Deploy website and publish package for release tags --- .github/workflows/deploy-preview.yml | 2 +- .github/workflows/deploy.yml | 36 +++++++++++++++++-- .github/workflows/publish.yml | 29 ++++++++++++++-- .github/workflows/release.yml | 4 +-- tasks/create-release.js | 4 +-- tasks/newest-tag.js | 52 ++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 tasks/newest-tag.js diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 6587f175ef..010757e3b8 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -1,4 +1,4 @@ -name: Deploy Preview +name: Deploy Website (Preview) on: workflow_run: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3dceea09a1..62920ce2ac 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,16 +1,18 @@ -name: Deploy +name: Deploy Website on: push: branches: - main + tags: + - 'v*.*.*' concurrency: group: "deploy" - cancel-in-progress: true jobs: - deploy: + deploy-branch: + if: startsWith(github.ref, 'refs/heads/') runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -35,3 +37,31 @@ jobs: git add . git commit -m "Website updates" git push origin main + deploy-tag: + if: startsWith(github.ref, 'refs/tags/') + 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: Assert Latest Release + run: node tasks/newest-tag.js --tag ${GITHUB_REF_NAME} + - name: Build Website + run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -v ${GITHUB_REF_NAME} + - name: Check out openlayers.github.io + uses: actions/checkout@v3 + with: + repository: openlayers/openlayers.github.io + ssh-key: ${{ secrets.OPENLAYERS_GITHUB_IO_KEY }} + path: openlayers.github.io + - run: | + cp -r build/site/* openlayers.github.io/dist/ + cd openlayers.github.io + git config user.name "$(git --no-pager log --format=format:'%an' -n 1)" + git config user.email "$(git --no-pager log --format=format:'%ae' -n 1)" + git add . + git commit -m "Website updates" + git push origin main diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f679b4556b..f372dd13a3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,21 +1,24 @@ -name: Publish +name: Publish Package on: push: branches: - main + tags: + - 'v*.*.*' permissions: contents: read jobs: - publish-npm: + publish-branch: + if: startsWith(github.ref, 'refs/heads/') runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: npm ci @@ -28,3 +31,23 @@ jobs: npm publish --tag dev env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + publish-tag: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: npm ci + - name: Assert Latest Release + run: node tasks/newest-tag.js --tag ${GITHUB_REF_NAME} + - name: Publish + run: | + npm run build-package + cd build/ol + npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f3a6288d7..06da363e7b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release +name: Create Release on: push: @@ -16,6 +16,6 @@ jobs: - name: Install dependencies run: npm ci - name: Build Release Assets - run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -l ${GITHUB_REF_NAME} + run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -v ${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/create-release.js b/tasks/create-release.js index 6cb653073c..e35c7a4c70 100644 --- a/tasks/create-release.js +++ b/tasks/create-release.js @@ -6,7 +6,7 @@ import {hideBin} from 'yargs/helpers'; import {readFile, stat} from 'node:fs/promises'; /** - * @typedef {Object} ReleaseOptions + * @typedef {Object} Options * @property {string} token The bearer token. * @property {string} tag The tag. * @property {boolean} draft Create a draft release. @@ -20,7 +20,7 @@ const repo = 'openlayers'; /** * Create a release. - * @param {ReleaseOptions} options The release options. + * @param {Options} options The release options. */ async function createRelease(options) { const client = new Octokit({ diff --git a/tasks/newest-tag.js b/tasks/newest-tag.js new file mode 100644 index 0000000000..6887343df3 --- /dev/null +++ b/tasks/newest-tag.js @@ -0,0 +1,52 @@ +import esMain from 'es-main'; +import semver from 'semver'; +import yargs from 'yargs'; +import {getLatestRelease} from './get-latest-release.js'; +import {hideBin} from 'yargs/helpers'; + +/** + * @typedef {Object} Options + * @property {string} tag The tag. + */ + +/** + * Check if a tag is ahead of the latest release. + * @param {Options} options The options. + * @return {boolean} The provided tag is ahead of or equal to the latest release. + */ +async function main(options) { + const version = semver.valid(options.tag); + if (!version) { + return false; + } + + const parsed = semver.parse(version); + if (parsed.prerelease.length) { + return false; + } + + const latest = await getLatestRelease(); + return semver.gte(version, latest); +} + +if (esMain(import.meta)) { + const options = yargs(hideBin(process.argv)) + .option('tag', { + describe: 'The tag to test (e.g. v1.2.3)', + type: 'string', + }) + .demandOption('tag') + .parse(); + + main(options) + .then((newest) => { + if (newest) { + process.stdout.write('true\n', () => process.exit(0)); + } else { + process.stderr.write('false\n', () => process.exit(1)); + } + }) + .catch((err) => { + process.stderr.write(`${err.stack}\n`, () => process.exit(1)); + }); +}