Skip to content

Release

Release #20

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'New version number (e.g., 1.6.0)'
required: true
type: string
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate version format
run: |
if ! echo "${{ inputs.version }}" | grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?$'; then
echo "Error: Version must be in format X.Y or X.Y.Z"
exit 1
fi
- name: Check if tag already exists
run: |
if git rev-parse "v${{ inputs.version }}" >/dev/null 2>&1; then
echo "Error: Tag v${{ inputs.version }} already exists"
exit 1
fi
- name: Update version in app.py
run: |
sed -i 's/APP_VERSION = "[^"]*"/APP_VERSION = "${{ inputs.version }}"/' app.py
grep "APP_VERSION" app.py
- name: Update version in sw.js
run: |
sed -i "s/const CACHE_NAME = 'chemediaho-v[^']*'/const CACHE_NAME = 'chemediaho-v${{ inputs.version }}'/" frontend/sw.js
grep "CACHE_NAME" frontend/sw.js
- name: Get last release tag
id: last_release
run: |
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)
echo "last_tag=$LAST_TAG" >> $GITHUB_OUTPUT
- name: Generate changelog
id: changelog
uses: actions/github-script@v7
with:
script: |
const lastTag = '${{ steps.last_release.outputs.last_tag }}';
const newVersion = '${{ inputs.version }}';
// get all merged PRs since last release
const commits = await github.rest.repos.compareCommits({
owner: context.repo.owner,
repo: context.repo.repo,
base: lastTag,
head: 'main'
});
// extract PR numbers from commit messages
const prNumbers = new Set();
for (const commit of commits.data.commits) {
const match = commit.commit.message.match(/#(\d+)/);
if (match) {
prNumbers.add(parseInt(match[1]));
}
}
// fetch pr details
const features = [];
const fixes = [];
const breakings = [];
for (const prNumber of prNumbers) {
try {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
if (pr.merged_at) {
const hasSkipLabel = pr.labels.some(l => l.name === 'changelog:skip');
const hasFeatureLabel = pr.labels.some(l => l.name === 'changelog:feat');
const hasFixLabel = pr.labels.some(l => l.name === 'changelog:fix');
const hasBreakingLabel = pr.labels.some(l => l.name === 'changelog:breaking');
// skip prs with changelog:skip label
if (hasSkipLabel) {
console.log(`Skipping PR #${prNumber} (has changelog:skip label)`);
continue;
}
const prEntry = `* ${pr.title} by @${pr.user.login} in https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${prNumber}`;
if (hasBreakingLabel) {
breakings.push(prEntry);
} else if (hasFeatureLabel) {
features.push(prEntry);
} else if (hasFixLabel) {
fixes.push(prEntry);
}
}
} catch (error) {
console.log(`Could not fetch PR #${prNumber}: ${error.message}`);
}
}
// build changelog
let changelog = `# che media ho? - ${newVersion}\n`;
changelog += `## What's Changed\n`;
if (breakings.length > 0) {
changelog += `### BREAKING CHANGES\n`;
changelog += breakings.join('\n') + '\n\n';
}
if (features.length > 0) {
changelog += `### Feats\n`;
changelog += features.join('\n') + '\n\n';
}
if (fixes.length > 0) {
changelog += `### Fixes\n`;
changelog += fixes.join('\n') + '\n\n';
}
if (features.length === 0 && fixes.length === 0 && breakings.length === 0) {
changelog += `### Changes\n`;
changelog += `* Version bump to ${newVersion}\n\n`;
}
changelog += `\n\n**Full Changelog**: https://github.com/${context.repo.owner}/${context.repo.repo}/compare/${lastTag}...v${newVersion}`;
core.setOutput('changelog', changelog);
// write changelog to file
const fs = require('fs');
fs.writeFileSync('/tmp/changelog.md', changelog);
console.log('Generated changelog:');
console.log(changelog);
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add app.py frontend/sw.js
git commit -m "chore: bump version to ${{ inputs.version }}"
git push origin main
- name: Create and push tag
run: |
git tag -a "v${{ inputs.version }}" -m "Release v${{ inputs.version }}"
git push origin "v${{ inputs.version }}"
- name: Create GitHub Release
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const changelog = fs.readFileSync('/tmp/changelog.md', 'utf8');
await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: 'v${{ inputs.version }}',
name: '${{ inputs.version }}',
body: changelog
});
# ---------------------------
# DOCKER HUB DEPLOY
# ---------------------------
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push multi-arch Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: |
gablilli/chemediaho:${{ inputs.version }}
gablilli/chemediaho:latest