Skip to content

Build, Stage, and Deploy Release #19

Build, Stage, and Deploy Release

Build, Stage, and Deploy Release #19

Workflow file for this run

name: Build, Stage, and Deploy Release
on:
pull_request:
types: [ closed ]
branches: [ release ]
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run mode (no actual deployments)'
required: false
type: boolean
default: true
jobs:
calculate-version:
name: Calculate Next Version
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'release')
outputs:
version: ${{ steps.version.outputs.version }}
previous_version: ${{ steps.version.outputs.previous_version }}
dry_run: ${{ steps.dry_run.outputs.enabled }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set dry run mode
id: dry_run
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
DRY_RUN="${{ inputs.dry_run }}"
else
# PR merges to release branch are always production
DRY_RUN="false"
fi
echo "enabled=$DRY_RUN" >> $GITHUB_OUTPUT
echo "Dry run mode: $DRY_RUN"
- name: Calculate version
id: version
run: |
NEXT_VERSION=$(bash scripts/version-bump.sh)
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
PREVIOUS_VERSION="${LATEST_TAG#v}"
echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT
echo "previous_version=$PREVIOUS_VERSION" >> $GITHUB_OUTPUT
echo "Next version: $NEXT_VERSION"
echo "Previous version: $PREVIOUS_VERSION"
build-docker-ce:
name: Build Docker Images (CE)
strategy:
matrix:
include:
- runner: ubuntu-latest
platform: linux/amd64
arch: amd64
- runner: ubuntu-24.04-arm # Native ARM64 build
platform: linux/arm64
arch: arm64
runs-on: ${{ matrix.runner }}
needs: calculate-version
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image for ${{ matrix.platform }}
uses: docker/build-push-action@v6
with:
context: .
file: ./core/Dockerfile
platforms: ${{ matrix.platform }}
push: false
tags: clidey/whodb:${{ needs.calculate-version.outputs.version }}-${{ matrix.arch }}
outputs: type=oci,dest=/tmp/whodb-docker-${{ matrix.arch }}.tar
- name: Upload Docker artifact
uses: actions/upload-artifact@v4
with:
name: docker-image-${{ matrix.arch }}
path: /tmp/whodb-docker-${{ matrix.arch }}.tar
retention-days: 1
# Build desktop executables for store packaging
build-desktop-ce:
name: Build Desktop Executables (CE)
strategy:
matrix:
include:
# Windows builds for Microsoft Store
- os: windows-latest
platform: windows
arch: amd64
make-target: build-prod-windows-amd64
- os: windows-11-arm # Use native ARM64 runner for Windows ARM64
platform: windows
arch: arm64
make-target: build-prod-windows-arm64
# macOS build for Mac App Store
- os: macos-latest
platform: darwin
arch: universal
make-target: build-prod-mac
runs-on: ${{ matrix.os }}
needs: calculate-version
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'desktop-ce/go.mod'
- name: Setup Node.js and pnpm
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
with:
version: 10
- name: Install frontend dependencies
working-directory: ./frontend
run: pnpm i
- name: Install Wails CLI
run: go install github.com/wailsapp/wails/v2/cmd/wails@latest
- name: Setup ARM64 compiler on Windows ARM
if: matrix.platform == 'windows' && matrix.arch == 'arm64'
shell: pwsh
run: |
Write-Host "Checking available GCC compilers..."
Get-Command gcc -ErrorAction SilentlyContinue | Format-List
Write-Host "Go environment:"
go env GOARCH
go env CC
Write-Host "Setting GOARCH=arm64 explicitly..."
$env:GOARCH = "arm64"
Write-Host "Updated GOARCH:"
go env GOARCH
- name: Build Windows
if: matrix.platform == 'windows' && matrix.arch == 'amd64'
working-directory: ./desktop-ce
shell: pwsh
env:
GOARCH: ${{ matrix.arch }}
run: make ${{ matrix.make-target }} VERSION=${{ needs.calculate-version.outputs.version }}
- name: Skip Windows ARM64 Build (Temporarily Disabled)
if: matrix.platform == 'windows' && matrix.arch == 'arm64'
shell: pwsh
run: |
Write-Host "⚠️ Windows ARM64 build is temporarily disabled due to toolchain issues"
Write-Host "Creating placeholder for build artifacts..."
New-Item -ItemType Directory -Force -Path "./desktop-ce/build/windows/arm64"
Write-Host "Skipping actual build..."
- name: Build macOS
if: matrix.platform == 'darwin'
working-directory: ./desktop-ce
run: make ${{ matrix.make-target }} VERSION=${{ needs.calculate-version.outputs.version }}
- name: Upload artifacts (AMD64 and macOS)
if: matrix.arch != 'arm64' || matrix.platform != 'windows'
uses: actions/upload-artifact@v4
with:
name: desktop-${{ matrix.platform }}-${{ matrix.arch || 'all' }}
path: |
desktop-ce/build/
retention-days: 1
- name: Skip ARM64 Artifacts Upload
if: matrix.platform == 'windows' && matrix.arch == 'arm64'
shell: pwsh
run: |
Write-Host "⚠️ Skipping ARM64 artifact upload (build was disabled)"
# Package for Mac App Store
package-mac-app-store:
name: Package for Mac App Store
runs-on: macos-latest
needs: [ calculate-version, build-desktop-ce ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download macOS build
uses: actions/download-artifact@v4
with:
name: desktop-darwin-universal
path: desktop-ce/
- name: Skip Mac App Store packaging in dry run
if: needs.calculate-version.outputs.dry_run == 'true'
run: |
echo "🏃 DRY RUN MODE - Skipping Mac App Store packaging (requires certificates)"
echo "Creating dummy pkg for validation..."
touch "WhoDB-${{ needs.calculate-version.outputs.version }}.pkg"
- name: Package for Mac App Store
if: needs.calculate-version.outputs.dry_run == 'false'
working-directory: ./desktop-ce
env:
MAS_CODESIGN_ID: ${{ secrets.MAS_CODESIGN_ID }}
MAS_INSTALLER_ID: ${{ secrets.MAS_INSTALLER_ID }}
run: |
# Sign the app for Mac App Store
codesign --deep --force --options runtime --sign "$MAS_CODESIGN_ID" \
"build/darwin/universal/WhoDB.app"
# Create Mac App Store package
productbuild --component "build/darwin/universal/WhoDB.app" /Applications \
--sign "$MAS_INSTALLER_ID" \
"WhoDB-${{ needs.calculate-version.outputs.version }}.pkg"
- name: Upload Mac App Store package
uses: actions/upload-artifact@v4
with:
name: mac-app-store
path: desktop-ce/WhoDB-*.pkg
retention-days: 1
package-windows-msix:
name: Package Windows MSIX
runs-on: windows-latest
needs: [ calculate-version, build-desktop-ce ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Windows amd64 builds
uses: actions/download-artifact@v4
with:
name: desktop-windows-amd64
path: desktop-ce/
# ARM64 builds temporarily disabled
# - name: Download Windows arm64 builds
# uses: actions/download-artifact@v4
# with:
# name: desktop-windows-arm64
# path: desktop-ce/
- name: Skip MSIX packaging in dry run
if: needs.calculate-version.outputs.dry_run == 'true'
shell: pwsh
run: |
echo "🏃 DRY RUN MODE - Skipping MSIX packaging (requires certificate)"
echo "Creating dummy MSIX bundle for validation..."
New-Item -ItemType File -Path "WhoDB-${{ needs.calculate-version.outputs.version }}.msixbundle" -Force
- name: Decode certificate
if: needs.calculate-version.outputs.dry_run == 'false'
shell: pwsh
run: |
$bytes = [Convert]::FromBase64String("${{ secrets.WINDOWS_PFX_BASE64 }}")
[IO.File]::WriteAllBytes("cert.pfx", $bytes)
- name: Build MSIX amd64
if: needs.calculate-version.outputs.dry_run == 'false'
shell: pwsh
env:
WINDOWS_PFX_PASSWORD: ${{ secrets.WINDOWS_PFX_PASSWORD }}
run: |
.\scripts\build-msix.ps1 -Architecture amd64 -Version ${{ needs.calculate-version.outputs.version }} -PublisherCN "${{ secrets.WINDOWS_PUBLISHER_CN }}" -CertPath cert.pfx
# ARM64 build temporarily disabled
# - name: Build MSIX arm64
# if: needs.calculate-version.outputs.dry_run == 'false'
# shell: pwsh
# env:
# WINDOWS_PFX_PASSWORD: ${{ secrets.WINDOWS_PFX_PASSWORD }}
# run: |
# .\scripts\build-msix.ps1 -Architecture arm64 -Version ${{ needs.calculate-version.outputs.version }} -PublisherCN "${{ secrets.WINDOWS_PUBLISHER_CN }}" -CertPath cert.pfx
- name: Create MSIX package (AMD64 only)
if: needs.calculate-version.outputs.dry_run == 'false'
shell: pwsh
run: |
# Since we only have AMD64 for now, rename it as the bundle
Move-Item "WhoDB-${{ needs.calculate-version.outputs.version }}-amd64.msix" "WhoDB-${{ needs.calculate-version.outputs.version }}.msixbundle" -Force
- name: Upload MSIX artifacts
uses: actions/upload-artifact@v4
with:
name: windows-msix
path: WhoDB-*.msixbundle
retention-days: 1
build-snap:
name: Build Snap Package
strategy:
matrix:
include:
- runner: ubuntu-latest
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
needs: [ calculate-version ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Prepare snap build
run: |
# Copy the CE snapcraft file to the expected location
cp snapcraft-ce.yaml snapcraft.yaml
- name: Build snap from source
uses: snapcore/action-build@v1
id: build
- name: Rename snap with architecture
run: |
SNAP_FILE="${{ steps.build.outputs.snap }}"
NEW_NAME="${SNAP_FILE%.snap}_${{ matrix.arch }}.snap"
mv "$SNAP_FILE" "$NEW_NAME"
echo "SNAP_FILE=$NEW_NAME" >> $GITHUB_ENV
- name: Upload snap artifact
uses: actions/upload-artifact@v4
with:
name: snap-package-${{ matrix.arch }}
path: ${{ env.SNAP_FILE }}
retention-days: 1
sign-with-sigstore:
name: Sign Artifacts with Sigstore
runs-on: ubuntu-latest
needs: [ calculate-version, package-windows-msix, package-mac-app-store, build-snap, build-desktop-ce ]
# Need write permissions for OIDC token
permissions:
contents: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
# Continue even if dockerbuild metadata artifacts fail to download
continue-on-error: true
- name: Sign Windows MSIX bundle
env:
COSIGN_EXPERIMENTAL: 1
run: |
for file in artifacts/windows-msix/*.msixbundle; do
if [ -f "$file" ]; then
echo "Signing $file with Sigstore..."
cosign sign-blob --yes "$file" \
--output-signature="${file}.sig" \
--output-certificate="${file}.pem" \
--oidc-issuer=https://token.actions.githubusercontent.com \
|| echo "Warning: Failed to sign $file, continuing..."
fi
done
- name: Sign Mac App Store package
env:
COSIGN_EXPERIMENTAL: 1
run: |
for file in artifacts/mac-app-store/*.pkg; do
if [ -f "$file" ]; then
echo "Signing $file with Sigstore..."
cosign sign-blob --yes "$file" \
--output-signature="${file}.sig" \
--output-certificate="${file}.pem" \
--oidc-issuer=https://token.actions.githubusercontent.com \
|| echo "Warning: Failed to sign $file, continuing..."
fi
done
- name: Upload signed artifacts
uses: actions/upload-artifact@v4
with:
name: signed-artifacts
path: artifacts/
retention-days: 1
validate-all-builds:
name: Validate All Builds
runs-on: ubuntu-latest
needs: [ sign-with-sigstore, build-docker-ce ]
steps:
- name: Download required artifacts for validation
uses: actions/download-artifact@v4
with:
path: artifacts/
# Continue even if dockerbuild metadata artifacts fail
continue-on-error: true
- name: Clean up Docker metadata artifacts
run: |
# Remove any dockerbuild artifacts that may have partially downloaded
rm -rf artifacts/*dockerbuild* || true
echo "Cleaned up Docker metadata artifacts (not required for validation)"
- name: Validate artifacts
run: |
echo "Checking for required artifacts..."
[ -f "artifacts/docker-image-amd64/whodb-docker-amd64.tar" ] || (echo "Docker amd64 image missing" && exit 1)
[ -f "artifacts/docker-image-arm64/whodb-docker-arm64.tar" ] || (echo "Docker arm64 image missing" && exit 1)
[ -f "artifacts/windows-msix/"*.msixbundle ] || (echo "Windows MSIX bundle missing" && exit 1)
[ -f "artifacts/mac-app-store/"*.pkg ] || (echo "Mac App Store package missing" && exit 1)
[ -f "artifacts/snap-package-amd64/"*.snap ] || (echo "Snap amd64 package missing" && exit 1)
[ -f "artifacts/snap-package-arm64/"*.snap ] || (echo "Snap arm64 package missing" && exit 1)
echo "✓ All required artifacts present"
echo "Note: Windows ARM64 build is temporarily disabled"
- name: Initialize deployment manifest
run: |
bash scripts/track-deployment.sh docker pending ${{ needs.calculate-version.outputs.version }}
- name: Upload deployment manifest
uses: actions/upload-artifact@v4
with:
name: deployment-manifest
path: deployment_manifest.json
retention-days: 1
deploy-docker:
name: Deploy Docker Images
runs-on: ubuntu-latest
needs: [ calculate-version, validate-all-builds ]
steps:
- name: Checkout for scripts
uses: actions/checkout@v4
- name: Download Docker amd64 artifact
uses: actions/download-artifact@v4
with:
name: docker-image-amd64
path: /tmp
- name: Download Docker arm64 artifact
uses: actions/download-artifact@v4
with:
name: docker-image-arm64
path: /tmp
- name: Download deployment manifest
uses: actions/download-artifact@v4
with:
name: deployment-manifest
path: .
- name: Load Docker images
run: |
docker load --input /tmp/whodb-docker-amd64.tar
docker load --input /tmp/whodb-docker-arm64.tar
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Install Cosign
if: needs.calculate-version.outputs.dry_run == 'false'
uses: sigstore/cosign-installer@v3
- name: Set up Docker Buildx for manifest
uses: docker/setup-buildx-action@v3
- name: Push Docker images and create manifest
if: needs.calculate-version.outputs.dry_run == 'false'
run: |
# Push individual platform images
docker push clidey/whodb:${{ needs.calculate-version.outputs.version }}-amd64 || exit 1
docker push clidey/whodb:${{ needs.calculate-version.outputs.version }}-arm64 || exit 1
# Create and push multi-platform manifests
docker buildx imagetools create -t clidey/whodb:${{ needs.calculate-version.outputs.version }} \
clidey/whodb:${{ needs.calculate-version.outputs.version }}-amd64 \
clidey/whodb:${{ needs.calculate-version.outputs.version }}-arm64
docker buildx imagetools create -t clidey/whodb:latest \
clidey/whodb:${{ needs.calculate-version.outputs.version }}-amd64 \
clidey/whodb:${{ needs.calculate-version.outputs.version }}-arm64
bash scripts/track-deployment.sh docker deployed ${{ needs.calculate-version.outputs.version }}
- name: Sign Docker images
if: needs.calculate-version.outputs.dry_run == 'false'
run: |
cosign sign --yes clidey/whodb:${{ needs.calculate-version.outputs.version }}
cosign sign --yes clidey/whodb:latest
- name: Dry run summary
if: needs.calculate-version.outputs.dry_run == 'true'
run: |
echo "🏃 DRY RUN MODE"
echo "✅ Successfully logged in to Docker Hub"
echo "📦 Would have pushed:"
echo " - clidey/whodb:${{ needs.calculate-version.outputs.version }}"
echo " - clidey/whodb:latest"
echo "🔏 Would have signed both images with Sigstore"
- name: Upload updated manifest
if: always()
uses: actions/upload-artifact@v4
with:
name: deployment-manifest-docker
path: deployment_manifest.json
retention-days: 1
deploy-snap:
name: Deploy to Snap Store
runs-on: ubuntu-latest
needs: [ calculate-version, create-github-release ]
steps:
- name: Checkout for scripts
uses: actions/checkout@v4
- name: Download snap amd64 artifact
uses: actions/download-artifact@v4
with:
name: snap-package-amd64
- name: Download snap arm64 artifact
uses: actions/download-artifact@v4
with:
name: snap-package-arm64
- name: Download deployment manifest
uses: actions/download-artifact@v4
with:
name: deployment-manifest-github
path: .
- name: Capture current Snap Store state
if: needs.calculate-version.outputs.dry_run == 'false'
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
run: |
bash scripts/capture-store-state.sh whodb
- name: Publish to Snap Store
if: needs.calculate-version.outputs.dry_run == 'false'
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
run: |
# Snap Store automatically handles multi-arch packages when uploaded separately
for snap in *.snap; do
echo "Publishing $snap to Snap Store..."
snapcraft upload "$snap" --release=stable
done
- name: Track Snap deployment
if: needs.calculate-version.outputs.dry_run == 'false'
run: |
bash scripts/track-deployment.sh snap deployed ${{ needs.calculate-version.outputs.version }}
- name: Dry run summary
if: needs.calculate-version.outputs.dry_run == 'true'
run: |
echo "🏃 DRY RUN MODE - Would have published:"
echo " - Snap packages (amd64 and arm64) to stable channel"
ls -lh *.snap
- name: Upload updated manifest
if: always()
uses: actions/upload-artifact@v4
with:
name: deployment-manifest-snap
path: deployment_manifest.json
retention-days: 1
- name: Upload store state
if: always()
uses: actions/upload-artifact@v4
with:
name: store-state
path: store_state.json
retention-days: 1
create-github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [ calculate-version, deploy-docker ]
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download deployment manifest
uses: actions/download-artifact@v4
with:
name: deployment-manifest-docker
path: .
- name: Download all signed artifacts
uses: actions/download-artifact@v4
with:
name: signed-artifacts
path: release-assets/
- name: Prepare release assets
run: |
mkdir -p final-assets
# Copy Windows MSIX bundle (for users who prefer direct download)
cp release-assets/windows-msix/*.msixbundle final-assets/ || true
cp release-assets/windows-msix/*.sig final-assets/ || true
# Copy Mac App Store package (for users who prefer direct download)
cp release-assets/mac-app-store/*.pkg final-assets/ || true
cp release-assets/mac-app-store/*.sig final-assets/ || true
- name: Generate checksums
working-directory: final-assets
run: |
sha256sum * > SHA256SUMS.txt
- name: Create Release
id: create_release
if: needs.calculate-version.outputs.dry_run == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.calculate-version.outputs.version }}
name: Release v${{ needs.calculate-version.outputs.version }}
body: |
# WhoDB v${{ needs.calculate-version.outputs.version }}
## Docker Images
- `docker pull clidey/whodb:${{ needs.calculate-version.outputs.version }}`
- `docker pull clidey/whodb:latest`
## Installation
**Snap:**
```bash
sudo snap install whodb
```
**Flatpak:**
Available on [Flathub](https://flathub.org/apps/com.clidey.whodb)
**Windows:**
Available on Microsoft Store
**macOS:**
Available on Mac App Store
## Release Notes
[TODO: Add release notes here]
## Verification
All binaries are signed with Sigstore. Verify signatures using cosign:
```bash
cosign verify-blob --signature <file>.sig --certificate <file>.pem <file>
```
files: final-assets/*
draft: false
prerelease: false
- name: Track GitHub release
if: needs.calculate-version.outputs.dry_run == 'false'
run: |
bash scripts/track-deployment.sh github_release deployed ${{ needs.calculate-version.outputs.version }} "${{ steps.create_release.outputs.id }}"
- name: Dry run summary
if: needs.calculate-version.outputs.dry_run == 'true'
run: |
echo "🏃 DRY RUN MODE - Would have created GitHub release:"
echo " - Tag: v${{ needs.calculate-version.outputs.version }}"
echo " - Files:"
ls -lh final-assets/
- name: Upload updated manifest
if: always()
uses: actions/upload-artifact@v4
with:
name: deployment-manifest-github
path: deployment_manifest.json
retention-days: 1
deploy-microsoft-store:
name: Deploy to Microsoft Store
runs-on: windows-latest
needs: [ calculate-version, package-windows-msix, create-github-release ]
if: needs.calculate-version.outputs.dry_run == 'false'
steps:
- name: Checkout for scripts
uses: actions/checkout@v4
- name: Download MSIX package
uses: actions/download-artifact@v4
with:
name: windows-msix
path: .
- name: Setup Microsoft Store CLI
shell: pwsh
run: |
# Install Windows Store CLI tools
Install-Module -Name StoreBroker -Force -Scope CurrentUser
Import-Module StoreBroker
- name: Authenticate to Microsoft Store
shell: pwsh
env:
MS_TENANT_ID: ${{ secrets.MS_TENANT_ID }}
MS_CLIENT_ID: ${{ secrets.MS_CLIENT_ID }}
MS_CLIENT_SECRET: ${{ secrets.MS_CLIENT_SECRET }}
run: |
$securePassword = ConvertTo-SecureString $env:MS_CLIENT_SECRET -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($env:MS_CLIENT_ID, $securePassword)
# Authenticate to Partner Center
Set-StoreBrokerAuthentication -TenantId $env:MS_TENANT_ID -Credential $credential
- name: Create and submit Microsoft Store submission
shell: pwsh
env:
MS_APP_ID: ${{ secrets.MS_APP_ID }}
run: |
$appId = $env:MS_APP_ID
$msixPath = Get-ChildItem -Path "." -Filter "*.msixbundle" | Select-Object -First 1
# Create new submission
$submission = New-ApplicationSubmission -AppId $appId -Force
# Update submission with new package
Update-ApplicationSubmission -AppId $appId -SubmissionDataPath $submission -PackagePath $msixPath.FullName
# Submit to Store (as draft initially)
$submissionId = Submit-ApplicationSubmission -AppId $appId -SubmissionDataPath $submission -TargetPublishMode Immediate
Write-Host "Submission created with ID: $submissionId"
Write-Host "Check Partner Center to review and publish the submission"
deploy-apple-store:
name: Deploy to Apple App Store
runs-on: macos-latest
needs: [ calculate-version, package-mac-app-store, create-github-release ]
if: needs.calculate-version.outputs.dry_run == 'false'
steps:
- name: Checkout for scripts
uses: actions/checkout@v4
- name: Download Mac App Store package
uses: actions/download-artifact@v4
with:
name: mac-app-store
path: .
- name: Setup App Store Connect API Key
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
run: |
# Create private keys directory
mkdir -p ~/.appstoreconnect/private_keys
# Save API key
echo "$APP_STORE_CONNECT_API_KEY" > ~/.appstoreconnect/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8
# Set proper permissions
chmod 600 ~/.appstoreconnect/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8
- name: Upload to App Store Connect
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
run: |
# Find the .pkg file
PKG_FILE=$(find . -name "*.pkg" -type f | head -1)
echo "Uploading $PKG_FILE to App Store Connect..."
# Upload using Transporter (altool is deprecated)
xcrun iTMSTransporter -m upload \
-assetFile "$PKG_FILE" \
-apiKey "$APP_STORE_CONNECT_API_KEY_ID" \
-apiIssuer "$APP_STORE_CONNECT_ISSUER_ID" \
-v detailed \
-verboseLogging
echo "✅ Package uploaded to App Store Connect"
echo "📝 Review and submit for review in App Store Connect"
- name: Create TestFlight release (optional)
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
run: |
echo "Package is now available in App Store Connect for TestFlight distribution"
echo "Manual review and release required via App Store Connect dashboard"
rollback:
name: Rollback on Failure
runs-on: ubuntu-latest
if: failure() && needs.calculate-version.outputs.dry_run == 'false'
needs: [ calculate-version, deploy-docker, create-github-release, deploy-snap, deploy-microsoft-store, deploy-apple-store ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download deployment manifest
uses: actions/download-artifact@v4
with:
name: deployment-manifest-snap
path: .
continue-on-error: true
- name: Download store state
uses: actions/download-artifact@v4
with:
name: store-state
path: .
continue-on-error: true
- name: Enhanced comprehensive rollback
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
WINDOWS_TENANT_ID: ${{ secrets.WINDOWS_TENANT_ID }}
WINDOWS_CLIENT_ID: ${{ secrets.WINDOWS_CLIENT_ID }}
WINDOWS_CLIENT_SECRET: ${{ secrets.WINDOWS_CLIENT_SECRET }}
WINDOWS_APP_ID: ${{ secrets.WINDOWS_APP_ID }}
run: |
bash scripts/rollback-all.sh \
${{ needs.calculate-version.outputs.version }} \
${{ needs.calculate-version.outputs.previous_version }} \
deployment_manifest.json \
store_state.json
- name: Notify rollback
run: |
echo "::error::Deployment failed and has been rolled back"
echo "Failed version: ${{ needs.calculate-version.outputs.version }}"
echo "Reverted to: ${{ needs.calculate-version.outputs.previous_version }}"
echo ""
echo "Rollback actions taken:"
echo " - Docker: Reverted latest tag to ${{ needs.calculate-version.outputs.previous_version }}"
echo " - Docker: Deleted tag ${{ needs.calculate-version.outputs.version }}"
echo " - Snap: Manual verification required in Snapcraft dashboard"
echo " - GitHub: Release and tag deleted if they existed"
exit 1
verify-deployment:
name: Verify Deployment Success
runs-on: ubuntu-latest
needs: [ calculate-version, deploy-snap ]
if: needs.calculate-version.outputs.dry_run == 'false'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download final deployment manifest
uses: actions/download-artifact@v4
with:
name: deployment-manifest-snap
path: .
- name: Verify all deployments successful
run: |
echo "Verifying deployment status..."
DOCKER_STATUS=$(jq -r '.docker.status' deployment_manifest.json)
SNAP_STATUS=$(jq -r '.snap.status' deployment_manifest.json)
GITHUB_STATUS=$(jq -r '.github_release.status' deployment_manifest.json)
echo "Docker deployment: $DOCKER_STATUS"
echo "Snap deployment: $SNAP_STATUS"
echo "GitHub release: $GITHUB_STATUS"
if [ "$DOCKER_STATUS" != "deployed" ] || [ "$SNAP_STATUS" != "deployed" ] || [ "$GITHUB_STATUS" != "deployed" ]; then
echo "::error::Not all deployments completed successfully"
exit 1
fi
echo "✓ All deployments verified successful"
- name: Test Docker image availability
run: |
echo "Testing Docker image pull..."
docker pull clidey/whodb:${{ needs.calculate-version.outputs.version }} || exit 1
echo "✓ Docker image is accessible"
- name: Verify GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Verifying GitHub release..."
RELEASE_URL="https://api.github.com/repos/${{ github.repository }}/releases/tags/v${{ needs.calculate-version.outputs.version }}"
RELEASE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITHUB_TOKEN" "$RELEASE_URL")
if [ "$RELEASE_STATUS" != "200" ]; then
echo "::error::GitHub release not found (HTTP $RELEASE_STATUS)"
exit 1
fi
echo "✓ GitHub release verified"