Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: support releases with branch-protection #1458

Merged
merged 10 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See ternary operation in the top-level env section

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
Loading