diff --git a/.github/workflows/create-github-release.yml b/.github/workflows/create-github-release.yml deleted file mode 100644 index 8ef3f10f6a..0000000000 --- a/.github/workflows/create-github-release.yml +++ /dev/null @@ -1,36 +0,0 @@ -# New tags and releases are created by changeset https://github.com/atlassian/changesets -name: Create a new release on GitHub when a new tag is created - -on: - push: - tags: - - 'v*' # Push events to matching v*, i.e. v0.4.0, v1.1.0 - -jobs: - build: - name: Create a new release on GitHub when a new tag is created - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: use node.js 12.x - uses: actions/setup-node@v1 - with: - node-version: '12.x' - - - name: Install node dependencies - run: npm install @octokit/rest - - # GITHUB_REF is of the format refs/tags/vA.B.C - # This step extracts vA.B.C from GITHUB_REF - - name: Get the version - id: get_version - run: echo "::set-output name=TAG_NAME::${GITHUB_REF#refs/tags/}" - - # TODO/Note: This will only create a Draft release, which the maintainer can see and publish. - # If the Draft release looks good, modify the step to go ahead publish the release. (By adding the third CLI argument.) - - name: Create release on GitHub - run: node scripts/create-github-release.js ${{ steps.get_version.outputs.TAG_NAME }} - env: - GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 4de1ed060a..e9f5e602a5 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -70,21 +70,6 @@ jobs: bash <(curl -s https://codecov.io/bash) -f packages/core/coverage/* -F core bash <(curl -s https://codecov.io/bash) -f packages/core-api/coverage/* -F core-api - # Publishes current version of packages that are not already present in the registry - - name: publish - if: matrix.node-version == '12.x' - run: yarn lerna -- publish from-package --yes - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - # Tags the commit with the version in the core package if the tag doesn't exist - - uses: Klemensas/action-autotag@1.2.3 - if: matrix.node-version == '12.x' - with: - GITHUB_TOKEN: '${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}' - package_root: 'packages/core' - tag_prefix: 'v' - - name: Discord notification if: ${{ failure() }} uses: Ilshidur/action-discord@0.2.0 @@ -92,3 +77,85 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} with: args: 'Master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}' + + # A separate release build that is only run for commits that are the result of merging the "Version Packages" PR + # We can't re-use the output from the above step, but we'll have a guaranteed node_modules cache and + # only run the build steps that are necessary for publishing + release: + if: ${{ endsWith(github.event.head_commit.message, 'from backstage/changeset-release/master\n\nVersion Packages') }} + needs: build + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14.x] + + env: + CI: 'true' + NODE_OPTIONS: --max-old-space-size=4096 + + steps: + - uses: actions/checkout@v2 + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install + run: yarn install --frozen-lockfile + # End of yarn setup + + - name: build type declarations + run: yarn tsc:full + + - name: build packages + run: yarn lerna -- run --ignore example-app build + + # Publishes current version of packages that are not already present in the registry + - name: publish + run: yarn lerna -- publish from-package --yes + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + # Creates the next available tag with format "release---[.]" + - name: Create a release tag + id: create_tag + run: node scripts/create-release-tag.js + env: + GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} + + # Convert the newly created tag into a release with changelog information + - name: Create release on GitHub + run: node scripts/create-github-release.js ${{ steps.create_tag.outputs.tag_name }} 1 + env: + GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} + + # Notify everyone about this great new release :D + - name: Discord notification + uses: Ilshidur/action-discord@0.2.0 + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} + TAG_NAME: ${{ steps.create_tag.outputs.tag_name }} + with: + args: 'A new release has been published! https://github.com/backstage/backstage/releases/tag/{{TAG_NAME}}' diff --git a/scripts/create-release-tag.js b/scripts/create-release-tag.js new file mode 100755 index 0000000000..22b374a65e --- /dev/null +++ b/scripts/create-release-tag.js @@ -0,0 +1,103 @@ +#!/usr/bin/env node +/* eslint-disable import/no-extraneous-dependencies */ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This script creates a release on GitHub for the Backstage repository. + * Given a git tag, it identifies the PR created by changesets which is responsible for creating + * the git tag. It then uses the PR description consisting of changelogs for packages as the + * release description. + * + * Example: + * + * Set GITHUB_TOKEN environment variable. + * + * (Dry Run mode, will create a DRAFT release, but will not publish it.) + * (Draft releases are visible to maintainers and do not notify users.) + * $ node scripts/get-release-description v0.4.1 + * + * This will open the git tree at this tag https://github.com/backstage/backstage/tree/v0.4.1 + * It will identify https://github.com/backstage/backstage/pull/3668 as the responsible changeset PR. + * And will use everything in the PR description under "Releases" section. + * + * (Production or GitHub Actions Mode) + * $ node scripts/get-release-description v0.4.1 true + * + * This will do the same steps as above, and will publish the Release with the description. + */ + +const { Octokit } = require('@octokit/rest'); + +const baseOptions = { + owner: 'backstage', + repo: 'backstage', +}; + +async function main() { + const { GITHUB_SHA, GITHUB_TOKEN } = process.env; + if (!GITHUB_SHA) { + throw new Error('GITHUB_SHA is not set'); + } + if (!GITHUB_TOKEN) { + throw new Error('GITHUB_TOKEN is not set'); + } + + const octokit = new Octokit({ auth: GITHUB_TOKEN }); + + const date = new Date(); + const baseTagName = `release-${date.getUTCFullYear()}-${ + date.getUTCMonth() + 1 + }-${date.getUTCDate()}`; + + console.log('Requesting existing tags'); + + const existingTags = await octokit.repos.listTags({ + ...baseOptions, + per_page: 100, + }); + const existingTagNames = existingTags.data.map(obj => obj.name); + + let tagName = baseTagName; + let index = 0; + while (existingTagNames.includes(tagName)) { + index += 1; + tagName = `${baseTagName}.${index}`; + } + + console.log(`Creating release tag ${tagName}`); + + const annotatedTag = await octokit.git.createTag({ + ...baseOptions, + tag: tagName, + message: tagName, + object: GITHUB_SHA, + type: 'commit', + }); + + await octokit.git.createRef({ + ...baseOptions, + ref: `refs/tags/${tagName}`, + sha: annotatedTag.data.sha, + }); + + console.log(`::set-output name=tag_name::${tagName}`); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +});