1.Release workflow (Project-HAMi/HAMi)
The 1.Release workflow from Project-HAMi/HAMi, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the 1.Release workflow from the Project-HAMi/HAMi repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: 1.Release
# it is trigger by tag event:
# 1 build the release image, push images to ghcr.io, and build image like: ghcr.io/xxxx:v1.0.0
# 2 package the chart package, update index.yaml and commit to '/charts' of branch 'github_pages' ( PR with label pr/release/robot_update_githubpage )
# 3 create changelog file, commit to '/changelogs' of branch 'github_pages' for githubPage ( PR with label pr/release/robot_update_githubpage )
# 4 commit '/docs' to '/docs' of branch 'github_pages'
# 5 create a release , attached with the chart and changelog
on:
release:
types:
- prereleased
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
inputs:
tag:
description: 'Tag'
required: true
default: v1.0.0
permissions: write-all
jobs:
pre-release:
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
tag: ${{ env.RUN_TAG }}
steps:
- name: Check Version
run: |
TagVersion="${{ env.RUN_TAG }}"
RecordVersion=` cat VERSION | tr -d ' ' | tr -d '\n' `
if [ "$RecordVersion" != "$TagVersion" ] ; then
echo "error, version $RecordVersion of '/VERSION' is different with Tag $TagVersion "
exit 1
fi
#no need to check chart version, which will auto update to /VERSION by CI
# generate release notes for the new release branch
- name: Generate pre release notes
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
prerelease: true
generate_release_notes: true
ensure-tag:
runs-on: ubuntu-latest
needs: pre-release
outputs:
tag: ${{ env.RUN_TAG }}
steps:
- name: Free disk space
# https://github.com/actions/virtual-environments/issues/709
run: |
echo "=========original CI disk space"
df -h
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
echo "=========after clean up, the left CI disk space"
df -h
- name: Get Ref
id: get_ref
run: |
if ${{ github.event_name == 'workflow_dispatch' }} ; then
echo "call by self workflow_dispatch"
echo "RUN_TAG=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
YBranchName=` grep -Eo "v[0-9]+\.[0-9]+" <<< "${{ github.event.inputs.tag }}" `
elif ${{ github.event_name == 'push' }} ; then
echo "call by push tag"
echo "RUN_TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV
YBranchName=` grep -Eo "v[0-9]+\.[0-9]+" <<< "${GITHUB_REF##*/}" `
else
echo "unexpected event: ${{ github.event_name }}"
exit 1
fi
echo "YBranchName=${YBranchName}"
if [ -n "$YBranchName" ] ; then
echo "RUN_YBranchName=${YBranchName}" >> $GITHUB_ENV
else
echo "error, failed to find y branch"
exit 1
fi
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ env.RUN_TAG }}
# if branch exists, the action will no fail, and it output created=false
- name: release Y branch
uses: peterjgrainger/action-create-branch@v4.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
branch: 'release-${{ env.RUN_YBranchName }}'
sha: '${{ github.sha }}'
release-image-hamicore:
needs: ensure-tag
uses: ./.github/workflows/call-release-image-hamicore.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
secrets: inherit
release-image:
needs: [ensure-tag,release-image-hamicore]
uses: ./.github/workflows/call-release-image.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
secrets: inherit
release-chart:
needs: [ensure-tag]
uses: ./.github/workflows/call-release-helm.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
submit: true
secrets: inherit
# generate changelog and update to github releases pages
release-notes:
needs: [release-chart,release-image]
uses: ./.github/workflows/call-release-notes.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
release-website:
needs: [release-notes]
uses: ./.github/workflows/call-release-website.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
# excute a full e2e test when hami release
release-e2e:
needs: [release-notes]
uses: ./.github/workflows/call-e2e.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
type: "release"
# excute a compatibility test when hami release
release-e2e-upgrade:
needs: [release-notes]
uses: ./.github/workflows/call-e2e-upgrade.yaml
with:
ref: ${{ needs.ensure-tag.outputs.tag }}
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: 1.Release # it is trigger by tag event: # 1 build the release image, push images to ghcr.io, and build image like: ghcr.io/xxxx:v1.0.0 # 2 package the chart package, update index.yaml and commit to '/charts' of branch 'github_pages' ( PR with label pr/release/robot_update_githubpage ) # 3 create changelog file, commit to '/changelogs' of branch 'github_pages' for githubPage ( PR with label pr/release/robot_update_githubpage ) # 4 commit '/docs' to '/docs' of branch 'github_pages' # 5 create a release , attached with the chart and changelog on: release: types: - prereleased push: tags: - v[0-9]+.[0-9]+.[0-9]+ - v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+ workflow_dispatch: inputs: tag: description: 'Tag' required: true default: v1.0.0 permissions: write-all concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: pre-release: timeout-minutes: 30 runs-on: latchkey-small permissions: contents: write outputs: tag: ${{ env.RUN_TAG }} steps: - name: Check Version run: | TagVersion="${{ env.RUN_TAG }}" RecordVersion=` cat VERSION | tr -d ' ' | tr -d '\n' ` if [ "$RecordVersion" != "$TagVersion" ] ; then echo "error, version $RecordVersion of '/VERSION' is different with Tag $TagVersion " exit 1 fi #no need to check chart version, which will auto update to /VERSION by CI # generate release notes for the new release branch - name: Generate pre release notes if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: prerelease: true generate_release_notes: true ensure-tag: timeout-minutes: 30 runs-on: latchkey-small needs: pre-release outputs: tag: ${{ env.RUN_TAG }} steps: - name: Free disk space # https://github.com/actions/virtual-environments/issues/709 run: | echo "=========original CI disk space" df -h sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" echo "=========after clean up, the left CI disk space" df -h - name: Get Ref id: get_ref run: | if ${{ github.event_name == 'workflow_dispatch' }} ; then echo "call by self workflow_dispatch" echo "RUN_TAG=${{ github.event.inputs.tag }}" >> $GITHUB_ENV YBranchName=` grep -Eo "v[0-9]+\.[0-9]+" <<< "${{ github.event.inputs.tag }}" ` elif ${{ github.event_name == 'push' }} ; then echo "call by push tag" echo "RUN_TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV YBranchName=` grep -Eo "v[0-9]+\.[0-9]+" <<< "${GITHUB_REF##*/}" ` else echo "unexpected event: ${{ github.event_name }}" exit 1 fi echo "YBranchName=${YBranchName}" if [ -n "$YBranchName" ] ; then echo "RUN_YBranchName=${YBranchName}" >> $GITHUB_ENV else echo "error, failed to find y branch" exit 1 fi - name: Checkout uses: actions/checkout@v7 with: ref: ${{ env.RUN_TAG }} # if branch exists, the action will no fail, and it output created=false - name: release Y branch uses: peterjgrainger/action-create-branch@v4.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: branch: 'release-${{ env.RUN_YBranchName }}' sha: '${{ github.sha }}' release-image-hamicore: timeout-minutes: 30 needs: ensure-tag uses: ./.github/workflows/call-release-image-hamicore.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} secrets: inherit release-image: timeout-minutes: 30 needs: [ensure-tag,release-image-hamicore] uses: ./.github/workflows/call-release-image.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} secrets: inherit release-chart: timeout-minutes: 30 needs: [ensure-tag] uses: ./.github/workflows/call-release-helm.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} submit: true secrets: inherit # generate changelog and update to github releases pages release-notes: timeout-minutes: 30 needs: [release-chart,release-image] uses: ./.github/workflows/call-release-notes.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} release-website: timeout-minutes: 30 needs: [release-notes] uses: ./.github/workflows/call-release-website.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} # excute a full e2e test when hami release release-e2e: timeout-minutes: 30 needs: [release-notes] uses: ./.github/workflows/call-e2e.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }} type: "release" # excute a compatibility test when hami release release-e2e-upgrade: timeout-minutes: 30 needs: [release-notes] uses: ./.github/workflows/call-e2e-upgrade.yaml with: ref: ${{ needs.ensure-tag.outputs.tag }}
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- End-to-end and browser tests
This workflow runs 9 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.