Skip to content

Release Agent

Release Agent #217

name: Release Agent
on:
workflow_dispatch:
inputs:
packageVersion:
required: true
description: 'Package version number (3.x.x)'
default: ""
type: string
packageBuildNo:
required: true
description: 'Package build number'
default: "1"
type: string
releaseBranch:
description: 'Release branch to build from (release-3.x.x)'
required: true
type: string
tagRelease:
description: 'Add tag for release (v3.x.x)'
default: false
type: boolean
githubRelease:
description: 'Draft release (v3.x.x) on GitHub'
type: boolean
default: false
createPullRequest:
description: 'Create pull request into main (required if release branch has diverged from main)'
default: false
type: boolean
publishPackages:
description: 'Publish packages to nginx repo'
default: false
type: boolean
uploadUrl:
description: 'Location to publish packages to'
required: false
default: "https://up-ap.nginx.com"
type: string
assertionDoc:
description: 'Generate assertion document'
default: false
type: boolean
env:
NFPM_VERSION: 'v2.35.3'
GOPROXY: "https://${{ secrets.ARTIFACTORY_USER }}:${{ secrets.ARTIFACTORY_TOKEN }}@${{ secrets.ARTIFACTORY_URL_PROD }}"
defaults:
run:
shell: bash
concurrency:
group: ${{ github.ref_name }}-release
cancel-in-progress: true
permissions:
contents: read
jobs:
vars:
name: Set workflow variables
runs-on: ubuntu-22.04
outputs:
tag_release: ${{steps.vars.outputs.tag_release }}
github_release: ${{steps.vars.outputs.github_release }}
publish_packages: ${{steps.vars.outputs.publish_packages }}
create_pull_request: ${{steps.vars.outputs.create_pull_request }}
steps:
- name: Checkout Repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
ref: ${{ inputs.releaseBranch }}
- name: Set variables
id: vars
run: |
echo "tag_release=${{ inputs.tagRelease }}" >> $GITHUB_OUTPUT
echo "github_release=${{ inputs.githubRelease }}" >> $GITHUB_OUTPUT
echo "publish_packages=${{ inputs.publishPackages }}" >> $GITHUB_OUTPUT
echo "create_pull_request=${{ inputs.createPullRequest }}" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
release-draft:
name: Update Release Draft
runs-on: ubuntu-22.04
needs: [vars]
permissions:
contents: write # Needed to create draft release
outputs:
release_id: ${{ steps.vars.outputs.RELEASE_ID }}
steps:
- name: Checkout Repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
if: ${{ needs.vars.outputs.github_release == 'true' }}
with:
ref: ${{ inputs.releaseBranch }}
- name: Create Draft Release
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
if: ${{ needs.vars.outputs.github_release == 'true' }}
id: release
env:
version: ${{ inputs.packageVersion }}
with:
script: |
const ref = context.ref.split("/")[2]
const {version} = process.env
console.log(`The release version is v${version}`)
const releases = (await github.rest.repos.listReleases({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
per_page: 100,
})).data
const latest_release = (await github.rest.repos.getLatestRelease({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
})).data.tag_name
console.log(`The latest release was ${latest_release}`)
if (latest_release === "v"+version) {
core.setFailed(`A published release already exists for ${latest_release}`)
} else {
const draft = releases.find((r) => r.draft && r.tag_name === "v"+version)
const draft_found = !(draft === undefined)
let release
if (draft_found){
console.log("Draft release already exists. Deleting current draft release and recreating it")
release = (await github.rest.repos.deleteRelease({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
release_id: draft.id,
}))
}
const release_notes = (await github.rest.repos.generateReleaseNotes({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
tag_name: "v"+version,
previous_tag_name: latest_release,
target_commitish: ref,
}))
const footer = `
## Resources
- Documentation -- https://github.com/nginx/agent#readme
`
release = (await github.rest.repos.createRelease({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
tag_name: "v"+version,
target_commitish: ref,
name: "v"+version,
body: release_notes.data.body + footer,
draft: true,
}))
console.log(`Release created: ${release.data.html_url}`)
console.log(`Release ID: ${release.data.id}`)
console.log(`Release notes: ${release_notes.data.body}`)
console.log(`Release Upload URL: ${release.data.upload_url}`)
return {
version: version,
release_id: release.data.id,
release_upload_url: release.data.upload_url,
}
}
- name: Set Environment Variables
id: vars
if: ${{ needs.vars.outputs.github_release == 'true' }}
run: |
echo "RELEASE_ID=$(echo '${{steps.release.outputs.result}}' | jq -r '.release_id')" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
tag-release:
name: Tag Release
runs-on: ubuntu-22.04
needs: [vars,release-draft]
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
if: ${{ needs.vars.outputs.tag_release == 'true' }}
with:
ref: ${{ inputs.releaseBranch }}
- name: Tag release
if: ${{ needs.vars.outputs.tag_release == 'true' }}
run: |
git config --global user.name 'github-actions'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git tag -a "v${{ inputs.packageVersion }}" -m "CI Autogenerated"
git push origin "v${{ inputs.packageVersion }}"
build-and-upload-packages:
name: Build and upload release packages
runs-on: ubuntu-22.04-amd64
needs: [vars,release-draft,tag-release]
permissions:
id-token: write
contents: write # Needed to update a release
steps:
- name: Checkout Repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
ref: ${{ inputs.releaseBranch }}
- name: Setup go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: 'go.mod'
cache: false
- name: Setup package build environment
run: |
go mod download
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@${{ env.NFPM_VERSION }}
sudo apt-get update
sudo apt-get install -y gpgv1 monkeysphere
make install-tools
export PATH=$PATH:~/go/bin
nfpm --version
- name: Set the VERSION environment variable
run: echo VERSION=v${{ inputs.packageVersion }} >> $GITHUB_ENV
- name: Build Packages
env:
GPG_KEY: ${{ secrets.INDIGO_GPG_AGENT }}
NFPM_SIGNING_KEY_FILE: .key.asc
VERSION: ${{ env.VERSION }}
PACKAGE_BUILD: ${{ inputs.packageBuildNo }}
run: |
export PATH=$PATH:~/go/bin
echo "$GPG_KEY" | base64 --decode > ${NFPM_SIGNING_KEY_FILE}
make package
find build/ -type f -name "nginx-agent*"
- name: Archive AMD64 Binaries
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: nginx-agent-binaries-${{ inputs.packageVersion }}-amd64
path: |
build/amd64/nginx-agent
build/amd64/nginx-agent.sha256
build/amd64/nginx-agent.buildstart
build/amd64/nginx-agent.buildend
- name: Archive ARM64 Binaries
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: nginx-agent-binaries-${{ inputs.packageVersion }}-arm64
path: |
build/arm64/nginx-agent
build/arm64/nginx-agent.sha256
build/arm64/nginx-agent.buildstart
build/arm64/nginx-agent.buildend
- name: List artifacts
run: |
echo "Run ID: ${{ github.run_id }}"
echo "Run Number: ${{ github.run_number }}"
echo "nginx-agent-binaries-${{ inputs.packageVersion }}-arm64"
find build/arm64 -type f -name "nginx-agent*"
echo "nginx-agent-binaries-${{ inputs.packageVersion }}-amd64"
find build/amd64 -type f -name "nginx-agent*"
- name: Get Id Token
if: ${{ inputs.publishPackages == true }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: idtoken
with:
script: |
let id_token = await core.getIDToken()
core.setOutput('id_token', id_token)
- name: Upload Release Packages to NGINX repo
if: ${{ inputs.publishPackages == true }}
env:
TOKEN: ${{ steps.idtoken.outputs.id_token }}
UPLOAD_URL: ${{ inputs.uploadUrl }}
run: |
make release
assertion-document:
name: Build and Generate Assertion Document
needs: [build-and-upload-packages]
if : ${{ inputs.assertionDoc == true }}
uses: ./.github/workflows/assertion.yml
permissions:
id-token: write
contents: read
with:
packageVersion: ${{ inputs.packageVersion }}
runId: ${{ github.run_id }}
secrets:
ARTIFACTORY_USER: ${{ secrets.ARTIFACTORY_USER }}
ARTIFACTORY_TOKEN: ${{ secrets.ARTIFACTORY_TOKEN }}
ARTIFACTORY_URL: ${{ secrets.ARTIFACTORY_URL }}
merge-release:
if: ${{ needs.vars.outputs.create_pull_request == 'true' }}
name: Merge release branch back into main branch
runs-on: ubuntu-22.04
needs: [vars,tag-release]
permissions:
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
ref: ${{ inputs.releaseBranch }}
- name: Create Pull Request
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { repo, owner } = context.repo;
const result = await github.rest.pulls.create({
title: 'Merge ${{ inputs.releaseBranch }} back into main',
owner,
repo,
head: '${{ inputs.releaseBranch }}',
base: 'main',
body: [
'This PR was auto-generated by the release workflow.',
'NOTE: DO NOT squash commits when merging!',
].join('\n')
});