Skip to content
Merged
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
116 changes: 116 additions & 0 deletions .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
on:
workflow_dispatch:

concurrency:
group: prod-deploy-${{ github.repository }}
cancel-in-progress: false

permissions:
contents: read

jobs:
verify:
uses: ./.github/workflows/ci.yml

release:
name: Publish images and deploy production
needs:
- verify
runs-on: ubuntu-latest
environment:
name: production
env:
AWS_REGION: ${{ secrets.AWS_REGION }}

steps:
- uses: actions/checkout@v4

- name: Configure AWS credentials
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: ${{ env.AWS_REGION }}

- id: aws-account
name: Resolve account id
run: echo "account_id=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_OUTPUT"

- name: Compute registry host
run: >
echo "REGISTRY=${{ steps.aws-account.outputs.account_id }}.dkr.ecr.${AWS_REGION}.amazonaws.com"
>> "$GITHUB_ENV"

- name: Log in to ECR from the Actions runner
uses: aws-actions/amazon-ecr-login@v2

- uses: docker/setup-buildx-action@v3

- name: Push web image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.web
push: true
tags: |
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_WEB }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_WEB }}:prod-latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Push backend image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.backend
push: true
tags: |
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_BACKEND }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_BACKEND }}:prod-latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Ensure remote directories exist
uses: appleboy/ssh-action@v1.2.5
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
script_stop: true
script: mkdir -p /opt/nodejs-monorepo/scripts

- name: Upload docker-compose.prod.yml
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
source: docker-compose.prod.yml
target: /opt/nodejs-monorepo/

- name: Upload deploy-ec2-compose.sh
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
source: scripts/deploy-ec2-compose.sh
target: /opt/nodejs-monorepo/scripts/

- name: Roll stack on EC2
uses: appleboy/ssh-action@v1.2.5
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
script_stop: true
script: |
set -eu
cd /opt/nodejs-monorepo
chmod +x scripts/deploy-ec2-compose.sh
export IMAGE_TAG='${{ github.sha }}'
export ECR_REGISTRY='${{ env.REGISTRY }}'
export AWS_REGION='${{ env.AWS_REGION }}'
export ECR_REPOSITORY_WEB='${{ secrets.ECR_REPOSITORY_WEB }}'
export ECR_REPOSITORY_BACKEND='${{ secrets.ECR_REPOSITORY_BACKEND }}'
./scripts/deploy-ec2-compose.sh
116 changes: 116 additions & 0 deletions .github/workflows/cd-stage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
on:
workflow_dispatch:

concurrency:
group: staging-deploy-${{ github.repository }}
cancel-in-progress: false

permissions:
contents: read

jobs:
verify:
uses: ./.github/workflows/ci.yml

release:
name: Publish images and deploy
needs:
- verify
runs-on: ubuntu-latest
environment:
name: staging
env:
AWS_REGION: ${{ secrets.AWS_REGION }}

steps:
- uses: actions/checkout@v4

- name: Configure AWS credentials
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: ${{ env.AWS_REGION }}

- id: aws-account
name: Resolve account id
run: echo "account_id=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_OUTPUT"

- name: Compute registry host
run: >
echo "REGISTRY=${{ steps.aws-account.outputs.account_id }}.dkr.ecr.${AWS_REGION}.amazonaws.com"
>> "$GITHUB_ENV"

- name: Log in to ECR from the Actions runner
uses: aws-actions/amazon-ecr-login@v2

- uses: docker/setup-buildx-action@v3

- name: Push web image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.web
push: true
tags: |
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_WEB }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_WEB }}:staging-latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Push backend image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.backend
push: true
tags: |
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_BACKEND }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ secrets.ECR_REPOSITORY_BACKEND }}:staging-latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Ensure remote directories exist
uses: appleboy/ssh-action@v1.2.5
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
script_stop: true
script: mkdir -p /opt/nodejs-monorepo/scripts

- name: Upload docker-compose.prod.yml
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
source: docker-compose.prod.yml
target: /opt/nodejs-monorepo/

- name: Upload deploy-ec2-compose.sh
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
source: scripts/deploy-ec2-compose.sh
target: /opt/nodejs-monorepo/scripts/

- name: Roll stack on EC2
uses: appleboy/ssh-action@v1.2.5
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_SSH_USERNAME }}
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
script_stop: true
script: |
set -eu
cd /opt/nodejs-monorepo
chmod +x scripts/deploy-ec2-compose.sh
export IMAGE_TAG='${{ github.sha }}'
export ECR_REGISTRY='${{ env.REGISTRY }}'
export AWS_REGION='${{ env.AWS_REGION }}'
export ECR_REPOSITORY_WEB='${{ secrets.ECR_REPOSITORY_WEB }}'
export ECR_REPOSITORY_BACKEND='${{ secrets.ECR_REPOSITORY_BACKEND }}'
./scripts/deploy-ec2-compose.sh
82 changes: 82 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: CI

on:
pull_request:
push:
branches:
- main
- dev
workflow_dispatch:
workflow_call:

concurrency:
group: ci-${{ github.repository }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

permissions:
contents: read

jobs:
check:
name: Lint and build workspaces
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json

- name: Install npm dependencies
run: npm ci --no-audit --no-fund

- name: Minimal POSTGRES_URL for Prisma tooling
run: printf '%s\n' 'POSTGRES_URL=postgresql://ci:ci@127.0.0.1:5432/ci' > .env

- name: Lint
run: |
npm run lint:check --if-present --workspaces=false
npm run lint:format:check --if-present --workspaces=false
npm run lint:filesystem:check --if-present

- name: Knip
run: npm run lint:clean:check --if-present --workspaces=false

- name: Typecheck workspaces
run: |
npm run lint:types:check -w packages/shared --if-present
npm run lint:types:check -w apps/web --if-present
npm run lint:types:check -w apps/backend --if-present

- name: Build workspaces
run: |
npm run build -w @nodejs-monorepo/shared
npm run db:generate -w @nodejs-monorepo/backend
npm run build -w @nodejs-monorepo/backend
npm run build -w @nodejs-monorepo/web

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build web image (CI only)
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.web
push: false
tags: nodejs-monorepo-web:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build backend image (CI only)
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.backend
push: false
tags: nodejs-monorepo-backend:ci
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,4 @@ vite.config.ts.timestamp-*

.vscode

apps/backend/prisma/generated

*.pem
16 changes: 15 additions & 1 deletion .ls-lint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# Filesystem naming: match repo layout (apps/*/src, packages/*/src).
# Docs: README.md — Filesystem naming
ls:
src:
apps/*/src:
.dir: kebab-case
.ts: kebab-case
.tsx: kebab-case

packages/*/src:
.dir: kebab-case
.ts: kebab-case
.tsx: kebab-case

ignore:
- node_modules
- dist
- .git
- "**/prisma/migrations/**"
Loading
Loading