Files
devcontainer/.github/workflows/release-unified.yaml
T
DevContainer User da40d57e07 fix: overhaul release pipeline — 5 issues resolved
1. version input now optional — auto-increment from release_type works
2. replaced deprecated actions/create-release@v1 with gh release create
3. race condition fixed — release commit uses [skip ci], removed fragile
   github.actor guard from build-and-push.yaml
4. simplified gh-pages publishing — uses clean temp dir + shallow clone
   instead of convoluted git worktree fallback
5. version parsing strips pre-release suffixes (e.g., 2.0.0-dev → 2.0.0)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-25 13:53:58 +00:00

190 lines
6.7 KiB
YAML

name: Unified Release
on:
workflow_dispatch:
inputs:
version:
description: 'Explicit version (e.g., 1.2.3). Leave blank to auto-increment.'
required: false
type: string
release_type:
description: 'Release type (used when version is blank)'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Determine Version
id: version
run: |
INPUT_VERSION="${{ github.event.inputs.version }}"
if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else
# Auto-increment based on release_type
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
# Strip any pre-release suffix (e.g., 2.0.0-dev -> 2.0.0)
CURRENT=$(echo "$CURRENT" | sed 's/-.*//')
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
MINOR=$(echo "$CURRENT" | cut -d. -f2)
PATCH=$(echo "$CURRENT" | cut -d. -f3)
case "${{ github.event.inputs.release_type }}" in
major) VERSION="$((MAJOR + 1)).0.0" ;;
minor) VERSION="${MAJOR}.$((MINOR + 1)).0" ;;
patch) VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" ;;
esac
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
echo "Releasing version ${VERSION}"
- name: Update Chart Version
run: |
sed -i "s/^version: .*/version: ${{ steps.version.outputs.version }}/" chart/Chart.yaml
git add chart/Chart.yaml
git diff --quiet --staged || git commit -m "chore(release): ${{ steps.version.outputs.version }} [skip ci]"
- name: Create and Push Tag
run: |
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
git push origin main
git push origin "${{ steps.version.outputs.tag }}"
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Publish Helm Chart to GitHub Pages
run: |
helm package chart/
CHART_TGZ="devcontainer-${{ steps.version.outputs.version }}.tgz"
# Set up gh-pages in a temporary directory
PAGES_DIR=$(mktemp -d)
if git ls-remote --heads origin gh-pages | grep -q gh-pages; then
# gh-pages exists — shallow clone just that branch
git clone --single-branch --branch gh-pages \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
"$PAGES_DIR"
else
# First time — initialize gh-pages
git init "$PAGES_DIR"
git -C "$PAGES_DIR" checkout --orphan gh-pages
git -C "$PAGES_DIR" remote add origin \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
cat > "$PAGES_DIR/index.html" <<'HTMLEOF'
<!DOCTYPE html>
<html>
<head><title>Dev Container Helm Chart Repository</title></head>
<body>
<h1>Dev Container Helm Chart Repository</h1>
<p>Add this repository to Helm:</p>
<pre>helm repo add devcontainer https://cpfarhood.github.io/devcontainer</pre>
<p>Install the chart:</p>
<pre>helm install mydev devcontainer/devcontainer --set name=mydev</pre>
</body>
</html>
HTMLEOF
fi
git -C "$PAGES_DIR" config user.name "github-actions[bot]"
git -C "$PAGES_DIR" config user.email "github-actions[bot]@users.noreply.github.com"
# Copy chart package and rebuild index
cp "$CHART_TGZ" "$PAGES_DIR/"
if [ -f "$PAGES_DIR/index.yaml" ]; then
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer --merge "$PAGES_DIR/index.yaml"
else
helm repo index "$PAGES_DIR" --url https://cpfarhood.github.io/devcontainer
fi
# Commit and push
git -C "$PAGES_DIR" add .
git -C "$PAGES_DIR" commit -m "Publish chart ${{ steps.version.outputs.version }}"
git -C "$PAGES_DIR" push origin gh-pages
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Build release notes
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD)
else
COMMITS=$(git log --pretty=format:"- %s (%h)" "${PREV_TAG}..HEAD")
fi
cat > release-notes.md <<EOF
## Release ${{ steps.version.outputs.version }}
### Changes
${COMMITS}
### Docker Image
\`\`\`bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
\`\`\`
### Helm Chart
\`\`\`bash
helm repo add devcontainer https://cpfarhood.github.io/devcontainer
helm repo update
helm install mydev devcontainer/devcontainer --version ${{ steps.version.outputs.version }} --set name=mydev
\`\`\`
EOF
gh release create "${{ steps.version.outputs.tag }}" \
--title "Release ${{ steps.version.outputs.tag }}" \
--notes-file release-notes.md