240 lines
10 KiB
YAML
240 lines
10 KiB
YAML
# yaml-language-server: $schema=https://json-schema.org/draft-07/schema#
|
|
name: _build
|
|
|
|
concurrency:
|
|
group: build
|
|
cancel-in-progress: false
|
|
|
|
on:
|
|
workflow_call:
|
|
|
|
jobs:
|
|
check:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
outputs:
|
|
should-build: ${{ steps.check.outputs.should-build }}
|
|
artifact-run-id: ${{ steps.check.outputs.artifact-run-id }}
|
|
artifact-id: ${{ steps.check.outputs.artifact-id }}
|
|
build-sha: ${{ steps.check.outputs.build-sha }}
|
|
repo-name: ${{ steps.meta.outputs.repo-name }}
|
|
repo-path: ${{ steps.meta.outputs.repo-path }}
|
|
image-tar: ${{ steps.check.outputs.image-tar }}
|
|
prod: ${{ steps.meta.outputs.prod }}
|
|
tag: ${{ steps.check.outputs.tag }}
|
|
should-deploy: ${{ steps.check.outputs.should-deploy }}
|
|
steps:
|
|
- name: Checkout the repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Determine build metadata
|
|
id: meta
|
|
uses: ./.github/actions/determine-build-metadata
|
|
with:
|
|
repository: ${{ github.repository }}
|
|
github-ref: ${{ github.ref }}
|
|
|
|
- name: Check if build/deploy needed
|
|
id: check
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -eu -o pipefail
|
|
|
|
SHOULD_BUILD=false
|
|
SHOULD_DEPLOY=false
|
|
|
|
SHORT_SHA="${{ github.sha }}"
|
|
SHORT_SHA="${SHORT_SHA:0:7}"
|
|
IMAGE_TAR="${{ steps.meta.outputs.repo-name }}-${{github.sha}}.tar"
|
|
VERSION="$(git tag --points-at ${{ github.sha }})"
|
|
IS_VERSION_TAGGED="$([[ "$VERSION" =~ ^v ]] && echo "true" || echo "false")"
|
|
BRANCH_NAME="$([[ "${{ github.ref }}" =~ ^refs/tags/v ]] && echo "main" || echo "${{ github.ref }}" | sed 's/refs\/heads\///')"
|
|
COMMIT_MESSAGE="$(git log --format=%s -n 1 ${{ github.sha }})"
|
|
if [[ "${{ steps.meta.outputs.prod }}" == "true" ]]; then
|
|
TAG="${{ steps.meta.outputs.repo-name }}:sha-${SHORT_SHA},${{ steps.meta.outputs.repo-name }}:latest"
|
|
else
|
|
TAG="${{ steps.meta.outputs.repo-name }}-dev:sha-${SHORT_SHA}"
|
|
fi
|
|
|
|
if [[ "$BRANCH_NAME" == "staging" ]]; then
|
|
echo "🔨 Build is on staging - will also deploy to staging"
|
|
SHOULD_DEPLOY="true"
|
|
elif [[ "$BRANCH_NAME" == "main" ]]; then
|
|
if [[ ! "$COMMIT_MESSAGE" =~ ^chore:\ Release ]] || [[ "$IS_VERSION_TAGGED" == "false" ]]; then
|
|
echo "❌ Skipping build for main: $COMMIT_MESSAGE. Not a version release. Missing tag or proper commit message for release."
|
|
exit 1
|
|
fi
|
|
echo "🏷️ Version tagged build on main: $VERSION - will also deploy to production"
|
|
SHOULD_DEPLOY="true"
|
|
fi
|
|
|
|
echo "🔍 Checking for in progress builds with git SHA ${{github.sha}}"
|
|
IN_PROGRESS_BUILDS=""
|
|
if ! IN_PROGRESS_BUILDS=$(gh api repos/${{github.repository}}/actions/runs --jq '.workflow_runs[] | select(.head_sha == "${{github.sha}}" and .status == "in_progress" and .name == "Build/Deploy" and .id != ${{ github.run_id }}) | .id' 2>/dev/null); then
|
|
echo "❌ Failed to check for in-progress builds via GitHub API"
|
|
echo "::error::GitHub API call failed while checking for in-progress builds"
|
|
exit 1
|
|
fi
|
|
IN_PROGRESS_BUILDS=$(echo "$IN_PROGRESS_BUILDS" | head -1)
|
|
|
|
if [[ -n "$IN_PROGRESS_BUILDS" ]]; then
|
|
echo "❌ Found in-progress build for SHA ${{github.sha}}: $IN_PROGRESS_BUILDS - wait for existing build to complete before triggering a new one or rerunning this workflow"
|
|
exit 1
|
|
fi
|
|
|
|
echo "🔍 Checking for existing build artifacts with git SHA ${{github.sha}}"
|
|
|
|
# Check for existing artifacts with this SHA across all workflow runs
|
|
ARTIFACT_INFO=""
|
|
if ! ARTIFACT_INFO=$(gh api repos/${{github.repository}}/actions/artifacts --jq '.artifacts[] | select(.name | endswith("-${{github.sha}}.tar")) | {id, name, workflow_run} | @json' 2>/dev/null); then
|
|
echo "❌ Failed to check for existing artifacts via GitHub API"
|
|
echo "::error::GitHub API call failed while checking for existing artifacts"
|
|
exit 1
|
|
fi
|
|
ARTIFACT_INFO=$(echo "$ARTIFACT_INFO" | head -1)
|
|
|
|
if [[ -n "$ARTIFACT_INFO" ]]; then
|
|
ARTIFACT_ID=$(echo "$ARTIFACT_INFO" | jq -r '.id')
|
|
ARTIFACT_NAME=$(echo "$ARTIFACT_INFO" | jq -r '.name')
|
|
RUN_ID=$(echo "$ARTIFACT_INFO" | jq -r '.workflow_run.id')
|
|
|
|
echo "✅ Found existing artifact $ARTIFACT_NAME from run: $RUN_ID"
|
|
echo "artifact-id=$ARTIFACT_ID" >> $GITHUB_OUTPUT
|
|
echo "artifact-id -> $ARTIFACT_ID"
|
|
else
|
|
RUN_ID="${{ github.run_id }}"
|
|
SHOULD_BUILD="true"
|
|
echo "🔨 No existing build artifact found; will build..."
|
|
fi
|
|
|
|
echo "should-build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
|
|
echo "should-build -> $SHOULD_BUILD"
|
|
echo "should-deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
|
|
echo "should-deploy -> $SHOULD_DEPLOY"
|
|
echo "artifact-run-id=$RUN_ID" >> $GITHUB_OUTPUT
|
|
echo "artifact-run-id -> $RUN_ID"
|
|
echo "build-sha=${{github.sha}}" >> $GITHUB_OUTPUT
|
|
echo "build-sha -> ${{github.sha}}"
|
|
echo "image-tar=$IMAGE_TAR" >> $GITHUB_OUTPUT
|
|
echo "image-tar -> $IMAGE_TAR"
|
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
echo "tag -> $TAG"
|
|
|
|
build:
|
|
needs: check
|
|
if: ${{ needs.check.outputs.should-build == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
|
steps:
|
|
- name: Checkout the repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Ensure scripts are executable
|
|
run: chmod +x deploy/scripts/*.sh
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Generate cache keys
|
|
id: cache-keys
|
|
run: |
|
|
# Generate cache key based on package-lock.json for dependencies
|
|
DEPS_HASH=$(sha256sum package-lock.json | cut -d' ' -f1 | cut -c1-8)
|
|
echo "deps-hash=${DEPS_HASH}" >> $GITHUB_OUTPUT
|
|
echo "deps-hash -> $DEPS_HASH"
|
|
|
|
# Generate cache key based on source files and paths
|
|
SOURCE_PATHS="app/ server/ public/ nuxt.config.ts tsconfig.json tailwind.config.js package.json"
|
|
SRC_HASH=$(find $SOURCE_PATHS -type f 2>/dev/null -exec sha256sum {} + | sha256sum | cut -d' ' -f1 | cut -c1-8 || echo "fallback-$(date +%s)")
|
|
echo "src-hash=${SRC_HASH}" >> $GITHUB_OUTPUT
|
|
echo "src-hash -> $SRC_HASH"
|
|
|
|
- name: Cache Docker layers
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/.buildx-cache
|
|
key: ${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-src-${{ steps.cache-keys.outputs.src-hash }}-${{ github.sha }}
|
|
restore-keys: |
|
|
${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-src-${{ steps.cache-keys.outputs.src-hash }}-
|
|
${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-
|
|
${{ runner.os }}-buildx-
|
|
|
|
- name: Extract version for Docker build
|
|
id: extract_version
|
|
run: |
|
|
if [[ "${{ github.ref }}" =~ ^refs/tags/v([0-9]+\.[0-9]+\.[0-9]+)(-.*)?$ ]]; then
|
|
VERSION="${BASH_REMATCH[1]}"
|
|
if [[ -n "${BASH_REMATCH[2]}" ]]; then
|
|
VERSION="${VERSION}${BASH_REMATCH[2]}"
|
|
fi
|
|
echo "🏷️ Using git tag version: ${VERSION}"
|
|
else
|
|
VERSION=$(node -p "require('./package.json').version || '0.0.0'")
|
|
SHA="${{ github.sha }}"
|
|
GIT_SHA_SHORT="${SHA:0:7}"
|
|
VERSION="${VERSION}+${GIT_SHA_SHORT}"
|
|
echo "📦 Using package.json + SHA version: ${VERSION}"
|
|
fi
|
|
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
|
|
|
- name: Build container
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
outputs: type=docker,dest=${{ runner.temp }}/${{ needs.check.outputs.image-tar }}
|
|
tags: ${{ needs.check.outputs.tag }}
|
|
build-args: |
|
|
VERSION=${{ env.VERSION }}
|
|
context: .
|
|
target: runtime
|
|
cache-from: type=local,src=/tmp/.buildx-cache
|
|
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
platforms: linux/amd64
|
|
|
|
- name: Rotate cache
|
|
run: |
|
|
rm -rf /tmp/.buildx-cache
|
|
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
|
|
- name: Upload container as artifact
|
|
id: upload-artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ needs.check.outputs.image-tar }}
|
|
path: ${{ runner.temp }}/${{ needs.check.outputs.image-tar }}
|
|
if-no-files-found: error
|
|
retention-days: 30
|
|
compression-level: 0
|
|
|
|
- name: Notify successful build
|
|
run: |
|
|
echo "🎉 Build completed successfully!"
|
|
echo "📋 Summary:"
|
|
echo " - Source: ${{ github.ref }}"
|
|
echo " - Status: ✅ Build Successful"
|
|
echo " - Next: Stage this build manually or wait for the next scheduled staging."
|
|
|
|
deploy:
|
|
needs: [check, build]
|
|
if: ${{ always() && needs.check.outputs.should-deploy == 'true' }}
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
uses: ./.github/workflows/deploy.yml
|
|
with:
|
|
artifact-run-id: ${{ needs.check.outputs.artifact-run-id }}
|
|
artifact-id: ${{ needs.check.outputs.should-build == 'true' && needs.build.outputs.artifact-id || needs.check.outputs.artifact-id }}
|
|
build-sha: ${{ needs.check.outputs.build-sha }}
|
|
repo-name: ${{ needs.check.outputs.repo-name }}
|
|
repo-path: ${{ needs.check.outputs.repo-path }}
|
|
image-tar: ${{ needs.check.outputs.image-tar }}
|
|
prod: ${{ needs.check.outputs.prod == 'true' }}
|
|
tag: ${{ needs.check.outputs.tag }}
|
|
secrets: inherit
|