Add automated release workflow with validation

This commit introduces a comprehensive GitHub Actions workflow for
creating releases, ensuring all artifacts are validated before upload.

Changes:
- Add .github/workflows/release.yml: Manual workflow_dispatch trigger
  that builds, validates, and creates draft releases
- Update scripts/validate-release.sh: Add --skip-docker flag to allow
  validation without Docker image checks

Key features:
- Validation runs BEFORE any assets are uploaded
- If validation fails, no release is created
- checksums.txt and artifacts come from the same build
- No manual steps between validation and upload
- Checksums uploaded first, then all other assets
- Creates draft release for manual review before publishing

The workflow ensures that checksums.txt cannot drift from binaries
by running the entire build-validate-upload pipeline atomically.
This commit is contained in:
Claude 2025-11-11 09:22:03 +00:00
parent 4c4fd3a99b
commit e12980e351
No known key found for this signature in database
2 changed files with 213 additions and 33 deletions

150
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,150 @@
name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g., 4.28.1)'
required: true
type: string
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: |
# Install zip for Windows binaries
sudo apt-get update
sudo apt-get install -y zip
# Install Helm for chart packaging
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Build release artifacts
run: |
echo "Building release v${{ inputs.version }}..."
./scripts/build-release.sh ${{ inputs.version }}
- name: Validate release artifacts
run: |
echo "Validating release v${{ inputs.version }}..."
# Note: Docker image validation is skipped in this workflow
# since we're only validating the built artifacts (tarballs, binaries, checksums)
# Docker images should be validated separately before release
# Run validation with --skip-docker flag
./scripts/validate-release.sh ${{ inputs.version }} --skip-docker || {
echo "❌ Release validation failed!"
echo "The workflow will now fail. No release will be created."
exit 1
}
- name: Create draft release
id: create_release
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ inputs.version }}"
TAG="v${VERSION}"
echo "Creating draft release for ${TAG}..."
# Create draft release with placeholder body
gh release create "${TAG}" \
--draft \
--title "Pulse ${TAG}" \
--notes "Release ${TAG}
## What's Changed
<!-- TODO: Add release notes before publishing -->
## Installation
Quick install on Linux:
\`\`\`bash
curl -fsSL https://raw.githubusercontent.com/rcourtman/Pulse/main/install.sh | sudo bash
\`\`\`
Or download platform-specific archives below."
echo "release_url=$(gh release view ${TAG} --json url -q .url)" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- name: Upload checksums.txt first
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ steps.create_release.outputs.tag }}"
echo "Uploading checksums.txt..."
gh release upload "${TAG}" release/checksums.txt
# Also upload individual .sha256 files
echo "Uploading individual checksum files..."
gh release upload "${TAG}" release/*.sha256
- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ steps.create_release.outputs.tag }}"
echo "Uploading release assets..."
# Upload tarballs
gh release upload "${TAG}" release/*.tar.gz
# Upload Windows zip files
gh release upload "${TAG}" release/*.zip
# Upload Helm chart if it exists
if ls release/*.tgz 1> /dev/null 2>&1; then
echo "Uploading Helm chart..."
gh release upload "${TAG}" release/*.tgz
fi
# Upload install.sh
gh release upload "${TAG}" release/install.sh
# Upload standalone binaries (for direct download by installers)
echo "Uploading standalone binaries..."
gh release upload "${TAG}" release/pulse-sensor-proxy-*
gh release upload "${TAG}" release/pulse-host-agent-*
- name: Output release information
run: |
echo "✅ Release draft created successfully!"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Release: ${{ steps.create_release.outputs.tag }}"
echo "🔗 URL: ${{ steps.create_release.outputs.release_url }}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "⚠️ IMPORTANT: This release is in DRAFT status"
echo ""
echo "Next steps:"
echo "1. Review the release at the URL above"
echo "2. Update the release notes with changes since last release"
echo "3. Publish the release manually when ready"
echo ""
echo "All artifacts have been uploaded and validated."
echo "checksums.txt matches all uploaded binaries."
echo ""

View file

@ -3,9 +3,10 @@
# Pulse Release Validation Script # Pulse Release Validation Script
# Comprehensive artifact validation to prevent missing files/binaries in releases # Comprehensive artifact validation to prevent missing files/binaries in releases
# #
# Usage: ./scripts/validate-release.sh <pulse-version> [image] [release-dir] # Usage: ./scripts/validate-release.sh <pulse-version> [image] [release-dir] [--skip-docker]
# Example: ./scripts/validate-release.sh 4.26.2 # Example: ./scripts/validate-release.sh 4.26.2
# ./scripts/validate-release.sh 4.26.2 rcourtman/pulse:v4.26.2 release # ./scripts/validate-release.sh 4.26.2 rcourtman/pulse:v4.26.2 release
# ./scripts/validate-release.sh 4.26.2 --skip-docker
set -euo pipefail set -euo pipefail
@ -33,17 +34,41 @@ warn() {
} }
if [ $# -lt 1 ]; then if [ $# -lt 1 ]; then
error "Usage: $0 <pulse-version> [image] [release-dir]" error "Usage: $0 <pulse-version> [image] [release-dir] [--skip-docker]"
exit 1 exit 1
fi fi
PULSE_VERSION=$1 PULSE_VERSION=$1
PULSE_TAG="v${PULSE_VERSION}" PULSE_TAG="v${PULSE_VERSION}"
IMAGE=${2:-"rcourtman/pulse:${PULSE_TAG}"} SKIP_DOCKER=false
RELEASE_DIR=${3:-"release"}
# Parse arguments
shift
while [ $# -gt 0 ]; do
case "$1" in
--skip-docker)
SKIP_DOCKER=true
shift
;;
*)
if [ -z "${IMAGE:-}" ]; then
IMAGE="$1"
elif [ -z "${RELEASE_DIR:-}" ]; then
RELEASE_DIR="$1"
fi
shift
;;
esac
done
# Set defaults
IMAGE=${IMAGE:-"rcourtman/pulse:${PULSE_TAG}"}
RELEASE_DIR=${RELEASE_DIR:-"release"}
# Validate prerequisites # Validate prerequisites
command -v docker >/dev/null || { error "docker is required"; exit 1; } if [ "$SKIP_DOCKER" = false ]; then
command -v docker >/dev/null || { error "docker is required (use --skip-docker to skip Docker validation)"; exit 1; }
fi
[ -d "$RELEASE_DIR" ] || { error "release dir not found: $RELEASE_DIR"; exit 1; } [ -d "$RELEASE_DIR" ] || { error "release dir not found: $RELEASE_DIR"; exit 1; }
# Create temp directory for extractions # Create temp directory for extractions
@ -58,6 +83,7 @@ echo ""
#============================================================================= #=============================================================================
# DOCKER IMAGE VALIDATION # DOCKER IMAGE VALIDATION
#============================================================================= #=============================================================================
if [ "$SKIP_DOCKER" = false ]; then
info "=== Docker Image Validation ===" info "=== Docker Image Validation ==="
# Validate VERSION file in container # Validate VERSION file in container
@ -95,6 +121,10 @@ docker run --rm --entrypoint /bin/sh -e EXPECTED_TAG="$PULSE_TAG" "$IMAGE" -c 's
success "Docker agent version embedded: $PULSE_TAG" success "Docker agent version embedded: $PULSE_TAG"
echo "" echo ""
else
warn "=== Skipping Docker Image Validation (--skip-docker flag provided) ==="
echo ""
fi
#============================================================================= #=============================================================================
# RELEASE TARBALL VALIDATION # RELEASE TARBALL VALIDATION