diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 967c63411..45eac1318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,23 +3,23 @@ name: CI on: pull_request: +# Cancel in-progress runs when a new commit is pushed to the same PR +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: CI: true - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ vars.TURBO_TEAM }} - TURBO_REMOTE_ONLY: true jobs: - ci: - name: Build, Lint & Test + # Build job - runs in parallel + build: + name: Build runs-on: blacksmith-4vcpu-ubuntu-2404 - timeout-minutes: 30 - + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v4 - with: - fetch-depth: 2 - name: Install pnpm uses: pnpm/action-setup@v2 @@ -30,18 +30,19 @@ jobs: uses: useblacksmith/setup-node@v5 with: node-version: 22 - # Remove cache here since we're using stickydisk for pnpm store - - - name: Get pnpm store directory - shell: bash + cache: 'pnpm' + + - name: Fix pnpm store permissions run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Mount pnpm store sticky disk - uses: useblacksmith/stickydisk@v1 - with: - key: ${{ github.repository }}-pnpm-store - path: ${{ env.STORE_PATH }} + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + # Clear corrupted cache entries + find "$STORE_PATH" -type f ! -perm -u+r -delete 2>/dev/null || true + find "$STORE_PATH" -type d ! -perm -u+rx -delete 2>/dev/null || true + # Fix permissions on remaining files + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi - name: Mount Turbo cache sticky disk uses: useblacksmith/stickydisk@v1 @@ -49,19 +50,6 @@ jobs: key: ${{ github.repository }}-turbo-cache path: ./.turbo - - name: Check if lockfile changed - id: lockfile-check - run: | - if git diff HEAD~1 HEAD --name-only | grep -q "pnpm-lock.yaml"; then - echo "changed=true" >> $GITHUB_OUTPUT - else - echo "changed=false" >> $GITHUB_OUTPUT - fi - - - name: Fetch dependencies (if lockfile changed) - if: steps.lockfile-check.outputs.changed == 'true' - run: pnpm fetch --frozen-lockfile - - name: Install dependencies run: pnpm install --frozen-lockfile --prefer-offline @@ -70,9 +58,97 @@ jobs: env: NODE_ENV: production + # Lint job - runs in parallel + lint: + name: Lint + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 5 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: useblacksmith/setup-node@v5 + with: + node-version: 22 + cache: 'pnpm' + + - name: Fix pnpm store permissions + run: | + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + # Clear corrupted cache entries + find "$STORE_PATH" -type f ! -perm -u+r -delete 2>/dev/null || true + find "$STORE_PATH" -type d ! -perm -u+rx -delete 2>/dev/null || true + # Fix permissions on remaining files + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi + + - name: Mount Turbo cache sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-turbo-cache + path: ./.turbo + + - name: Install dependencies + run: pnpm install --frozen-lockfile --prefer-offline + - name: Lint all packages (excluding web) run: pnpm lint --filter='!@buster-app/web' + # Test job - runs after build completes + test: + name: Test + runs-on: blacksmith-4vcpu-ubuntu-2404 + timeout-minutes: 15 + needs: build + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: useblacksmith/setup-node@v5 + with: + node-version: 22 + cache: 'pnpm' + + - name: Fix pnpm store permissions + run: | + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + # Clear corrupted cache entries + find "$STORE_PATH" -type f ! -perm -u+r -delete 2>/dev/null || true + find "$STORE_PATH" -type d ! -perm -u+rx -delete 2>/dev/null || true + # Fix permissions on remaining files + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi + + - name: Mount Turbo cache sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-turbo-cache + path: ./.turbo + + - name: Install dependencies + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Build required packages + run: pnpm build --filter='!@buster-app/web' + env: + NODE_ENV: production + - name: Run all unit tests (excluding web) run: pnpm test:unit --filter='!@buster-app/web' diff --git a/.github/workflows/database-migrations.yml b/.github/workflows/database-migrations.yml index 2d5a29858..27bc63ef4 100644 --- a/.github/workflows/database-migrations.yml +++ b/.github/workflows/database-migrations.yml @@ -8,38 +8,45 @@ on: - 'packages/database/drizzle.config.ts' - '.github/workflows/database-migrations.yml' +# Only one migration per environment at a time +concurrency: + group: db-migrate-${{ github.ref }} + cancel-in-progress: false # Never cancel migrations + jobs: migrate: - runs-on: blacksmith-8vcpu-ubuntu-2204 + runs-on: blacksmith-2vcpu-ubuntu-2404 environment: ${{ github.ref_name }} steps: - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22.x' - - name: Install pnpm uses: pnpm/action-setup@v2 with: version: 9.15.0 - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Setup pnpm cache - uses: actions/cache@v3 + - name: Setup Node.js + uses: useblacksmith/setup-node@v5 with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 22 + cache: 'pnpm' + + - name: Fix pnpm store permissions + run: | + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi + + - name: Mount Turbo cache sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-turbo-cache + path: ./.turbo - name: Install dependencies - run: pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile --prefer-offline - name: Run migrations run: pnpm run db:migrate diff --git a/.github/workflows/docker-build-server.yml b/.github/workflows/docker-build-server.yml new file mode 100644 index 000000000..d5d0e9937 --- /dev/null +++ b/.github/workflows/docker-build-server.yml @@ -0,0 +1,165 @@ +name: Build and Push Server Docker Image + +on: + push: + branches: [main, staging] + paths: + - 'apps/server/**' + - 'packages/**' + - 'pnpm-lock.yaml' + - '.github/workflows/docker-build-server.yml' + +# Only one build per branch at a time, queue others +concurrency: + group: docker-build-${{ github.ref }} + cancel-in-progress: false # Don't cancel, queue instead for deployments + +env: + REGISTRY: ghcr.io + IMAGE_NAME: buster-so/buster-server + +jobs: + build-and-push: + runs-on: blacksmith-8vcpu-ubuntu-2404 + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: useblacksmith/setup-node@v5 + with: + node-version: 22 + cache: 'pnpm' + + - name: Fix pnpm store permissions + run: | + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi + + - name: Install bun + run: npm install -g bun@1.2.15 + + - name: Mount Turbo cache sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-turbo-cache + path: ./.turbo + + - name: Mount Docker buildkit sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-buildkit-cache + path: /tmp/.buildkit-cache + + - name: Install dependencies + run: | + echo "๐Ÿ“ฆ Installing dependencies with pnpm..." + pnpm install --frozen-lockfile --prefer-offline + + - name: Build with Turbo + run: | + echo "๐Ÿ”จ Building server with Turbo (using sticky disk cache)..." + pnpm turbo run build --filter=@buster-app/server + echo "โœ… Build complete!" + env: + NODE_ENV: production + DOCKER_BUILD: true + CI: true + + - name: Build server bundle + run: | + echo "๐Ÿ“ฆ Creating server bundle with bun..." + cd apps/server + bun build src/index.ts --outdir ./dist --target bun --external pino-pretty + ls -la dist/ + cd ../.. + + - name: Prepare production dependencies + run: | + echo "๐Ÿงน Preparing production-only dependencies..." + # Create a temporary directory for production deps + mkdir -p /tmp/prod-deps/apps/server + cp package.json pnpm-lock.yaml pnpm-workspace.yaml /tmp/prod-deps/ + cp -r packages /tmp/prod-deps/ + cp apps/server/package.json /tmp/prod-deps/apps/server/ + + # Install production dependencies only + cd /tmp/prod-deps + pnpm install --frozen-lockfile --prod --ignore-scripts --no-optional + + # Copy back to workspace + cd - + mkdir -p docker-context + cp -r /tmp/prod-deps/node_modules docker-context/ + cp -r apps/server/dist docker-context/ + cp apps/server/package.json docker-context/ + + - name: Set up Docker Builder with Blacksmith cache + uses: useblacksmith/setup-docker-builder@v1 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata and determine tags + id: meta + run: | + SHA_SHORT=$(git rev-parse --short HEAD) + echo "sha_short=${SHA_SHORT}" >> $GITHUB_OUTPUT + + if [[ "${{ github.ref_name }}" == "main" ]]; then + # For main: use commit SHA and latest + echo "tags=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${SHA_SHORT},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref_name }}" == "staging" ]]; then + # For staging: use staging-SHA and staging + echo "tags=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:staging-${SHA_SHORT},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:staging" >> $GITHUB_OUTPUT + fi + + # Set build timestamp + echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: useblacksmith/build-push-action@v2 + with: + context: ./docker-context + file: ./apps/server/Dockerfile.prebuilt + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.created=${{ steps.meta.outputs.timestamp }} + org.opencontainers.image.ref.name=${{ github.ref_name }} + build-args: | + COMMIT_SHA=${{ steps.meta.outputs.sha_short }} + BUILD_DATE=${{ steps.meta.outputs.timestamp }} + + - name: Output image details + run: | + echo "โœ… Docker image built and pushed successfully!" + echo "๐Ÿ“ฆ Image tags:" + echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' | sed 's/^/ - /' + echo "" + echo "๐Ÿ”ง To use in Porter:" + if [[ "${{ github.ref_name }}" == "main" ]]; then + echo " Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.sha_short }}" + else + echo " Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:staging-${{ steps.meta.outputs.sha_short }}" + fi \ No newline at end of file diff --git a/.github/workflows/porter_app_main-hono-server_3155.yml b/.github/workflows/porter_app_main-hono-server_3155.yml index 8f47772f1..1e3aa0e74 100644 --- a/.github/workflows/porter_app_main-hono-server_3155.yml +++ b/.github/workflows/porter_app_main-hono-server_3155.yml @@ -5,7 +5,7 @@ name: Deploy to main-hono-server jobs: porter-deploy: - runs-on: ubuntu-latest + runs-on: blacksmith-8vcpu-ubuntu-2204 steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/trigger-deployment.yml b/.github/workflows/trigger-deployment.yml index 6ae4657ec..8d66bc9f3 100644 --- a/.github/workflows/trigger-deployment.yml +++ b/.github/workflows/trigger-deployment.yml @@ -4,39 +4,46 @@ on: push: branches: [main, staging] +# Only one deployment per environment at a time +concurrency: + group: trigger-deploy-${{ github.ref }} + cancel-in-progress: false # Never cancel deployments + jobs: deploy: - runs-on: blacksmith-8vcpu-ubuntu-2204 + runs-on: blacksmith-4vcpu-ubuntu-2404 environment: ${{ github.ref_name }} steps: - name: Checkout code uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22.x' - - name: Install pnpm uses: pnpm/action-setup@v2 with: version: 9.15.0 - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Setup pnpm cache - uses: actions/cache@v3 + - name: Setup Node.js + uses: useblacksmith/setup-node@v5 with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 22 + cache: 'pnpm' + + - name: Fix pnpm store permissions + run: | + STORE_PATH=$(pnpm store path --silent) + if [ -d "$STORE_PATH" ]; then + sudo chown -R $(whoami):$(whoami) "$STORE_PATH" || true + chmod -R u+rw "$STORE_PATH" || true + fi + + - name: Mount Turbo cache sticky disk + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-turbo-cache + path: ./.turbo - name: Install dependencies - run: pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile --prefer-offline - name: ๐Ÿš€ Deploy to ${{ github.ref_name == 'main' && 'Production' || 'Staging' }} env: