diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000000..77ce8bc0da --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,81 @@ +name: Publish a New Release +on: + pull_request: + branches: [main] + types: [closed] + +jobs: + publish_to_npm: + name: Publish to NPM and GitHub + runs-on: ubuntu-latest + steps: + + # Determine if PR was a valid merged release PR. If so, publish the new release. Otherwise, do nothing. + # This is a valid release PR if the following 3 things are true: + # 1. The head (source) branch of the PR matches the expected "release/x.y.z" pattern + # 2. The PR has been merged, rather than closed + # 3. The head (source) repo of the PR is not a fork, or in other words the PR is made from a branch within the main repo + - name: Validate Release PR + id: validate_pr + run: | + merged=${{ github.event.pull_request.merged }} + forked=${{ github.event.pull_request.head.repo.fork }} + echo "Source branch is ${{ github.head_ref }}; merged = $merged; from a fork = $forked" + if [ $(grep -E '^release/[0-9]+\.[0-9]+\..+$' <<< '${{ github.head_ref }}') ] && [ $merged = true ] && [ $forked = false ] + then + echo "::set-output name=is_release::true" + fi + + # Get release version and draft release tag from PR metadata + - name: Get Release Metadata + if: steps.validate_pr.outputs.is_release + id: metadata + run: | + version=$(cut -d'/' -f2 <<< '${{ github.head_ref }}') + echo "::set-output name=version::$version" + + - name: Checkout Repository + if: steps.validate_pr.outputs.is_release + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '14.x' + registry-url: 'https://registry.npmjs.org' + + - name: Publish to NPM + if: steps.validate_pr.outputs.is_release + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./scripts/publish.sh + + # Generates changelog without the "Unreleased" portion for use in release body + # Newlines must be URL-encoded to render properly in the GitHub UI + - name: Generate Release Body + if: steps.validate_pr.outputs.is_release + id: generate-changelog + env: + GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} + run: | + description=$(npx lerna-changelog | sed '1,3d') + description="${description//'%'/'%25'}" + description="${description//$'\n'/'%0A'}" + description="${description//$'\r'/'%0D'}" + echo "::set-output name=CHANGELOG::$description" + + - name: Create GitHub Release + if: steps.validate_pr.outputs.is_release + id: make-release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: 'v${{ steps.metadata.outputs.version }}' + commitish: main + release_name: 'v${{ steps.metadata.outputs.version }} Release' + body: "${{ steps.generate-changelog.outputs.CHANGELOG }}" + draft: false + prerelease: false diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 0000000000..3e29aa3382 --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,63 @@ +name: Create New Release PR +on: + workflow_dispatch: + inputs: + version: + description: The semver-compliant version to tag the release with, e.g. 1.2.3, 1.0.0-rc.1 + required: true + +jobs: + create_release_pr: + name: Create Release PR and Draft Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '14.x' + + - name: Cache Dependencies + uses: actions/cache@v2 + with: + path: | + node_modules + package-lock.json + detectors/node/*/node_modules + metapackages/*/node_modules + packages/*/node_modules + plugins/node/*/node_modules + plugins/web/*/node_modules + propagators/*/node_modules + key: ${{ runner.os }}-${{ matrix.container }}-${{ hashFiles('**/package.json') }} + + # Bump versions in all package.json and version.ts files + - name: Prepare Release + run: | + npm install + npm --no-git-tag-version version ${{ github.event.inputs.version }} + npx lerna publish ${{ github.event.inputs.version }} --skip-npm --no-git-tag-version --no-push --yes + npx lerna bootstrap --no-ci + + - name: Update Changelog + env: + GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/changelog-update.sh ${{ github.event.inputs.version }} + + # Make PR with version bumps and changelog update. Merging this PR triggers publish workflow. + # See: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/.github/workflows/publish.yml + - name: Create Release PR + uses: peter-evans/create-pull-request@v3 + with: + branch: release/${{ github.event.inputs.version }} + commit-message: 'chore: ${{ github.event.inputs.version }} release proposal' + title: 'chore: ${{ github.event.inputs.version }} release proposal' + body: | + This is an auto-generated release PR. If additional changes need to be incorporated before the release, the CHANGELOG must be updated manually. + + Merging this PR will automatically release all packages to NPM. + delete-branch: true diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a4bbf588..e8cd5e07e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -All notable changes to this project will be documented in this file. +All notable changes to this project will be documented in this file. Do not remove the "Unreleased" header; it is used in the automated release workflow. ## Unreleased diff --git a/RELEASING.md b/RELEASING.md index e124d0c6fc..cdbac1c1b7 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,7 +2,31 @@ This document explains how to publish all OT modules at version x.y.z. Ensure that you’re following semver when choosing a version number. -Release Process: +## Auto-generate a Release PR + +Navigate to the [Create a Release PR Worflow](https://github.com/open-telemetry/opentelemetry-js-contrib/actions/workflows/release-pr.yml). Click the "Run workflow" dropdown, enter the semver-compliant version to release, then click "Run workflow". It will generate a release pull request from a branch named like `release/x.y.z`. + +## Review Release PR + +If necessary, update sample code or documentation on the `release/x.y.z` branch to be included in the release. Another maintainer should approve the PR, specifically validating the automatic updates and verifying the tests are passing. + +NOTE: If any major changes are merged while the release PR is open, the CHANGELOG on the `release/x.y.z` branch must be manually updated to reflect the change. + +## Merge Release PR + +After approval, merge the release PR. This will automatically publish all packages. Verify: + +* The [publish workflow](https://github.com/open-telemetry/opentelemetry-js-contrib/actions/workflows/publish.yml) runs successfully +* The new version is available in NPM, e.g. for the [Express instrumentation package](https://www.npmjs.com/package/@opentelemetry/instrumentation-express) +* A [GitHub Release](https://github.com/open-telemetry/opentelemetry-js-contrib/releases) was cut correctly, with a tag pointing to the new version, and a body of the changelog + +That's it! No need to read on unless something above went wrong. + +# Manual Publishing Process + +If any step of the above automated process fails, complete the release manually by picking up at the failed step. + +Manual Release Process Steps: * [Update to latest locally](#update-to-latest-locally) * [Create a new branch](#create-a-new-branch) diff --git a/lerna.json b/lerna.json index f0761e6bc4..f367998057 100644 --- a/lerna.json +++ b/lerna.json @@ -21,6 +21,7 @@ "feature-request": ":sparkles: (Feature)", "internal": ":house: Internal" }, - "cacheDir": ".changelog" + "cacheDir": ".changelog", + "ignoreCommitters": ["github-actions"] } } diff --git a/package.json b/package.json index 42b83f0d8d..dc3c578224 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "compile": "lerna run compile", "test": "lerna run test", "test:browser": "lerna run test:browser", - "bootstrap": "lerna bootstrap", + "bootstrap": "lerna bootstrap --no-ci", "bump": "lerna publish", "codecov": "lerna run codecov", "codecov:browser": "lerna run codecov:browser", diff --git a/scripts/changelog-update.sh b/scripts/changelog-update.sh new file mode 100755 index 0000000000..2ac2c2e1f1 --- /dev/null +++ b/scripts/changelog-update.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo " +## $1 +" > delete_me.txt +npx lerna-changelog | sed '1,3d' >> delete_me.txt +sed -i -e '/## Unreleased/r delete_me.txt' CHANGELOG.md +rm delete_me.txt diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000000..a1a6a81a91 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +for path in $(cat lerna.json | jq '.packages[]'); do + base=$(sed 's/"//g' <<< $path) # Remove quotes + for package in $base; do + if [ -d $package ]; then + echo Publishing to NPM: $package + pushd $package + npm publish + popd + fi + done +done