Concurrency. release.yml calls ci.yml, so a push to main started the called workflow and the standalone one, both landing in the concurrency group ci-<ref>. One cancelled the other and every merge left a cancelled run behind. main is now excluded from the push trigger: release.yml is already the entry point there, and pull_request still covers changes before they land. Node 20 deprecation (23 warnings). Every action was pinned to a major that still declares node20. Each replacement was checked by reading the action.yml at the new tag and confirming "using: node24", and the inputs in use were confirmed to still exist there: checkout v4->v7, setup-node v4->v7, setup-go v5->v7, upload-artifact v4->v7, download-artifact v4->v7, setup-buildx-action v3->v4, build-push-action v6->v7, login-action v3->v4, metadata-action v5->v6 download-artifact has a v8, but v7 is also node24 and matches the upload side, so the pair stays on one major. trivy-action is a composite action and has no Node runtime to deprecate. Go cache (1 warning). setup-go looked for go.sum at the repository root and skipped its cache: defaults.run.working-directory applies to run steps, not to actions. cache-dependency-path now points at services/protocols/go.sum. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
179 lines
5.5 KiB
YAML
179 lines
5.5 KiB
YAML
name: CI
|
|
|
|
# Runs on every branch and pull request. Publishing lives in release.yml and
|
|
# only runs once this workflow has passed.
|
|
#
|
|
# main is excluded from the push trigger on purpose: release.yml already calls
|
|
# this workflow there. Running both would put two jobs in the same concurrency
|
|
# group, and one would cancel the other - which is why every merge used to
|
|
# leave a cancelled CI run behind.
|
|
on:
|
|
push:
|
|
branches-ignore: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_call:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
lang-lint:
|
|
name: Lint de idioma (R9)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: node scripts/lang-lint.mjs
|
|
|
|
go-protocols:
|
|
name: Go (protocols)
|
|
runs-on: ubuntu-latest
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_DB: lambdamail_test
|
|
POSTGRES_USER: lambdamail_test
|
|
POSTGRES_PASSWORD: lambdamail_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 10
|
|
defaults:
|
|
run:
|
|
working-directory: services/protocols
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-go@v7
|
|
with:
|
|
go-version-file: services/protocols/go.mod
|
|
# defaults.run.working-directory applies to run steps, not to
|
|
# actions, so setup-go looks for go.sum at the repository root and
|
|
# silently skips its cache without this.
|
|
cache-dependency-path: services/protocols/go.sum
|
|
- name: Run migrations
|
|
run: |
|
|
docker run --rm --network host \
|
|
-v "$(pwd)/../../migrations:/migrations" \
|
|
migrate/migrate:v4.17.1 \
|
|
-path=/migrations \
|
|
-database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \
|
|
up
|
|
- run: go vet ./...
|
|
- run: go test ./... -v
|
|
env:
|
|
TEST_DATABASE_URL: postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable
|
|
|
|
ts-auth:
|
|
name: TypeScript (auth)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: services/auth
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
- run: npm install
|
|
- run: npm run lint
|
|
- run: npm test
|
|
|
|
ts-storage:
|
|
name: TypeScript (storage)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: services/storage
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
- run: npm install
|
|
- run: npm run lint
|
|
- run: npm test
|
|
|
|
webmail-build:
|
|
name: Build (webmail)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: services/webmail
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-node@v7
|
|
with: { node-version: "20" }
|
|
- run: npm install
|
|
- run: npm run build
|
|
|
|
security:
|
|
name: Trivy (dependências, segredos e Dockerfiles)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
# A mail server holds credentials for every mailbox it serves, so the
|
|
# secret scanner matters as much as the vulnerability one here: a DKIM
|
|
# key or a Cloudflare token committed by accident is a full compromise.
|
|
- name: Repositório
|
|
uses: aquasecurity/trivy-action@v0.36.0
|
|
with:
|
|
scan-type: fs
|
|
scanners: vuln,secret,misconfig
|
|
severity: HIGH,CRITICAL
|
|
exit-code: '1'
|
|
skip-dirs: services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next
|
|
|
|
image:
|
|
name: Imagem (${{ matrix.service }})
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
service: [protocols, auth, storage, webmail]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: docker/setup-buildx-action@v4
|
|
|
|
# Built but not pushed: this proves each Dockerfile still works on every
|
|
# branch, while publishing stays in release.yml.
|
|
- name: Build
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: services/${{ matrix.service }}
|
|
push: false
|
|
load: true
|
|
tags: lambdamail-${{ matrix.service }}:ci
|
|
cache-from: type=gha,scope=${{ matrix.service }}
|
|
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
|
|
|
|
- name: Trivy na imagem
|
|
uses: aquasecurity/trivy-action@v0.36.0
|
|
with:
|
|
image-ref: lambdamail-${{ matrix.service }}:ci
|
|
severity: HIGH,CRITICAL
|
|
exit-code: '1'
|
|
|
|
compose-up:
|
|
name: Stack completa
|
|
runs-on: ubuntu-latest
|
|
needs: [go-protocols, ts-auth, ts-storage, webmail-build]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: cp .env.example .env
|
|
# The proxy network is declared external because Coolify owns it on the
|
|
# deploy host. Nothing creates it here, and without it compose refuses to
|
|
# start the stack at all.
|
|
- name: Cria a rede do proxy
|
|
run: docker network create "${COOLIFY_PROXY_NETWORK:-coolify}"
|
|
# --build makes compose build each service locally and tag it under the
|
|
# same name the published image uses, so the stack comes up without ever
|
|
# reaching the registry.
|
|
- run: docker compose --env-file .env up --build --wait
|
|
- run: docker compose ps
|
|
- run: docker compose down -v
|