Skip to content

release

release #73

Workflow file for this run

# Copyright 2023 The Authors (see AUTHORS file)
#
# 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.
name: 'release'
on:
workflow_run:
workflows:
- 'ci'
types:
- 'completed'
branches:
- 'main'
env:
SOURCE_DOCKER_REPO: 'us-docker.pkg.dev/github-metrics-aggreg-i-64d426/ci-images'
TARGET_DOCKER_REPO: 'us-docker.pkg.dev/abcxyz-artifacts/docker-images'
IMAGE_NAME: 'github-metrics-aggregator'
SOURCE_TAG: '${{ github.event.workflow_run.head_sha }}'
# Don't cancel in progress since we don't want to have half-baked releases.
concurrency:
group: '${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}'
cancel-in-progress: false
jobs:
create-release:
# trigger only when ci workflow completes successfully and the head commit message starts with 'Release: v'
if: |-
${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_commit.message, 'Release: v') }}
runs-on: 'ubuntu-latest'
permissions:
contents: 'read'
id-token: 'write'
outputs:
created: '${{ steps.create-release.outputs.created || false }}'
tag: '${{ steps.create-release.outputs.tag }}'
version: '${{ steps.create-release.outputs.version }}'
steps:
- name: 'Mint token'
id: 'mint-token'
uses: 'abcxyz/github-token-minter/.github/actions/mint-token@main' # ratchet:exclude
with:
wif_provider: '${{ vars.TOKEN_MINTER_WIF_PROVIDER }}'
wif_service_account: '${{ vars.TOKEN_MINTER_WIF_SERVICE_ACCOUNT }}'
service_audience: '${{ vars.TOKEN_MINTER_SERVICE_AUDIENCE }}'
service_url: '${{ vars.TOKEN_MINTER_SERVICE_URL }}'
requested_permissions: |-
{
"scope": "release",
"repositories": ["${{ github.event.repository.name }}"],
"permissions": {
"contents": "write"
}
}
- uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7
id: 'create-release'
env:
EXPECTED_EMAIL: '${{ vars.TOKEN_MINTER_GITHUB_EMAIL }}'
with:
github-token: '${{ steps.mint-token.outputs.token }}'
script: |-
// Get the head commit from the API instead of the event, because
// signature status is not available in the webhook.
const headCommit = context.payload.workflow_run.head_commit;
// Ensure the commit is signed.
const commitResult = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
ref: headCommit.id,
})
// Ensure the commit is a release commit.
const commitMessage = commitResult.data.commit.message;
const matches = commitMessage.match(/Release: v(?<version>[^\s]+)/i);
if (!matches || !matches.groups) {
core.setFailed(`❌ Commit "${commitMessage}" does not match version syntax`);
return;
}
let version = matches.groups.version;
while(version.charAt(0).toLowerCase() === 'v') {
version = version.substr(1);
}
core.info(`👾 Computed version as: "${version}"`)
core.setOutput('version', version)
// Set the tag (which has the leading "v") prefix.
const tag = `v${version}`;
core.info(`👾 Computed tag as: "${tag}"`)
core.setOutput('tag', tag)
// Verify the commit is signed.
if (!commitResult.data.commit.verification.verified) {
core.setFailed(`❌ Commit is not signed`)
return;
}
// Verify the email matches the expected committer.
const expectedEmail = process.env.EXPECTED_EMAIL;
const gotEmail = commitResult.data.commit.author.email;
if (gotEmail !== expectedEmail) {
core.setFailed(`❌ Commit author is "${gotEmail}", expected "${expectedEmail}"`);
return;
}
// Compute prerelease.
const prerelease = ['-', 'pre', 'alpha', 'beta', 'preview'].some((v) => version.includes(v));
// Create the release.
const response = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: tag,
target_commitish: headCommit.id,
name: tag,
generate_release_notes: true,
prerelease: prerelease,
draft: true,
make_latest: 'legacy',
});
core.setOutput('created', true);
core.info(`✅ Created release "${response.data.name}" at ${response.data.html_url}`);
publish-release:
runs-on: 'ubuntu-latest'
environment: 'production'
permissions:
contents: 'read'
id-token: 'write'
needs:
- 'create-release'
steps:
- name: 'Mint token'
id: 'mint-token'
uses: 'abcxyz/github-token-minter/.github/actions/mint-token@main' # ratchet:exclude
with:
wif_provider: '${{ vars.TOKEN_MINTER_WIF_PROVIDER }}'
wif_service_account: '${{ vars.TOKEN_MINTER_WIF_SERVICE_ACCOUNT }}'
service_audience: '${{ vars.TOKEN_MINTER_SERVICE_AUDIENCE }}'
service_url: '${{ vars.TOKEN_MINTER_SERVICE_URL }}'
requested_permissions: |-
{
"scope": "release",
"repositories": ["${{ github.event.repository.name }}"],
"permissions": {
"contents": "write"
}
}
- name: 'Publish GitHub release'
env:
GH_TOKEN: '${{ steps.mint-token.outputs.token }}'
RELEASE_VERSION: 'v${{ needs.create-release.outputs.version }}'
REPO: '${{ github.repository }}'
run: |-
gh release edit "${RELEASE_VERSION}" \
--repo "${REPO}" \
--draft=false
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c' # ratchet:google-github-actions/auth@v2
with:
workload_identity_provider: '${{ vars.WIF_PROVIDER }}'
service_account: '${{ vars.WIF_SERVICE_ACCOUNT }}'
token_format: 'access_token'
- name: 'Authenticate to Artifact Registry'
uses: 'docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20' # ratchet:docker/login-action@v3
with:
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
registry: 'us-docker.pkg.dev'
- name: 'Copy images to Release registry'
env:
SOURCE_SERVER_IMAGE: '${{ env.IMAGE_NAME }}:${{ env.SOURCE_TAG }}'
TARGET_SERVER_IMAGE: '${{ env.IMAGE_NAME }}:v${{ needs.create-release.outputs.version }}'
run: |-
# GMA Server Images
gcloud container images add-tag --quiet ${{ env.SOURCE_DOCKER_REPO }}/${{ env.SOURCE_SERVER_IMAGE }}-amd64 ${{ env.TARGET_DOCKER_REPO }}/${{ env.TARGET_SERVER_IMAGE }}-amd64
gcloud container images add-tag --quiet ${{ env.SOURCE_DOCKER_REPO }}/${{ env.SOURCE_SERVER_IMAGE }}-arm64 ${{ env.TARGET_DOCKER_REPO }}/${{ env.TARGET_SERVER_IMAGE }}-arm64
cleanup-failed-release:
if: |-
${{ always() && needs.create-release.outputs.created == 'true' && contains(fromJSON('["failure", "cancelled", "skipped"]'), needs.publish-release.result) }}
runs-on: 'ubuntu-latest'
needs:
- 'create-release'
- 'publish-release'
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: 'Mint token'
id: 'mint-token'
uses: 'abcxyz/github-token-minter/.github/actions/mint-token@main' # ratchet:exclude
with:
wif_provider: '${{ vars.TOKEN_MINTER_WIF_PROVIDER }}'
wif_service_account: '${{ vars.TOKEN_MINTER_WIF_SERVICE_ACCOUNT }}'
service_audience: '${{ vars.TOKEN_MINTER_SERVICE_AUDIENCE }}'
service_url: '${{ vars.TOKEN_MINTER_SERVICE_URL }}'
requested_permissions: |-
{
"scope": "release",
"repositories": ["${{ github.event.repository.name }}"],
"permissions": {
"contents": "write"
}
}
- name: 'Cleanup failed release'
env:
GH_TOKEN: '${{ steps.mint-token.outputs.token }}'
RELEASE_VERSION: 'v${{ needs.create-release.outputs.version }}'
REPO: '${{ github.repository }}'
run: |-
gh release delete "${RELEASE_VERSION}" \
--repo "${REPO}" \
--cleanup-tag \
--yes || true