Release workflow (gnmyt/MySpeed)
The Release workflow from gnmyt/MySpeed, 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 Release workflow from the gnmyt/MySpeed repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Release
on:
push:
tags: ["v*"]
jobs:
create-release:
name: "Create Release"
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
release_id: ${{ steps.create_release.outputs.id }}
steps:
- name: Checkout project
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from tag
id: get_version
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref }}
name: Release ${{ steps.get_version.outputs.version }}
draft: true
prerelease: false
build-binaries:
name: "Build Binaries"
needs: create-release
uses: ./.github/workflows/build-binaries.yml
with:
version: ${{ needs.create-release.outputs.version }}
release_id: ${{ needs.create-release.outputs.release_id }}
secrets: inherit
build-docker:
name: "Build Docker"
needs: create-release
uses: ./.github/workflows/build-docker.yml
with:
version: ${{ needs.create-release.outputs.version }}
secrets: inherit
build-msi:
name: "Build MSI"
needs: [create-release, build-binaries]
uses: ./.github/workflows/build-msi.yml
with:
version: ${{ needs.create-release.outputs.version }}
release_id: ${{ needs.create-release.outputs.release_id }}
secrets: inherit
finalize-release:
name: "Finalize Release"
needs: [create-release, build-binaries, build-docker, build-msi]
uses: ./.github/workflows/finalize-release.yml
with:
version: ${{ needs.create-release.outputs.version }}
release_id: ${{ needs.create-release.outputs.release_id }}
secrets: inherit
cleanup-on-failure:
name: "Cleanup on Failure"
runs-on: ubuntu-latest
needs: [create-release, build-binaries, build-docker, build-msi]
if: failure()
steps:
- name: Delete release
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.repos.deleteRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: ${{ needs.create-release.outputs.release_id }}
});
console.log('Release deleted');
} catch (e) {
console.log('Failed to delete release:', e.message);
}
- name: Delete tag
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/${{ github.ref_name }}'
});
console.log('Tag deleted');
} catch (e) {
console.log('Failed to delete tag:', e.message);
}The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Release on: push: tags: ["v*"] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: create-release: timeout-minutes: 30 name: "Create Release" runs-on: latchkey-small outputs: version: ${{ steps.get_version.outputs.version }} release_id: ${{ steps.create_release.outputs.id }} steps: - name: Checkout project uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get version from tag id: get_version run: | VERSION="${GITHUB_REF#refs/tags/v}" echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Create GitHub Release id: create_release uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.ref }} name: Release ${{ steps.get_version.outputs.version }} draft: true prerelease: false build-binaries: timeout-minutes: 30 name: "Build Binaries" needs: create-release uses: ./.github/workflows/build-binaries.yml with: version: ${{ needs.create-release.outputs.version }} release_id: ${{ needs.create-release.outputs.release_id }} secrets: inherit build-docker: timeout-minutes: 30 name: "Build Docker" needs: create-release uses: ./.github/workflows/build-docker.yml with: version: ${{ needs.create-release.outputs.version }} secrets: inherit build-msi: timeout-minutes: 30 name: "Build MSI" needs: [create-release, build-binaries] uses: ./.github/workflows/build-msi.yml with: version: ${{ needs.create-release.outputs.version }} release_id: ${{ needs.create-release.outputs.release_id }} secrets: inherit finalize-release: timeout-minutes: 30 name: "Finalize Release" needs: [create-release, build-binaries, build-docker, build-msi] uses: ./.github/workflows/finalize-release.yml with: version: ${{ needs.create-release.outputs.version }} release_id: ${{ needs.create-release.outputs.release_id }} secrets: inherit cleanup-on-failure: timeout-minutes: 30 name: "Cleanup on Failure" runs-on: latchkey-small needs: [create-release, build-binaries, build-docker, build-msi] if: failure() steps: - name: Delete release uses: actions/github-script@v7 with: script: | try { await github.rest.repos.deleteRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: ${{ needs.create-release.outputs.release_id }} }); console.log('Release deleted'); } catch (e) { console.log('Failed to delete release:', e.message); } - name: Delete tag uses: actions/github-script@v7 with: script: | try { await github.rest.git.deleteRef({ owner: context.repo.owner, repo: context.repo.repo, ref: 'tags/${{ github.ref_name }}' }); console.log('Tag deleted'); } catch (e) { console.log('Failed to delete tag:', e.message); }
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.
1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
This workflow runs 6 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.