Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 100 additions & 36 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,33 @@ on:
push:
branches:
- main
- dev
pull_request: {}
pull_request:
types: [opened, reopened, synchronize]
# Clean up the staging environment when a PR is closed
# Use pull_request_target to also run when the PR has merge conflicts
pull_request_target:
types: [closed]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
actions: write
contents: read

env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
# Change this if you want to deploy to a different org
FLY_ORG: personal
jobs:
lint:
name: ⬣ ESLint
runs-on: ubuntu-22.04
if: ${{ github.event.action != 'closed' }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: βŽ” Setup node
uses: actions/setup-node@v4
with:
Expand All @@ -42,10 +50,10 @@ jobs:
typecheck:
name: Κ¦ TypeScript
runs-on: ubuntu-22.04
if: ${{ github.event.action != 'closed' }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: βŽ” Setup node
uses: actions/setup-node@v4
with:
Expand All @@ -69,10 +77,10 @@ jobs:
vitest:
name: ⚑ Vitest
runs-on: ubuntu-22.04
if: ${{ github.event.action != 'closed' }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: βŽ” Setup node
uses: actions/setup-node@v4
with:
Expand All @@ -93,11 +101,11 @@ jobs:
playwright:
name: 🎭 Playwright
runs-on: ubuntu-22.04
if: ${{ github.event.action != 'closed' }}
timeout-minutes: 60
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: πŸ„ Copy test env vars
run: cp .env.example .env

Expand Down Expand Up @@ -146,8 +154,7 @@ jobs:
container:
name: πŸ“¦ Prepare Container
runs-on: ubuntu-24.04
# only prepare container on pushes
if: ${{ github.event_name == 'push' }}
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
Expand All @@ -164,37 +171,106 @@ jobs:
- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]

- name: πŸ“¦ Build Staging Container
if: ${{ github.ref == 'refs/heads/dev' }}
- name: πŸ“¦ Build Production Container
run: |
flyctl deploy \
--build-only \
--push \
--image-label ${{ github.sha }} \
--build-arg COMMIT_SHA=${{ github.sha }} \
--app ${{ steps.app_name.outputs.value }}-staging
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
--build-secret SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \
--app ${{ steps.app_name.outputs.value }}

- name: πŸ“¦ Build Production Container
if: ${{ github.ref == 'refs/heads/main' }}
deploy-staging:
name: 🚁 Deploy staging app for PR
runs-on: ubuntu-24.04
# Only run for PRs from the same repository (skip forks)
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }}
outputs:
url: ${{ steps.deploy.outputs.url }}
environment:
name: staging
url: ${{ steps.deploy.outputs.url }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: '50'
- name: πŸ‘€ Read app name
uses: SebRollen/[email protected]
id: app_name
with:
file: 'fly.toml'
field: 'app'

- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]

# Inspired by https://github.com/superfly/fly-pr-review-apps/blob/main/entrypoint.sh
- name: 🚁️ Deploy PR app to Fly.io
id: deploy
if: ${{ env.FLY_API_TOKEN }}
run: |
FLY_APP_NAME="${{ steps.app_name.outputs.value }}-pr-${{ github.event.number }}"
FLY_REGION=$(flyctl config show | jq -r '.primary_region')

# Create app if it doesn't exist
if ! flyctl status --app "$FLY_APP_NAME"; then
# change org name if needed
flyctl apps create $FLY_APP_NAME --org $FLY_ORG
flyctl secrets --app $FLY_APP_NAME set SESSION_SECRET=$(openssl rand -hex 32) HONEYPOT_SECRET=$(openssl rand -hex 32)
flyctl consul attach --app $FLY_APP_NAME
# Don't log the created tigris secrets!
flyctl storage create --app $FLY_APP_NAME --name epic-stack-$FLY_APP_NAME --yes > /dev/null 2>&1
fi

flyctl secrets --app $FLY_APP_NAME set SENTRY_DSN=${{ secrets.SENTRY_DSN }} RESEND_API_KEY=${{ secrets.RESEND_API_KEY }}

flyctl deploy \
--build-only \
--push \
--ha=false \
--regions $FLY_REGION \
--vm-size shared-cpu-1x \
--env APP_ENV=staging \
--env ALLOW_INDEXING=false \
--app $FLY_APP_NAME \
--image-label ${{ github.sha }} \
--build-arg COMMIT_SHA=${{ github.sha }} \
--build-secret SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \
--app ${{ steps.app_name.outputs.value }}
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

echo "url=https://$FLY_APP_NAME.fly.dev" >> $GITHUB_OUTPUT

cleanup-staging:
name: 🧹 Cleanup staging app
runs-on: ubuntu-24.04
if: ${{ github.event.action == 'closed' }}
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: πŸ‘€ Read app name
uses: SebRollen/[email protected]
id: app_name
with:
file: 'fly.toml'
field: 'app'

- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]

- name: 🧹 Cleanup resources
if: ${{ env.FLY_API_TOKEN }}
run: |
FLY_APP_NAME="${{ steps.app_name.outputs.value }}-pr-${{ github.event.number }}"
flyctl storage destroy epic-stack-$FLY_APP_NAME --yes || true
flyctl apps destroy "$FLY_APP_NAME" -y || true
deploy:
name: πŸš€ Deploy
name: πŸš€ Deploy production
runs-on: ubuntu-24.04
needs: [lint, typecheck, vitest, playwright, container]
# only deploy on pushes
if: ${{ github.event_name == 'push' }}
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
environment:
name: production
url: https://${{ steps.app_name.outputs.value }}.fly.dev
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
Expand All @@ -211,19 +287,7 @@ jobs:
- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]

- name: πŸš€ Deploy Staging
if: ${{ github.ref == 'refs/heads/dev' }}
run: |
flyctl deploy \
--image "registry.fly.io/${{ steps.app_name.outputs.value }}-staging:${{ github.sha }}" \
--app ${{ steps.app_name.outputs.value }}-staging
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

- name: πŸš€ Deploy Production
if: ${{ github.ref == 'refs/heads/main' }}
run: |
flyctl deploy \
--image "registry.fly.io/${{ steps.app_name.outputs.value }}:${{ github.sha }}"
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
166 changes: 166 additions & 0 deletions .github/workflows/reset-upstream-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: πŸ”„ Reset upstream-main Branch

on:
push:
branches:
- copilot/reset-upstream-main-branch
workflow_dispatch:
inputs:
target_commit:
description: 'Target commit SHA to reset to'
required: true
default: 'bac7bd445d5d4c7c602399a842518f40ec591f2d'
confirm:
description: 'Type "confirm" to proceed with the reset'
required: true

permissions:
contents: write

env:
DEFAULT_TARGET_COMMIT: 'bac7bd445d5d4c7c602399a842518f40ec591f2d'

jobs:
reset-branch:
name: Reset upstream-main to target commit
runs-on: ubuntu-22.04
# Only run on push events, or on workflow_dispatch with confirmation
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.confirm == 'confirm')

steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for all branches
token: ${{ secrets.GITHUB_TOKEN }}

- name: 🎯 Set target commit
id: set-target
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TARGET_COMMIT="${{ github.event.inputs.target_commit }}"
else
TARGET_COMMIT="${{ env.DEFAULT_TARGET_COMMIT }}"
fi
echo "target_commit=$TARGET_COMMIT" >> $GITHUB_OUTPUT
echo "Using target commit: $TARGET_COMMIT"

- name: πŸ” Verify target commit exists
run: |
TARGET_COMMIT="${{ steps.set-target.outputs.target_commit }}"
if ! git cat-file -e "$TARGET_COMMIT^{commit}"; then
echo "❌ Target commit $TARGET_COMMIT does not exist"
exit 1
fi
echo "βœ… Target commit exists: $TARGET_COMMIT"
git log --oneline -1 "$TARGET_COMMIT"

- name: πŸ“Š Show commits to be removed
run: |
TARGET_COMMIT="${{ steps.set-target.outputs.target_commit }}"
echo "The following commits will be REMOVED from upstream-main:"
git log --oneline "$TARGET_COMMIT..origin/upstream-main" || echo "No commits to remove (branch already at or before target)"

- name: πŸ”„ Reset upstream-main branch
run: |
TARGET_COMMIT="${{ steps.set-target.outputs.target_commit }}"

# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Fetch and checkout upstream-main
git fetch origin upstream-main
git checkout upstream-main

# Reset to target commit
git reset --hard "$TARGET_COMMIT"

echo "βœ… Local branch reset complete"
echo "Current HEAD:"
git log --oneline -1

- name: πŸš€ Force push to remote
run: |
git push origin upstream-main --force
echo "βœ… Successfully force pushed upstream-main to origin"

- name: βœ… Verify reset
run: |
TARGET_COMMIT="${{ steps.set-target.outputs.target_commit }}"

# Verify the remote branch
REMOTE_SHA=$(git ls-remote origin upstream-main | awk '{print $1}')

if [ "$REMOTE_SHA" = "$TARGET_COMMIT" ]; then
echo "βœ… SUCCESS: upstream-main branch has been reset to $TARGET_COMMIT"
else
echo "❌ ERROR: Remote branch is at $REMOTE_SHA, expected $TARGET_COMMIT"
exit 1
fi

echo ""
echo "Latest commits on upstream-main:"
git log --oneline upstream-main -5

verify-result:
name: Verify upstream-main reset result
runs-on: ubuntu-22.04
needs: reset-branch
if: always()

steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: πŸ” Check reset result
run: |
TARGET_COMMIT="${{ env.DEFAULT_TARGET_COMMIT }}"

# Fetch the latest state
git fetch origin upstream-main

# Get the current commit on upstream-main
CURRENT_SHA=$(git ls-remote origin upstream-main | awk '{print $1}')

echo "Target commit: $TARGET_COMMIT"
echo "Current upstream-main: $CURRENT_SHA"
echo ""

if [ "$CURRENT_SHA" = "$TARGET_COMMIT" ]; then
echo "βœ… VERIFICATION SUCCESS"
echo "The upstream-main branch is correctly reset to $TARGET_COMMIT"
echo ""
echo "Commits on upstream-main:"
git log --oneline "$CURRENT_SHA" -5
exit 0
else
echo "❌ VERIFICATION FAILED"
echo "The upstream-main branch is at $CURRENT_SHA but should be at $TARGET_COMMIT"
echo ""
echo "Difference:"
git log --oneline "$TARGET_COMMIT..$CURRENT_SHA" 2>/dev/null || echo "Target is ahead of current"
exit 1
fi

- name: πŸ“‹ Job summary
if: always()
run: |
TARGET_COMMIT="${{ env.DEFAULT_TARGET_COMMIT }}"
CURRENT_SHA=$(git ls-remote origin upstream-main | awk '{print $1}')

echo "## Reset Verification Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Target commit**: \`$TARGET_COMMIT\`" >> $GITHUB_STEP_SUMMARY
echo "- **Current commit**: \`$CURRENT_SHA\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "$CURRENT_SHA" = "$TARGET_COMMIT" ]; then
echo "βœ… **Status**: Reset successful" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Status**: Reset failed or incomplete" >> $GITHUB_STEP_SUMMARY
fi
Loading
Loading