Skip to content

Commit

Permalink
ci: support releases with branch-protection (#1458)
Browse files Browse the repository at this point in the history
* ci: support pre-release

* ci: split pre-release versus release

* chore: tbc

* support release manually

In two different steps

* docs

* chore: required

* ci: enable dry-run

* support dry-run in pre-release

* avoid pushing git tag when running lerna version

* chore: use lerna pubish from-package

To avoid bumping the versions
  • Loading branch information
v1v authored Dec 27, 2023
1 parent 866f066 commit 1c4c0ef
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 41 deletions.
87 changes: 87 additions & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
# When triggered it will prepare the release by creating a Pull Request with the
# changes to be merged. Then it will be possible to run the release manually
name: pre-release

on:
workflow_dispatch:
inputs:
dry-run:
type: boolean
description: 'Run release process in dry-run mode'
default: true

permissions:
contents: read

env:
SLACK_BUILD_MESSAGE: "Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>)"

jobs:
release:
runs-on: ubuntu-latest
permissions:
# Needed to write the release changelog
contents: write
steps:
- name: Configure github token
uses: elastic/apm-pipeline-library/.github/actions/github-token@current
with:
url: ${{ secrets.VAULT_ADDR }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}

- name: Configure git user
uses: elastic/apm-pipeline-library/.github/actions/setup-git@current
with:
username: ${{ env.GIT_USER }}
email: ${{ env.GIT_EMAIL }}
token: ${{ env.GITHUB_TOKEN }}

- uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ env.GITHUB_TOKEN }}

- uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'

- name: Install dependencies
run: npm ci

# This prevent lerna command from throwing this error:
# "Working tree has uncommitted changes, please commit or remove the following changes before continuing"
- name: Ignore git uncommitted changes
run: git update-index --skip-worktree .npmrc

- name: Create PR with the required changes to be released
id: pre-release
env:
DRY_RUN: "${{ inputs.dry-run }}"
run: |
npm run ci:pre-release
if [ -e .pr.txt ] ; then
echo "pr=$(cat .pr.txt)" >> "$GITHUB_OUTPUT"
rm .pr.txt
fi
- if: ${{ success() && inputs.dry-run == false }}
uses: elastic/apm-pipeline-library/.github/actions/slack-message@current
with:
url: ${{ secrets.VAULT_ADDR }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
channel: "#apm-agent-js"
message: |
:runner: [${{ github.repository }}] Pre Release has been triggered. Review the PR ${{ steps.pre-release.outputs.pr }}. ${{ env.SLACK_BUILD_MESSAGE }}
- if: ${{ failure() && inputs.dry-run == false }}
uses: elastic/apm-pipeline-library/.github/actions/slack-message@current
with:
url: ${{ secrets.VAULT_ADDR }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
channel: "#apm-agent-js"
message: |
:ghost: [${{ github.repository }}] Pre Release failed. ${{ env.SLACK_BUILD_MESSAGE }}
60 changes: 28 additions & 32 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
---
# When triggered it will publish the release only if the changes are related to the
# release
name: release

on:
Expand All @@ -11,6 +14,11 @@ on:
permissions:
contents: read

env:
ELASTIC_CDN_BUCKET_NAME: ${{ inputs.dry-run == false && 'apm-rum-357700bc' || 'oblt-apm-agent-rum-js-ci' }}
ELASTIC_CDN_CREDENTIALS: ${{ inputs.dry-run == false && 'secret/gce/elastic-cdn/service-account/apm-rum-admin' || 'secret/observability-team/ci/service-account/apm-agent-rum-js' }}
SLACK_BUILD_MESSAGE: "Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>)"

jobs:
release:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -49,7 +57,7 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Read NPM vault secrets
- name: Read NPM vault TOTP
if: inputs.dry-run == false
uses: hashicorp/[email protected]
with:
Expand All @@ -67,8 +75,8 @@ jobs:
git update-index --skip-worktree .npmrc
- name: Configure npm registry
uses: elastic/apm-pipeline-library/.github/actions/setup-npmrc@current
if: inputs.dry-run == false
uses: elastic/apm-pipeline-library/.github/actions/setup-npmrc@current
with:
vault-url: ${{ secrets.VAULT_ADDR }}
vault-role-id: ${{ secrets.VAULT_ROLE_ID }}
Expand All @@ -78,21 +86,9 @@ jobs:

- name: Publish the release
env:
DRY_RUN: ${{ inputs.dry-run }}
DRY_RUN: "${{ inputs.dry-run }}"
run: npm run ci:release

- name: Setup credentials
env:
DRY_RUN: ${{ inputs.dry-run }}
run: |
if [ "${DRY_RUN}" == "false" ]; then
echo 'ELASTIC_CDN_BUCKET_NAME=apm-rum-357700bc' >> ${GITHUB_ENV}
echo 'ELASTIC_CDN_CREDENTIALS=secret/gce/elastic-cdn/service-account/apm-rum-admin' >> ${GITHUB_ENV}
else
echo 'ELASTIC_CDN_BUCKET_NAME=oblt-apm-agent-rum-js-ci' >> ${GITHUB_ENV}
echo 'ELASTIC_CDN_CREDENTIALS=secret/observability-team/ci/service-account/apm-agent-rum-js' >> ${GITHUB_ENV}
fi
- name: Read GCE vault secrets
uses: hashicorp/[email protected]
with:
Expand Down Expand Up @@ -148,22 +144,22 @@ jobs:
headers: |-
cache-control: public,max-age=604800,immutable
status:
if: always()
needs:
- release
runs-on: ubuntu-latest
steps:
- id: check
uses: elastic/apm-pipeline-library/.github/actions/check-dependent-jobs@current
- if: ${{ success() && inputs.dry-run == false }}
uses: elastic/apm-pipeline-library/.github/actions/slack-message@current
with:
needs: ${{ toJSON(needs) }}
- uses: elastic/apm-pipeline-library/.github/actions/notify-build-status@current
if: inputs.dry-run == false
url: ${{ secrets.VAULT_ADDR }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
channel: "#apm-agent-js"
message: |
:runner: [${{ github.repository }}] Release has been published. ${{ env.SLACK_BUILD_MESSAGE }}
- if: ${{ failure() && inputs.dry-run == false }}
uses: elastic/apm-pipeline-library/.github/actions/slack-message@current
with:
status: ${{ steps.check.outputs.status }}
vaultUrl: ${{ secrets.VAULT_ADDR }}
vaultRoleId: ${{ secrets.VAULT_ROLE_ID }}
vaultSecretId: ${{ secrets.VAULT_SECRET_ID }}
slackChannel: "#apm-agent-js"
message: "Build result for release publication"
url: ${{ secrets.VAULT_ADDR }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
channel: "#apm-agent-js"
message: |
:ghost: [${{ github.repository }}] Release failed. ${{ env.SLACK_BUILD_MESSAGE }}
6 changes: 5 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ Before releasing, be sure to update the following documentation:

The release process is also automated in the way any specific commit from the main branch can be potentially released, for such it's required the below steps:

* Go to the [GitHub Actions](https://github.com/elastic/apm-agent-rum-js/actions/workflows/release.yml) workflow.
* Go to the [GitHub Actions](https://github.com/elastic/apm-agent-rum-js/actions/workflows/pre-release.yml) workflow.
* Click on `Run workflow` and select the `main` branch.
* Click on `Run workflow`.
* Wait for completion.
* Review the PR with the changes when merged
* Go to the [GitHub Actions](https://github.com/elastic/apm-agent-rum-js/actions/workflows/release.yml) workflow.
* Click on `Run workflow` and select the `main` branch.
* Click on `Run workflow`.
* You can go to the `https://www.npmjs.com/package/@elastic/apm-rum` and [GitHub releases](https://github.com/elastic/apm-agent-rum-js/releases) to validate that the bundles and release notes have been published.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"registry": "https://registry.npmjs.org",
"command": {
"version": {
"allowBranch": ["main", "4.x", "release"],
"allowBranch": ["main", "4.x", "release", "release/*"],
"conventionalCommits": true,
"message": "chore(release): publish",
"changelogPreset": "conventionalcommits",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"package:snapshot": "lerna exec npm pack",
"clean": "lerna exec -- rm -rf dist/",
"ci:prepare-release": "node ./scripts/ci-prepare-release.js",
"ci:pre-release": "node ./scripts/ci-pre-release.mjs",
"ci:release": "node ./scripts/ci-release.mjs"
},
"lint-staged": {
Expand Down
168 changes: 168 additions & 0 deletions scripts/ci-pre-release.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* MIT License
*
* Copyright (c) 2017-present, Elasticsearch BV
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

import { execa } from 'execa'
import * as process from 'node:process'
// To read the version then use https://nodejs.org/api/esm.html#no-require-exports-or-moduleexports
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const { version } = require('../packages/rum/package.json')

function raiseError(msg) {
console.log(msg)
process.exit(1)
}

async function gitContext() {
try {
const { stdout: username } = await execa('git', ['config', 'user.name'])
const { stdout: email } = await execa('git', ['config', 'user.email'])
return {
username,
email
}
} catch (err) {
raiseError('Failed to extract git context')
}
}

// Script logic
async function main() {
const isDryRun =
process.env.DRY_RUN == null || process.env.DRY_RUN !== 'false'

// Extract git context
const ctx = await gitContext()
console.log(`Git User: username=${ctx.username}, email=${ctx.email}`)

if (isDryRun) {
await dryRunMode()
} else {
await prodMode()
}
}

// Script logic
async function dryRunMode() {
console.log('Running in dry-run mode')

const githubToken = process.env.GITHUB_TOKEN
if (githubToken == null || githubToken === '') {
raiseError("The 'GITHUB_TOKEN' env var isn't defined")
}

const branch = `release/${version}-next`

try {
await execa('git', ['checkout', '-b', branch], {
stdin: process.stdin
})
.pipeStdout(process.stdout)
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to create git branch')
}

try {
await execa('npx', ['lerna', 'version', '--no-push', '--no-git-tag-version', '--no-changelog', 'yes'], {
stdin: process.stdin,
env: {
GH_TOKEN: githubToken
}
})
.pipeStdout(process.stdout)
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to version npm')
}
}

async function prodMode() {
console.log('Running in prod mode')

const githubToken = process.env.GITHUB_TOKEN
if (githubToken == null || githubToken === '') {
raiseError("The 'GITHUB_TOKEN' env var isn't defined")
}

const branch = `release/${version}-next`

try {
await execa('git', ['checkout', '-b', branch], {
stdin: process.stdin
})
.pipeStdout(process.stdout)
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to create git branch')
}

try {
await execa('npx', ['lerna', 'version', '--yes', '--no-push'], {
stdin: process.stdin,
env: {
GH_TOKEN: githubToken
}
})
.pipeStdout(process.stdout)
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to version npm')
}

// As long as lerna version uses --no-push then it's required to push the commits
// this will avoid pushing the git tag too.
try {
await execa('git', ['push', 'origin', branch], {
stdin: process.stdin
})
.pipeStdout(process.stdout)
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to push git branch')
}

try {
await execa('gh', ['pr', 'create', '--fill-first'], {
stdin: process.stdin,
env: {
GH_TOKEN: githubToken
}
})
.pipeStdout('.pr.txt')
.pipeStderr(process.stderr)
} catch (err) {
raiseError('Failed to create GitHub PR')
}
}

// Entrypoint
;(async () => {
try {
await main()
} catch (err) {
console.log(err)
}
})()
Loading

0 comments on commit 1c4c0ef

Please sign in to comment.