poc for northflank #1254
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build & Deploy | |
on: | |
push: | |
branches: | |
- main | |
- feat/northflank-deploy | |
workflow_dispatch: | |
inputs: | |
force: | |
description: "Force deploy to all services" | |
default: true | |
type: boolean | |
nocache: | |
description: "Do not rely on existing layer cache" | |
default: false | |
type: boolean | |
deploy: | |
description: "Deploy changes upon successful build" | |
default: true | |
type: boolean | |
env: | |
REGION: us-central1 | |
PROJECT_ID: cask-382601 | |
GOOGLE_CLIENT_ID: "721909483682-uk3befic1j1krv3drig2puu30v1i4v48.apps.googleusercontent.com" | |
SENTRY_DSN: "https://[email protected]/4505138086019073" | |
SENTRY_ORG: "peated" | |
SENTRY_PROJECT: "peated" | |
API_SERVER: https://api.peated.com | |
URL_PREFIX: https://peated.com | |
FATHOM_SITE_ID: "OGNPFEUC" | |
REGISTRY: ghcr.io | |
IMAGE_NAME: ${{ github.repository }} | |
NF_CREDENTIALS_ID: github | |
NF_PROJECT_ID: default-project | |
jobs: | |
build: | |
name: 🚀 Build | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
runs-on: ubuntu-latest | |
permissions: | |
contents: "read" | |
id-token: "write" | |
if: ${{ inputs.deploy || github.event_name == 'push' }} | |
steps: | |
- name: ⬇️ Checkout repo | |
uses: actions/checkout@v3 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Log in to the Container registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ${{ env.REGISTRY }} | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Extract metadata (tags, labels) for Docker | |
id: meta | |
uses: docker/metadata-action@v5 | |
with: | |
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
- name: Record Version | |
id: vars | |
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT | |
- name: Verify CLI | |
id: verify_cli | |
run: | | |
if docker inspect "${{ env.IMAGE_NAME }}/cli:${{ github.sha }}"; then | |
echo "image_exists=true" >> "$GITHUB_OUTPUT" | |
else | |
echo "image_exists=false" >> "$GITHUB_OUTPUT" | |
fi | |
- name: Verify Web | |
id: verify_web | |
run: | | |
if docker inspect "${{ env.IMAGE_NAME }}/web:${{ github.sha }}"; then | |
echo "image_exists=true" >> "$GITHUB_OUTPUT" | |
else | |
echo "image_exists=false" >> "$GITHUB_OUTPUT" | |
fi | |
- name: Verify API | |
id: verify_api | |
run: | | |
if docker inspect "${{ env.IMAGE_NAME }}/api:${{ github.sha }}"; then | |
echo "image_exists=true" >> "$GITHUB_OUTPUT" | |
else | |
echo "image_exists=false" >> "$GITHUB_OUTPUT" | |
fi | |
- name: Verify Worker | |
id: verify_worker | |
run: | | |
if docker inspect "${{ env.IMAGE_NAME }}/worker:${{ github.sha }}"; then | |
echo "image_exists=true" >> "$GITHUB_OUTPUT" | |
else | |
echo "image_exists=false" >> "$GITHUB_OUTPUT" | |
fi | |
# optimize cache by building all stages | |
- name: Build All Stages | |
uses: docker/build-push-action@v5 | |
if: | | |
steps.verify_web.outputs.image_exists == 'false' | |
|| steps.verify_api.outputs.image_exists == 'false' | |
|| steps.verify_worker.outputs.image_exists == 'false' | |
|| steps.verify_cli.outputs.image_exists == 'false' | |
with: | |
context: . | |
cache-from: type=gha,scope=prod | |
cache-to: type=gha,mode=max,scope=prod | |
no-cache: ${{ inputs.nocache || false }} | |
build-args: | | |
VERSION=${{ github.sha }} | |
API_SERVER=${{ env.API_SERVER }} | |
URL_PREFIX=${{ env.URL_PREFIX }} | |
GOOGLE_CLIENT_ID=${{ env.GOOGLE_CLIENT_ID }} | |
SENTRY_DSN=${{ env.SENTRY_DSN }} | |
SENTRY_ORG=${{ env.SENTRY_ORG }} | |
SENTRY_PROJECT=${{ env.SENTRY_PROJECT }} | |
FATHOM_SITE_ID=${{ env.FATHOM_SITE_ID }} | |
secrets: | | |
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" | |
- name: Build Web | |
uses: docker/build-push-action@v5 | |
if: steps.verify_web.outputs.image_exists == 'false' | |
with: | |
context: . | |
cache-from: type=gha,scope=prod | |
no-cache: ${{ inputs.nocache || false }} | |
push: true | |
target: web | |
build-args: | | |
VERSION=${{ github.sha }} | |
API_SERVER=${{ env.API_SERVER }} | |
URL_PREFIX=${{ env.URL_PREFIX }} | |
GOOGLE_CLIENT_ID=${{ env.GOOGLE_CLIENT_ID }} | |
SENTRY_DSN=${{ env.SENTRY_DSN }} | |
SENTRY_ORG=${{ env.SENTRY_ORG }} | |
SENTRY_PROJECT=${{ env.SENTRY_PROJECT }} | |
FATHOM_SITE_ID=${{ env.FATHOM_SITE_ID }} | |
secrets: | | |
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" | |
tags: | | |
${{ env.IMAGE_NAME }}/web:${{ github.sha }} | |
- name: Build Worker | |
uses: docker/build-push-action@v5 | |
if: steps.verify_worker.outputs.image_exists == 'false' | |
with: | |
context: . | |
cache-from: type=gha,scope=prod | |
no-cache: ${{ inputs.nocache || false }} | |
push: true | |
target: worker | |
build-args: | | |
VERSION=${{ github.sha }} | |
API_SERVER={{ env.API_SERVER }} | |
URL_PREFIX=${{ env.URL_PREFIX }} | |
GOOGLE_CLIENT_ID=${{ env.GOOGLE_CLIENT_ID }} | |
SENTRY_DSN=${{ env.SENTRY_DSN }} | |
SENTRY_ORG=${{ env.SENTRY_ORG }} | |
SENTRY_PROJECT=${{ env.SENTRY_PROJECT }} | |
FATHOM_SITE_ID=${{ env.FATHOM_SITE_ID }} | |
secrets: | | |
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" | |
tags: | | |
${{ env.IMAGE_NAME }}/worker:${{ github.sha }} | |
- name: Build CLI | |
uses: docker/build-push-action@v5 | |
if: steps.verify_cli.outputs.image_exists == 'false' | |
with: | |
context: . | |
cache-from: type=gha,scope=prod | |
no-cache: ${{ inputs.nocache || false }} | |
push: true | |
target: cli | |
build-args: | | |
VERSION=${{ github.sha }} | |
API_SERVER={{ env.API_SERVER }} | |
URL_PREFIX=${{ env.URL_PREFIX }} | |
GOOGLE_CLIENT_ID=${{ env.GOOGLE_CLIENT_ID }} | |
SENTRY_DSN=${{ env.SENTRY_DSN }} | |
SENTRY_ORG=${{ env.SENTRY_ORG }} | |
SENTRY_PROJECT=${{ env.SENTRY_PROJECT }} | |
FATHOM_SITE_ID=${{ env.FATHOM_SITE_ID }} | |
secrets: | | |
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" | |
tags: | | |
${{ env.IMAGE_NAME }}/cli:${{ github.sha }} | |
- name: Build API | |
uses: docker/build-push-action@v5 | |
if: steps.verify_api.outputs.image_exists == 'false' | |
with: | |
context: . | |
cache-from: type=gha,scope=prod | |
no-cache: ${{ inputs.nocache || false }} | |
push: true | |
target: api | |
build-args: | | |
VERSION=${{ github.sha }} | |
API_SERVER=${{ env.API_SERVER }} | |
URL_PREFIX=${{ env.URL_PREFIX }} | |
GOOGLE_CLIENT_ID=${{ env.GOOGLE_CLIENT_ID }} | |
SENTRY_DSN=${{ env.SENTRY_DSN }} | |
SENTRY_ORG=${{ env.SENTRY_ORG }} | |
SENTRY_PROJECT=${{ env.SENTRY_PROJECT }} | |
FATHOM_SITE_ID=${{ env.FATHOM_SITE_ID }} | |
secrets: | | |
"SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" | |
tags: | | |
${{ env.IMAGE_NAME }}/api:${{ github.sha }} | |
deploy: | |
name: 🚀 Deploy | |
concurrency: | |
group: ${{ github.workflow }} | |
runs-on: ubuntu-latest | |
needs: [build] | |
permissions: | |
contents: "read" | |
id-token: "write" | |
deployments: "write" | |
steps: | |
- uses: chrnorm/deployment-action@v2 | |
name: Create GitHub deployment | |
id: deployment | |
with: | |
token: "${{ github.token }}" | |
environment-url: https://peated.app | |
environment: production | |
initial-status: in_progress | |
- name: ⬇️ Checkout repo | |
uses: actions/checkout@v3 | |
- name: Record Version | |
id: vars | |
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT | |
- name: Get changed files | |
id: changed-files-yaml | |
uses: tj-actions/changed-files@v41 | |
with: | |
files_yaml: | | |
server: | |
- apps/server/** | |
cli: | |
- apps/cli/** | |
web: | |
- apps/web/** | |
common: | |
- packages/** | |
- apps/server** | |
- Dockerfile | |
- package.json | |
- .github/workflows/deploy.yml | |
migrations: | |
- apps/**/migrations/*.sql | |
- packages/**/migrations/*.sql | |
- .github/workflows/deploy.yml | |
- name: Update Migrate DB | |
uses: northflank/deploy-to-northflank@v1 | |
with: | |
northflank-api-key: ${{ secrets.NORTHFLANK_API_KEY }} | |
project-id: ${{ env.NF_PROJECT_ID }} | |
service-id: migrate-db | |
image-path: ${{ steps.meta.outputs.tags }} | |
credentials-id: ${{ env.NF_CREDENTIALS_ID }} | |
# - name: Run Migrate DB | |
# if: ${{ inputs.force || steps.changed-files-yaml.outputs.migrations_any_changed == 'true' }} | |
# run: |- | |
# gcloud run jobs execute cli \ | |
# --region ${{ env.REGION }} \ | |
# --args db,migrate \ | |
# --wait | |
- name: Deploy API | |
if: ${{ inputs.force || steps.changed-files-yaml.outputs.server_any_changed == 'true' || steps.changed-files-yaml.outputs.common_any_changed == 'true' }} | |
uses: northflank/deploy-to-northflank@v1 | |
with: | |
northflank-api-key: ${{ secrets.NORTHFLANK_API_KEY }} | |
project-id: ${{ env.NF_PROJECT_ID }} | |
service-id: api | |
image-path: ${{ steps.meta.outputs.tags }} | |
credentials-id: ${{ env.NF_CREDENTIALS_ID }} | |
- name: Deploy Web | |
if: ${{ inputs.force || steps.changed-files-yaml.outputs.web_any_changed == 'true' || steps.changed-files-yaml.outputs.common_any_changed == 'true' }} | |
uses: northflank/deploy-to-northflank@v1 | |
with: | |
northflank-api-key: ${{ secrets.NORTHFLANK_API_KEY }} | |
project-id: ${{ env.NF_PROJECT_ID }} | |
service-id: web | |
image-path: ${{ steps.meta.outputs.tags }} | |
credentials-id: ${{ env.NF_CREDENTIALS_ID }} | |
- name: Deploy Worker | |
if: ${{ inputs.force || steps.changed-files-yaml.outputs.worker_any_changed == 'true' || steps.changed-files-yaml.outputs.common_any_changed == 'true' }} | |
uses: northflank/deploy-to-northflank@v1 | |
with: | |
northflank-api-key: ${{ secrets.NORTHFLANK_API_KEY }} | |
project-id: ${{ env.NF_PROJECT_ID }} | |
service-id: worker | |
image-path: ${{ steps.meta.outputs.tags }} | |
credentials-id: ${{ env.NF_CREDENTIALS_ID }} | |
- name: Discord notification (success) | |
if: success() | |
env: | |
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} | |
uses: Ilshidur/[email protected] | |
with: | |
args: | | |
"${{ github.actor }} deployed version [${{ steps.vars.outputs.sha_short }}](https://github.com/{{ EVENT_PAYLOAD.repository.full_name }}/commit/${{ github.sha }})." | |
- name: Discord notification (failure) | |
if: failure() | |
env: | |
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} | |
uses: Ilshidur/[email protected] | |
with: | |
args: | | |
"ERROR: Failed to deploy version [${{ steps.vars.outputs.sha_short }}](https://github.com/{{ EVENT_PAYLOAD.repository.full_name }}/commit/${{ github.sha }})." | |
- name: Update deployment status (success) | |
if: success() | |
uses: chrnorm/deployment-status@v2 | |
with: | |
token: "${{ github.token }}" | |
environment-url: ${{ steps.deployment.outputs.environment_url }} | |
deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
state: "success" | |
- name: Update deployment status (failure) | |
if: failure() | |
uses: chrnorm/deployment-status@v2 | |
with: | |
token: "${{ github.token }}" | |
environment-url: ${{ steps.deployment.outputs.environment_url }} | |
deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
state: "failure" |