From 6f95e09bb08065fc83495be96f7d1479939250a6 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 9 May 2026 20:14:12 +0100 Subject: [PATCH] ci: Adds GH actions workflow to automate releases --- .github/workflows/deploy-aws.yml | 127 -------------------------- .github/workflows/release.yml | 148 +++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 127 deletions(-) delete mode 100644 .github/workflows/deploy-aws.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/deploy-aws.yml b/.github/workflows/deploy-aws.yml deleted file mode 100644 index 426035e..0000000 --- a/.github/workflows/deploy-aws.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: 🚀 Deploy to AWS - -on: - workflow_dispatch: - push: - branches: - - master - tags: - - '*' - paths: - - api/** - - serverless.yml - - package.json - - .github/workflows/deploy-aws.yml - -jobs: - deploy-api: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 16 - - - name: Cache node_modules - uses: actions/cache@v4 - with: - path: node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Create GitHub deployment for API - uses: chrnorm/deployment-action@releases/v2 - id: deployment_api - with: - token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }} - environment: AWS (Backend API) - ref: ${{ github.ref }} - - - name: Install Serverless CLI and dependencies - run: | - npm i -g serverless - yarn - - - name: Deploy to AWS - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} - run: serverless deploy - - - name: Update GitHub deployment status (API) - if: always() - uses: chrnorm/deployment-status@v2 - with: - token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }} - state: '${{ job.status }}' - deployment_id: ${{ steps.deployment_api.outputs.deployment_id }} - ref: ${{ github.ref }} - - deploy-frontend: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 16 - - - name: Cache node_modules - uses: actions/cache@v4 - with: - path: node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Create GitHub deployment for Frontend - uses: chrnorm/deployment-action@v2 - id: deployment_frontend - with: - token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }} - environment: AWS (Frontend Web UI) - ref: ${{ github.ref }} - - - name: Install dependencies and build - run: | - yarn install - yarn build - - - name: Setup AWS - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - - name: Upload to S3 - env: - AWS_S3_BUCKET: 'web-check-frontend' - run: aws s3 sync ./build/ s3://$AWS_S3_BUCKET/ --delete - - - name: Invalidate CloudFront cache - uses: chetan/invalidate-cloudfront-action@v2 - env: - DISTRIBUTION: E30XKAM2TG9FD8 - PATHS: '/*' - AWS_REGION: 'us-east-1' - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - - name: Update GitHub deployment status (Frontend) - if: always() - uses: chrnorm/deployment-status@v2 - with: - token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }} - state: '${{ job.status }}' - deployment_id: ${{ steps.deployment_frontend.outputs.deployment_id }} - ref: ${{ github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..184bc58 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,148 @@ +# Publishes GH release for new versions, with compiled app attached +# - Triggered when a git tag matching X.Y.0 (major or minor) is pushed +# - Or if manually dispatched with any valid and existing tag specified +# - Check out the source at the requested tag +# - Run a clean production build (yarn build → dist/) +# - Bundle dist/ + server.js + package.json + yarn.lock into tar.gz and zip +# - Create a DRAFT GH release with auto-gen changelog and bundled artifacts attached + +name: 🚀 Release + +on: + push: + tags: + - '*.*.0' + workflow_dispatch: + inputs: + tag: + description: 'Existing git tag to release (e.g. 2.1.0)' + required: true + type: string + +permissions: + contents: write + +concurrency: + group: release-${{ inputs.tag || github.ref_name }} + cancel-in-progress: false + +jobs: + release: + name: 🚀 Build & Publish Release + runs-on: ubuntu-latest + steps: + - name: Resolve tag 🏷️ + id: tag + env: + INPUT_TAG: ${{ inputs.tag }} + EVENT_NAME: ${{ github.event_name }} + REF: ${{ github.ref }} + run: | + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + TAG="$INPUT_TAG" + else + TAG="${REF#refs/tags/}" + fi + if ! echo "$TAG" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid tag '$TAG'. Expected semver (e.g. 2.1.0)." + exit 1 + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "Releasing tag: $TAG" + + - name: Checkout source at tag 🛎️ + uses: actions/checkout@v6 + with: + ref: refs/tags/${{ steps.tag.outputs.tag }} + fetch-depth: 0 + + - name: Setup Node.js 🔧 + uses: actions/setup-node@v6 + with: + node-version: '22' + cache: 'yarn' + + - name: Install dependencies 📦 + run: yarn install --frozen-lockfile + + - name: Build for production 🏗️ + run: yarn build + + - name: Verify build output ✅ + run: | + if [ ! -d "dist/client" ]; then + echo "::error::Build failed: dist/client directory not created" + exit 1 + fi + if [ ! -f "dist/server/entry.mjs" ]; then + echo "::error::Build failed: SSR entry not found" + exit 1 + fi + echo "✅ Build successful" + + - name: Package release artifacts 🗜️ + id: package + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + STAGING="web-check-${TAG}" + rm -rf "$STAGING" + mkdir -p "$STAGING" + cp -r dist api public "$STAGING/" + cp server.js package.json yarn.lock "$STAGING/" + [ -f LICENSE ] && cp LICENSE "$STAGING/" || true + [ -f README.md ] && cp README.md "$STAGING/" || true + + TARBALL="${STAGING}.tar.gz" + ZIPFILE="${STAGING}.zip" + tar -czf "$TARBALL" "$STAGING" + zip -qr "$ZIPFILE" "$STAGING" + + ls -lh "$TARBALL" "$ZIPFILE" + echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT" + echo "zipfile=$ZIPFILE" >> "$GITHUB_OUTPUT" + + - name: Create draft release 🚀 + id: release + uses: softprops/action-gh-release@v3 + with: + tag_name: ${{ steps.tag.outputs.tag }} + name: Web-Check v${{ steps.tag.outputs.tag }} + draft: true + prerelease: false + generate_release_notes: true + fail_on_unmatched_files: true + files: | + ${{ steps.package.outputs.tarball }} + ${{ steps.package.outputs.zipfile }} + token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }} + + - name: Job summary 📋 + if: always() + env: + TAG: ${{ steps.tag.outputs.tag }} + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + RELEASE_URL: ${{ steps.release.outputs.url }} + RELEASE_OUTCOME: ${{ steps.release.outcome }} + run: | + { + echo "## 🚀 Release" + echo "" + echo "| Step | Result |" + echo "|------|--------|" + if [ -n "$TAG" ]; then + echo "| Tag | [\`${TAG}\`](${REPO_URL}/releases/tag/${TAG}) |" + else + echo "| Tag | ❌ Could not resolve |" + fi + if [ "$RELEASE_OUTCOME" = "success" ]; then + if [ -n "$RELEASE_URL" ]; then + echo "| Draft release | ✅ [View draft](${RELEASE_URL}) |" + else + echo "| Draft release | ✅ [View releases](${REPO_URL}/releases) |" + fi + echo "| Artifacts | \`web-check-${TAG}.tar.gz\`, \`web-check-${TAG}.zip\` |" + else + echo "| Draft release | ❌ Failed (outcome: ${RELEASE_OUTCOME:-unknown}) |" + fi + } >> "$GITHUB_STEP_SUMMARY"