Skip to content

Instantly share code, notes, and snippets.

@veechs
Last active April 7, 2025 09:54
Show Gist options
  • Save veechs/f6b3950d5c3637d67b483fbf96201ed0 to your computer and use it in GitHub Desktop.
Save veechs/f6b3950d5c3637d67b483fbf96201ed0 to your computer and use it in GitHub Desktop.
Super Easy WoW Addon Releases with GitHub Actions

Super Easy WoW Addon Releases with GitHub Actions

Trigger a new release by just updating your changelog. Includes automatic TOC version update, zip file named to match the TOC, and release notes pulled from the changelog.

Setup

⚠️ This assumes you already have a changelog. If not, you'll need to create one in the correct format (Bagshui's is a good example).

  1. In your GitHub repository, create a new custom workflow.
    Actions > New Workflow > Set up a workflow yourself…
  2. Name the file whatever you want.
    Mine is called CreateAddonReleaseFromChangelog.yml.
  3. Copy and paste the workflow configuration.
  4. Update the two lines below the # ❇️ Changelog file name comments to reflect the exact name of your addon's changlog (case-sensitive).
  5. If your default branch isn't named main, change the branches: section (marked with ✴️) near the top as appropriate.
  6. If your default branch is protected, create a deploy key and add it to your repository's Actions secrets as DEPLOY_KEY.
  7. Commit the workflow.

Triggering a release

All you need to do is update your changelog according to the keep-a-changelog format and push to the default branch, which will trigger the workflow.

Workflow Summary

When run, the action will:

  1. Determine whether the version has changed, and will only proceed if it's different from the most recent version tag on the repository.
  2. Update your TOC to match the version from the changelog
  3. Create a vX.Y.Z tag for the release.
  4. Zip your repository up
  5. Create the release, attach the zip file, and add release notes from the changelog.

Notes

  • This workflow assumes a flat structure with a single TOC file in the root of the repository.
  • The changelog heading must be ## X.Y.Z - YYYY-MM-DD (or ## [X.Y.Z] - YYYY-MM-DD if you prefer)
  • 1.5 will not trigger the workflow; it must be 1.5.0.
# This workflow creates a new release of a WoW addon when the changelog is
# updated with a new version. The repository contents are zipped up
# and added as a release asset.
#
# The changelog is expected to be in https://keepachangelog.com/
# format (brackets around version numbers are *not* required though).
#
# Just about everything should be determined automatically, but if you want
# to use this for your addon, the two places marked "❇️ Changelog file name"
# must be updated if they don't *exactly* match your changelog file.
#
name: Create WoW Addon Release From Changelog
on:
push:
branches:
# ✴️ Update if your default branch is called someting else.
- main
paths:
# ❇️ Changlog file name. Case must match.
- 'Changelog.md'
jobs:
create-release:
permissions:
contents: write
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
# Must check out repository to query the changelog.
- uses: actions/checkout@v4
# Required to override main branch protection.
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
# Get version and releaae notes.
- name: Query Changelog
id: changelog
uses: release-flow/keep-a-changelog-action@v2
with:
command: query
# ❇️ Changlog file name. Case must match.
changelog: Changelog.md
version: latest
# Figure out everything we need to know.
- name: Set Variables
id: vars
shell: bash
run: |
# In this section, it's not *necessary* to put things in Bash variables,
# then send to $GITHUB_OUTPUT at the end, but it makes things a little
# more readable.
# The TOC will decide the name of everything else.
# This is a little lazy, but *most* addons don't have multiple TOCs.
tocFile=$(shopt -s nullglob; ls -bt *.toc | head -n 1)
tocVersion=""
baseName=""
addonName=""
# Grab info from TOC.
if [[ "$tocFile" != "" ]]; then
addonName=$(perl -lne 'print "$1" and last if /^##\s*Title:\s*(.+?)\s*$/' "$tocFile")
tocVersion=$(perl -lne 'print "$1" and last if /^##\s*Version:\s*(.+?)\s*$/' "$tocFile")
baseName=$(basename "$tocFile" .toc)
fi
# Couldn't get TOC info, so use repository info.
if [[ "$addonName" == '' ]]; then
addonName=${GITHUB_REPOSITORY#$GITHUB_REPOSITORY_OWNER/}
baseName=$addonName
fi
zipFile=${baseName}.zip
currentChangelogVersion=${{ steps.changelog.outputs.version }}
# Alternate method of getting version from changelog. Would also require parsing release notes out.
#currentChangelogVersion=$(perl -lne 'print "$1" and last if /^##\s+\[(\d+\.\d+\.\d+)\]/;' Changelog.md)
lastReleaseTag=$(gh repo view --json latestRelease --jq '.[] | .tagName')
# These will be assigned to steps.vars.outputs.
echo "addonName=$addonName" >> $GITHUB_OUTPUT
# Version = X.Y.Z
echo "currentChangelogVersion=$currentChangelogVersion" >> $GITHUB_OUTPUT
# Tag = vX.Y.Z
echo "currentChangelogTag=v$currentChangelogVersion" >> $GITHUB_OUTPUT
echo "lastReleaseTag=$lastReleaseTag" >> $GITHUB_OUTPUT
echo "tocFile=$tocFile" >> $GITHUB_OUTPUT
echo "tocVersion=$tocVersion" >> $GITHUB_OUTPUT
echo "zipFile=$zipFile" >> $GITHUB_OUTPUT
# Decide whether we should do a release (and do some logging).
# Every step after this must have this condition so changelog updates that dont't contain a new version won't trigger a release:
# ```
# if: ${{ steps.check.outputs.proceed == 'true' }}
# ```
- name: Check Variables
id: check
shell: bash
run: |
# The rest of the workflow only needs to run if the changelog version is different.
proceed=${{ steps.vars.outputs.currentChangelogTag != steps.vars.outputs.lastReleaseTag }}
echo "proceed=$proceed" >> $GITHUB_OUTPUT
# Logging.
echo "addonName: ${{ steps.vars.outputs.addonName }}"
echo "currentChangelogVersion: ${{ steps.vars.outputs.currentChangelogVersion }}"
echo "currentChangelogTag: ${{ steps.vars.outputs.currentChangelogTag }}"
echo "lastReleaseTag: ${{ steps.vars.outputs.lastReleaseTag }}"
echo "tocFile: ${{ steps.vars.outputs.tocFile }}"
echo "tocVersion: ${{ steps.vars.outputs.tocVersion }}"
echo "zipFile: ${{ steps.vars.outputs.zipFile }}"
echo "toc hash: ${{ hashFiles(steps.vars.outputs.tocFile) }}"
echo "🚦proceed: $proceed"
# TOC needs a version update.
- name: Update TOC
if: ${{
steps.check.outputs.proceed == 'true'
&& hashFiles(steps.vars.outputs.tocFile) != ''
&& steps.vars.outputs.tocVersion != steps.vars.outputs.currentChangelogVersion
}}
shell: bash
run: |
sed -i 's/^\(##[[:space:]]*Version:\).*/\1 ${{ steps.vars.outputs.currentChangelogVersion }}/' ${{ steps.vars.outputs.tocFile }}
git config --global user.name '${{ github.actor }}'
git config --global user.email '${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com'
git commit -am '${{ steps.vars.outputs.addonName }} ${{ steps.vars.outputs.currentChangelogVersion }}'
git push
# This is the vX.Y.Z tag the release will be created from.
- name: Create Tag
if: ${{ steps.check.outputs.proceed == 'true' }}
shell: bash
run: |
git config --global user.name '${{ github.actor }}'
git config --global user.email '${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com'
git tag -a "${{ steps.vars.outputs.currentChangelogTag }}" -m ":bookmark: ${{ steps.vars.outputs.currentChangelogVersion }}"
git push origin ${{ steps.vars.outputs.currentChangelogTag }}
# This is our release asset.
- name: Create Zip
if: ${{ steps.check.outputs.proceed == 'true' }}
id: create-zip
uses: thedoctor0/[email protected]
with:
type: 'zip'
filename: '${{ steps.vars.outputs.zipFile }}'
exclusions: '*.git* .vscode .editorconfig'
# Release it!
- name: Create Release
if: ${{ steps.check.outputs.proceed == 'true' }}
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.changelog.outputs.release-notes }}
files: ${{ steps.vars.outputs.zipFile }}
tag_name: ${{ steps.vars.outputs.currentChangelogTag }}
fail_on_unmatched_files: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment