mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-30 04:20:20 +00:00
Rename workflow file to force new workflow_id
This commit is contained in:
parent
0e49b11b35
commit
9dbe9f900d
1 changed files with 0 additions and 0 deletions
487
.github/workflows/create-release.yml
vendored
Normal file
487
.github/workflows/create-release.yml
vendored
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
name: Pulse Release Pipeline
|
||||
# Triggers: workflow_dispatch only
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version number (e.g., 4.30.0)'
|
||||
required: true
|
||||
type: string
|
||||
release_notes:
|
||||
description: 'Release notes (markdown) - generated by Claude'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
extract-version:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
version: ${{ steps.extract.outputs.version }}
|
||||
tag: ${{ steps.extract.outputs.tag }}
|
||||
steps:
|
||||
- name: Extract version
|
||||
id: extract
|
||||
run: |
|
||||
# Handle both tag push and workflow_dispatch
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${TAG#v}"
|
||||
else
|
||||
VERSION="${{ inputs.version }}"
|
||||
TAG="v${VERSION}"
|
||||
fi
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "Version: ${VERSION}, Tag: ${TAG}"
|
||||
|
||||
version-guard:
|
||||
needs: extract-version
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure VERSION file matches requested version
|
||||
run: |
|
||||
FILE_VERSION=$(cat VERSION | tr -d '\n')
|
||||
REQUESTED_VERSION="${{ needs.extract-version.outputs.version }}"
|
||||
if [ "$FILE_VERSION" != "$REQUESTED_VERSION" ]; then
|
||||
echo "::error::VERSION file ($FILE_VERSION) does not match requested version ($REQUESTED_VERSION)."
|
||||
echo "The VERSION file must be updated and committed before running release."
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ VERSION file matches requested version ($REQUESTED_VERSION)"
|
||||
|
||||
preflight-tests:
|
||||
needs: version-guard
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'frontend-modern/package-lock.json'
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: npm --prefix frontend-modern ci
|
||||
|
||||
- name: Build frontend bundle for Go embed
|
||||
run: |
|
||||
npm --prefix frontend-modern run build
|
||||
rm -rf internal/api/frontend-modern
|
||||
mkdir -p internal/api/frontend-modern
|
||||
cp -r frontend-modern/dist internal/api/frontend-modern/
|
||||
|
||||
- name: Lint frontend
|
||||
run: npm --prefix frontend-modern run lint
|
||||
|
||||
- name: Install docker-compose
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-compose
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache: true
|
||||
|
||||
- name: Run backend tests
|
||||
run: go test ./...
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ hashFiles('tests/integration/package-lock.json') }}
|
||||
|
||||
- name: Prepare integration test dependencies
|
||||
working-directory: tests/integration
|
||||
run: |
|
||||
npm ci
|
||||
npx playwright install --with-deps chromium
|
||||
|
||||
- name: Build Pulse for integration tests
|
||||
run: make build
|
||||
|
||||
- name: Build Docker images for integration tests
|
||||
run: |
|
||||
docker build -t pulse-mock-github:test tests/integration/mock-github-server
|
||||
docker build -t pulse:test -f Dockerfile .
|
||||
|
||||
- name: Run update integration smoke tests
|
||||
working-directory: tests/integration
|
||||
env:
|
||||
MOCK_CHECKSUM_ERROR: "false"
|
||||
MOCK_NETWORK_ERROR: "false"
|
||||
MOCK_RATE_LIMIT: "false"
|
||||
MOCK_STALE_RELEASE: "false"
|
||||
run: |
|
||||
docker-compose -f docker-compose.test.yml up -d
|
||||
|
||||
# Wait for services to be healthy
|
||||
echo "Waiting for mock-github to be healthy..."
|
||||
timeout 60 sh -c 'until docker inspect --format="{{json .State.Health.Status}}" pulse-mock-github | grep -q "healthy"; do sleep 2; done' || {
|
||||
echo "Mock GitHub failed to become healthy"
|
||||
docker logs pulse-mock-github
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Waiting for pulse-test-server to be healthy..."
|
||||
timeout 60 sh -c 'until docker inspect --format="{{json .State.Health.Status}}" pulse-test-server | grep -q "healthy"; do sleep 2; done' || {
|
||||
echo "Pulse server failed to become healthy"
|
||||
docker logs pulse-test-server
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "All services healthy, verifying port mapping..."
|
||||
# Test that the host can actually reach the container through port mapping
|
||||
for i in 1 2 3 4 5; do
|
||||
if curl -f -s http://localhost:7655/api/health > /dev/null 2>&1; then
|
||||
echo "Port mapping verified: Pulse server is reachable from host"
|
||||
break
|
||||
elif [ $i -eq 5 ]; then
|
||||
echo "ERROR: Port mapping failed - cannot reach Pulse server from host"
|
||||
echo "Container healthcheck passed, but host cannot connect via localhost:7655"
|
||||
echo "Pulse server logs:"
|
||||
docker logs pulse-test-server || true
|
||||
echo "Mock GitHub logs:"
|
||||
docker logs pulse-mock-github || true
|
||||
exit 1
|
||||
else
|
||||
echo "Attempt $i: Server not yet reachable from host, waiting..."
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Running API-level update integration test..."
|
||||
UPDATE_API_BASE_URL=http://localhost:7655 go test ../../tests/integration/api -run TestUpdateFlowIntegration -count=1
|
||||
|
||||
echo "Skipping legacy Playwright update scenarios (removed until they can be rebuilt)"
|
||||
docker-compose -f docker-compose.test.yml down -v
|
||||
|
||||
- name: Cleanup integration environment
|
||||
if: always()
|
||||
working-directory: tests/integration
|
||||
run: docker-compose -f docker-compose.test.yml down -v || true
|
||||
|
||||
build-docker-images:
|
||||
needs:
|
||||
- extract-version
|
||||
- version-guard
|
||||
- preflight-tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Pulse server image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
provenance: false
|
||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse:buildcache
|
||||
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse:buildcache,mode=max
|
||||
tags: |
|
||||
rcourtman/pulse:${{ needs.extract-version.outputs.tag }}
|
||||
rcourtman/pulse:${{ needs.extract-version.outputs.version }}
|
||||
rcourtman/pulse:latest
|
||||
ghcr.io/${{ github.repository_owner }}/pulse:${{ needs.extract-version.outputs.tag }}
|
||||
ghcr.io/${{ github.repository_owner }}/pulse:${{ needs.extract-version.outputs.version }}
|
||||
ghcr.io/${{ github.repository_owner }}/pulse:latest
|
||||
labels: |
|
||||
org.opencontainers.image.title=Pulse
|
||||
org.opencontainers.image.description=Proxmox monitoring system
|
||||
org.opencontainers.image.version=${{ needs.extract-version.outputs.tag }}
|
||||
org.opencontainers.image.created=${{ github.event.repository.updated_at }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.licenses=MIT
|
||||
|
||||
- name: Build and push Docker agent image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
target: agent_runtime
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
provenance: false
|
||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:buildcache
|
||||
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:buildcache,mode=max
|
||||
tags: |
|
||||
rcourtman/pulse-docker-agent:${{ needs.extract-version.outputs.tag }}
|
||||
rcourtman/pulse-docker-agent:${{ needs.extract-version.outputs.version }}
|
||||
rcourtman/pulse-docker-agent:latest
|
||||
ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:${{ needs.extract-version.outputs.tag }}
|
||||
ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:${{ needs.extract-version.outputs.version }}
|
||||
ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:latest
|
||||
labels: |
|
||||
org.opencontainers.image.title=Pulse Docker Agent
|
||||
org.opencontainers.image.description=Docker container monitoring agent for Pulse
|
||||
org.opencontainers.image.version=${{ needs.extract-version.outputs.tag }}
|
||||
org.opencontainers.image.created=${{ github.event.repository.updated_at }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.licenses=MIT
|
||||
|
||||
- name: Output Docker image information
|
||||
run: |
|
||||
echo "✅ Docker images built and pushed successfully!"
|
||||
echo ""
|
||||
echo "Server images:"
|
||||
echo " - rcourtman/pulse:${{ needs.extract-version.outputs.tag }}"
|
||||
echo " - rcourtman/pulse:${{ needs.extract-version.outputs.version }}"
|
||||
echo " - rcourtman/pulse:latest"
|
||||
echo ""
|
||||
echo "Agent images:"
|
||||
echo " - rcourtman/pulse-docker-agent:${{ needs.extract-version.outputs.tag }}"
|
||||
echo " - rcourtman/pulse-docker-agent:${{ needs.extract-version.outputs.version }}"
|
||||
echo " - rcourtman/pulse-docker-agent:latest"
|
||||
|
||||
create-release:
|
||||
needs:
|
||||
- extract-version
|
||||
- build-docker-images
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
release_id: ${{ steps.create_release.outputs.release_id }}
|
||||
release_url: ${{ steps.create_release.outputs.release_url }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for changelog generation
|
||||
|
||||
- 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 ${{ needs.extract-version.outputs.tag }}..."
|
||||
./scripts/build-release.sh ${{ needs.extract-version.outputs.version }}
|
||||
|
||||
- name: Prepare release notes
|
||||
id: generate_notes
|
||||
env:
|
||||
RELEASE_NOTES_INPUT: ${{ inputs.release_notes }}
|
||||
run: |
|
||||
VERSION="${{ needs.extract-version.outputs.version }}"
|
||||
|
||||
# Save release notes to file
|
||||
NOTES_FILE=$(mktemp)
|
||||
|
||||
if [ -n "$RELEASE_NOTES_INPUT" ]; then
|
||||
echo "Using Claude-generated release notes from workflow input"
|
||||
printf "%s\n" "$RELEASE_NOTES_INPUT" > "$NOTES_FILE"
|
||||
else
|
||||
echo "Tag-triggered release - using placeholder notes"
|
||||
echo "Release $VERSION" > "$NOTES_FILE"
|
||||
echo "" >> "$NOTES_FILE"
|
||||
echo "See commit history for changes." >> "$NOTES_FILE"
|
||||
fi
|
||||
|
||||
# Add installation instructions
|
||||
cat >> "$NOTES_FILE" << EOF
|
||||
|
||||
## Installation
|
||||
|
||||
**Docker (recommended):**
|
||||
\`\`\`bash
|
||||
docker pull rcourtman/pulse:${VERSION}
|
||||
\`\`\`
|
||||
|
||||
**Docker Compose:**
|
||||
Update your \`docker-compose.yml\` to use \`rcourtman/pulse:${VERSION}\`
|
||||
|
||||
See the [Installation Guide](https://github.com/rcourtman/Pulse#installation) for complete setup instructions.
|
||||
EOF
|
||||
|
||||
echo "notes_file=${NOTES_FILE}" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "Release notes content:"
|
||||
cat "$NOTES_FILE"
|
||||
|
||||
- name: Create draft release
|
||||
id: create_release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="${{ needs.extract-version.outputs.tag }}"
|
||||
NOTES_FILE="${{ steps.generate_notes.outputs.notes_file }}"
|
||||
|
||||
echo "Creating draft release for ${TAG}..."
|
||||
|
||||
# Tag already exists (pushed by user), so don't create it
|
||||
# Just create the release pointing to the existing tag
|
||||
gh release create "${TAG}" \
|
||||
--draft \
|
||||
--title "Pulse ${TAG}" \
|
||||
--notes-file "$NOTES_FILE"
|
||||
|
||||
rm -f "$NOTES_FILE"
|
||||
|
||||
# Get the release ID
|
||||
echo "Waiting for release to appear in GitHub API..."
|
||||
MAX_ATTEMPTS=10
|
||||
ATTEMPT=1
|
||||
RELEASE_JSON=""
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Looking for release ${TAG}..."
|
||||
RELEASE_JSON=$(gh api "repos/${{ github.repository }}/releases" --paginate | jq ".[] | select(.tag_name == \"${TAG}\")")
|
||||
|
||||
if [ -n "$RELEASE_JSON" ]; then
|
||||
echo "✓ Found release in API"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then
|
||||
echo "Release not indexed yet, waiting 2 seconds..."
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
done
|
||||
|
||||
if [ -z "$RELEASE_JSON" ]; then
|
||||
echo "::error::Failed to find release ${TAG} in API after $MAX_ATTEMPTS attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id')
|
||||
|
||||
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
|
||||
echo "::error::Failed to extract release ID from API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "release_url=$(echo "$RELEASE_JSON" | jq -r '.html_url')" >> $GITHUB_OUTPUT
|
||||
echo "release_id=${RELEASE_ID}" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "✓ Release ID: ${RELEASE_ID}"
|
||||
|
||||
- name: Upload checksums.txt
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="${{ needs.extract-version.outputs.tag }}"
|
||||
|
||||
echo "Uploading checksums.txt..."
|
||||
gh release upload "${TAG}" release/checksums.txt
|
||||
|
||||
# Upload individual .sha256 files for backward compatibility
|
||||
echo "Uploading .sha256 checksum files..."
|
||||
gh release upload "${TAG}" release/*.sha256
|
||||
|
||||
- name: Upload release assets
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="${{ needs.extract-version.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
|
||||
|
||||
- name: Output release information
|
||||
run: |
|
||||
echo "✅ Release draft created successfully!"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 Release: ${{ needs.extract-version.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 automatically generated release notes"
|
||||
echo "2. Edit and categorize changes as needed"
|
||||
echo "3. Publish the release when ready"
|
||||
echo ""
|
||||
echo "All artifacts have been uploaded."
|
||||
echo "Docker images are available at Docker Hub and GHCR."
|
||||
echo ""
|
||||
|
||||
validate-release-assets:
|
||||
needs:
|
||||
- extract-version
|
||||
- create-release
|
||||
uses: ./.github/workflows/validate-release-assets.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
tag: ${{ needs.extract-version.outputs.tag }}
|
||||
version: ${{ needs.extract-version.outputs.version }}
|
||||
release_id: ${{ needs.create-release.outputs.release_id }}
|
||||
draft: true
|
||||
target_commitish: ${{ needs.create-release.outputs.target_commitish }}
|
||||
Loading…
Add table
Add a link
Reference in a new issue