Compare commits
10 Commits
feat/whats
...
feat/gitea
| Author | SHA1 | Date | |
|---|---|---|---|
| 26a68e1823 | |||
| ac08b5f064 | |||
| 91c0d412fa | |||
| dd82ca2dca | |||
| 87a019109d | |||
| 1468a23145 | |||
| f730e766f5 | |||
| 1e215ac3d2 | |||
| 716196dbe0 | |||
| 84f6d7c31a |
@@ -6,7 +6,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
group: ci-${{ gitea.workflow }}-${{ gitea.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -18,7 +18,9 @@ env:
|
|||||||
BCRYPT_ROUNDS: 4
|
BCRYPT_ROUNDS: 4
|
||||||
CACHE_STORE: database
|
CACHE_STORE: database
|
||||||
DB_CONNECTION: pgsql
|
DB_CONNECTION: pgsql
|
||||||
DB_HOST: 127.0.0.1
|
# Service hostname on the per-job network (act_runner with empty
|
||||||
|
# container.network). Do not publish host :5432/:8000 — parallel jobs collide.
|
||||||
|
DB_HOST: postgres
|
||||||
DB_PORT: 5432
|
DB_PORT: 5432
|
||||||
DB_DATABASE: amare_test
|
DB_DATABASE: amare_test
|
||||||
DB_USERNAME: amare
|
DB_USERNAME: amare
|
||||||
@@ -40,12 +42,6 @@ jobs:
|
|||||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.composer/cache/files
|
|
||||||
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
|
|
||||||
restore-keys: composer-${{ runner.os }}-
|
|
||||||
|
|
||||||
- run: composer validate --strict
|
- run: composer validate --strict
|
||||||
- run: composer install --no-interaction --prefer-dist
|
- run: composer install --no-interaction --prefer-dist
|
||||||
- run: composer pint:check
|
- run: composer pint:check
|
||||||
@@ -74,8 +70,6 @@ jobs:
|
|||||||
POSTGRES_DB: amare_test
|
POSTGRES_DB: amare_test
|
||||||
POSTGRES_USER: amare
|
POSTGRES_USER: amare
|
||||||
POSTGRES_PASSWORD: secret
|
POSTGRES_PASSWORD: secret
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "pg_isready -U amare -d amare_test"
|
--health-cmd "pg_isready -U amare -d amare_test"
|
||||||
--health-interval 5s
|
--health-interval 5s
|
||||||
@@ -91,18 +85,6 @@ jobs:
|
|||||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
||||||
coverage: pcov
|
coverage: pcov
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.composer/cache/files
|
|
||||||
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
|
|
||||||
restore-keys: composer-${{ runner.os }}-
|
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: npm-${{ runner.os }}-
|
|
||||||
|
|
||||||
- run: composer install --no-interaction --prefer-dist
|
- run: composer install --no-interaction --prefer-dist
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
@@ -126,8 +108,6 @@ jobs:
|
|||||||
POSTGRES_DB: amare_test
|
POSTGRES_DB: amare_test
|
||||||
POSTGRES_USER: amare
|
POSTGRES_USER: amare
|
||||||
POSTGRES_PASSWORD: secret
|
POSTGRES_PASSWORD: secret
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "pg_isready -U amare -d amare_test"
|
--health-cmd "pg_isready -U amare -d amare_test"
|
||||||
--health-interval 5s
|
--health-interval 5s
|
||||||
@@ -143,18 +123,6 @@ jobs:
|
|||||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.composer/cache/files
|
|
||||||
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
|
|
||||||
restore-keys: composer-${{ runner.os }}-
|
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: npm-${{ runner.os }}-
|
|
||||||
|
|
||||||
- run: composer install --no-interaction --prefer-dist
|
- run: composer install --no-interaction --prefer-dist
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
@@ -171,8 +139,6 @@ jobs:
|
|||||||
POSTGRES_DB: amare_test
|
POSTGRES_DB: amare_test
|
||||||
POSTGRES_USER: amare
|
POSTGRES_USER: amare
|
||||||
POSTGRES_PASSWORD: secret
|
POSTGRES_PASSWORD: secret
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "pg_isready -U amare -d amare_test"
|
--health-cmd "pg_isready -U amare -d amare_test"
|
||||||
--health-interval 5s
|
--health-interval 5s
|
||||||
@@ -188,18 +154,6 @@ jobs:
|
|||||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.composer/cache/files
|
|
||||||
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
|
|
||||||
restore-keys: composer-${{ runner.os }}-
|
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: npm-${{ runner.os }}-
|
|
||||||
|
|
||||||
- run: composer install --no-interaction --prefer-dist
|
- run: composer install --no-interaction --prefer-dist
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
@@ -212,15 +166,28 @@ jobs:
|
|||||||
|
|
||||||
- name: Run browser tests against FrankenPHP container
|
- name: Run browser tests against FrankenPHP container
|
||||||
run: |
|
run: |
|
||||||
docker run -d --name amare-web \
|
# Join the per-job network (act_runner creates one when
|
||||||
|
# container.network is empty). No host -p: parallel jobs would
|
||||||
|
# collide on :8000/:5432; DNS names work on the job network.
|
||||||
|
# Container --name is global on the shared docker.sock host —
|
||||||
|
# include run id or leftovers from cancelled jobs Conflict.
|
||||||
|
JOB_CID="$(hostname)"
|
||||||
|
JOB_NET="$(docker inspect -f '{{range $k, $_ := .NetworkSettings.Networks}}{{println $k}}{{end}}' "$JOB_CID" | head -n1)"
|
||||||
|
test -n "$JOB_NET"
|
||||||
|
WEB_NAME="amare-web-${GITHUB_RUN_ID:-$$}"
|
||||||
|
docker rm -f "$WEB_NAME" 2>/dev/null || true
|
||||||
|
|
||||||
|
docker run -d --name "$WEB_NAME" \
|
||||||
|
--network "$JOB_NET" \
|
||||||
|
--network-alias amare-web \
|
||||||
-e APP_ENV=testing \
|
-e APP_ENV=testing \
|
||||||
-e APP_KEY="${APP_KEY}" \
|
-e APP_KEY="${APP_KEY}" \
|
||||||
-e APP_URL=http://127.0.0.1:8000 \
|
-e APP_URL=http://amare-web:8000 \
|
||||||
-e APP_LOCALE=pt_BR \
|
-e APP_LOCALE=pt_BR \
|
||||||
-e APP_FALLBACK_LOCALE=pt_BR \
|
-e APP_FALLBACK_LOCALE=pt_BR \
|
||||||
-e APP_TIMEZONE=America/Sao_Paulo \
|
-e APP_TIMEZONE=America/Sao_Paulo \
|
||||||
-e DB_CONNECTION=pgsql \
|
-e DB_CONNECTION=pgsql \
|
||||||
-e DB_HOST=host.docker.internal \
|
-e DB_HOST=postgres \
|
||||||
-e DB_PORT=5432 \
|
-e DB_PORT=5432 \
|
||||||
-e DB_DATABASE=amare_test \
|
-e DB_DATABASE=amare_test \
|
||||||
-e DB_USERNAME=amare \
|
-e DB_USERNAME=amare \
|
||||||
@@ -228,26 +195,28 @@ jobs:
|
|||||||
-e SESSION_DRIVER=database \
|
-e SESSION_DRIVER=database \
|
||||||
-e CACHE_STORE=database \
|
-e CACHE_STORE=database \
|
||||||
-e QUEUE_CONNECTION=database \
|
-e QUEUE_CONNECTION=database \
|
||||||
--add-host=host.docker.internal:host-gateway \
|
|
||||||
-v "${GITHUB_WORKSPACE}/storage/app/public:/app/storage/app/public" \
|
-v "${GITHUB_WORKSPACE}/storage/app/public:/app/storage/app/public" \
|
||||||
-p 8000:8000 \
|
|
||||||
amare-app:ci
|
amare-app:ci
|
||||||
|
|
||||||
|
cleanup() { docker rm -f "$WEB_NAME" >/dev/null 2>&1 || true; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
if curl -fsS http://127.0.0.1:8000/up; then
|
if curl -fsS http://amare-web:8000/up; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
curl -fsS http://127.0.0.1:8000/up
|
curl -fsS http://amare-web:8000/up
|
||||||
./vendor/bin/pest --testsuite=Browser
|
APP_URL=http://amare-web:8000 ./vendor/bin/pest --testsuite=Browser
|
||||||
|
|
||||||
- name: Collect failure diagnostics
|
- name: Collect failure diagnostics
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
mkdir -p artifacts/browser
|
mkdir -p artifacts/browser
|
||||||
docker logs amare-web > artifacts/browser/container.log 2>&1 || true
|
WEB_NAME="amare-web-${GITHUB_RUN_ID:-$$}"
|
||||||
|
docker logs "$WEB_NAME" > artifacts/browser/container.log 2>&1 || true
|
||||||
cp -R storage/logs artifacts/browser/app-logs 2>/dev/null || true
|
cp -R storage/logs artifacts/browser/app-logs 2>/dev/null || true
|
||||||
|
|
||||||
- name: Upload browser failure artifacts
|
- name: Upload browser failure artifacts
|
||||||
@@ -269,10 +238,21 @@ jobs:
|
|||||||
|
|
||||||
- name: Verify container healthcheck and storage link
|
- name: Verify container healthcheck and storage link
|
||||||
run: |
|
run: |
|
||||||
docker run -d --name amare-health \
|
# Same per-job network as the step container — no host :8000
|
||||||
|
# publish (collides when capacity > 1). Unique --name: docker.sock
|
||||||
|
# is shared across jobs; leftovers from cancelled runs Conflict.
|
||||||
|
JOB_CID="$(hostname)"
|
||||||
|
JOB_NET="$(docker inspect -f '{{range $k, $_ := .NetworkSettings.Networks}}{{println $k}}{{end}}' "$JOB_CID" | head -n1)"
|
||||||
|
test -n "$JOB_NET"
|
||||||
|
HEALTH_NAME="amare-health-${GITHUB_RUN_ID:-$$}"
|
||||||
|
docker rm -f "$HEALTH_NAME" 2>/dev/null || true
|
||||||
|
|
||||||
|
docker run -d --name "$HEALTH_NAME" \
|
||||||
|
--network "$JOB_NET" \
|
||||||
|
--network-alias amare-health \
|
||||||
-e APP_ENV=production \
|
-e APP_ENV=production \
|
||||||
-e APP_KEY="${{ env.APP_KEY }}" \
|
-e APP_KEY="${{ env.APP_KEY }}" \
|
||||||
-e APP_URL=http://127.0.0.1:8000 \
|
-e APP_URL=http://amare-health:8000 \
|
||||||
-e APP_DEBUG=false \
|
-e APP_DEBUG=false \
|
||||||
-e DB_CONNECTION=pgsql \
|
-e DB_CONNECTION=pgsql \
|
||||||
-e DB_HOST=127.0.0.1 \
|
-e DB_HOST=127.0.0.1 \
|
||||||
@@ -280,16 +260,18 @@ jobs:
|
|||||||
-e DB_DATABASE=amare \
|
-e DB_DATABASE=amare \
|
||||||
-e DB_USERNAME=amare \
|
-e DB_USERNAME=amare \
|
||||||
-e DB_PASSWORD=secret \
|
-e DB_PASSWORD=secret \
|
||||||
-p 8000:8000 \
|
|
||||||
amare-app:ci
|
amare-app:ci
|
||||||
|
|
||||||
|
cleanup() { docker rm -f "$HEALTH_NAME" >/dev/null 2>&1 || true; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
if curl -fsS http://127.0.0.1:8000/up; then
|
if curl -fsS http://amare-health:8000/up; then
|
||||||
docker exec amare-health test -L /app/public/storage
|
docker exec "$HEALTH_NAME" test -L /app/public/storage
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
docker logs amare-health
|
docker logs "$HEALTH_NAME"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
name: Deploy staging
|
name: Deploy staging
|
||||||
|
|
||||||
|
# Requires Gitea >= 1.25 for workflow_run (1.24.x has no trigger match).
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: [CI]
|
workflows:
|
||||||
types: [completed]
|
- CI
|
||||||
branches: [main]
|
- ci.yml
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -15,27 +21,28 @@ concurrency:
|
|||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: git.hellomanoel.com
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
IMAGE_NAME: ${{ gitea.repository }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: publish-and-deploy-staging
|
name: publish-and-deploy-staging
|
||||||
if: >-
|
if: >-
|
||||||
github.event.workflow_run.conclusion == 'success' &&
|
gitea.event_name == 'workflow_dispatch' ||
|
||||||
github.event.workflow_run.event == 'push' &&
|
(gitea.event.workflow_run.conclusion == 'success' &&
|
||||||
github.event.workflow_run.head_branch == 'main'
|
gitea.event.workflow_run.event == 'push' &&
|
||||||
|
gitea.event.workflow_run.head_branch == 'main')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout deployed SHA
|
- name: Checkout deployed SHA
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.workflow_run.head_sha }}
|
ref: ${{ gitea.event.workflow_run.head_sha || gitea.sha }}
|
||||||
|
|
||||||
- name: Set image metadata
|
- name: Set image metadata
|
||||||
id: meta
|
id: meta
|
||||||
run: |
|
run: |
|
||||||
SHA="${{ github.event.workflow_run.head_sha }}"
|
SHA="${{ gitea.event.workflow_run.head_sha || gitea.sha }}"
|
||||||
SHORT_SHA="${SHA:0:7}"
|
SHORT_SHA="${SHA:0:7}"
|
||||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||||
IMAGE="$(echo "$IMAGE" | tr '[:upper:]' '[:lower:]')"
|
IMAGE="$(echo "$IMAGE" | tr '[:upper:]' '[:lower:]')"
|
||||||
@@ -43,12 +50,14 @@ jobs:
|
|||||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||||
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to Gitea registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
# Gitea's GITEA_TOKEN cannot push OCI packages (gitea#23642); a PAT
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
# with read:package/write:package scopes is required instead.
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_PAT }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -61,8 +70,6 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.sha }}
|
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.sha }}
|
||||||
${{ steps.meta.outputs.image }}:staging
|
${{ steps.meta.outputs.image }}:staging
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Deploy staging on Dokploy
|
- name: Deploy staging on Dokploy
|
||||||
env:
|
env:
|
||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
sha:
|
sha:
|
||||||
description: Full git SHA already published to GHCR (same digest used by staging)
|
description: Full git SHA already published to the Gitea registry (same digest used by staging)
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
confirm:
|
confirm:
|
||||||
@@ -21,8 +21,8 @@ concurrency:
|
|||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: git.hellomanoel.com
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
IMAGE_NAME: ${{ gitea.repository }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
promote:
|
promote:
|
||||||
@@ -50,12 +50,14 @@ jobs:
|
|||||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||||
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to Gitea registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
# Gitea's GITEA_TOKEN cannot push OCI packages (gitea#23642); a PAT
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
# with read:package/write:package scopes is required instead.
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_PAT }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
11
CLAUDE.md
11
CLAUDE.md
@@ -22,13 +22,14 @@ Write for the problem at hand, not an imagined future. **DRY**: extract and reus
|
|||||||
|
|
||||||
PHP and Composer are **not on PATH** in this environment, and `vendor/` and `node_modules/` are absent. Every `composer …` / `php artisan …` command in `AGENTS.md` and `README.md` assumes a PHP 8.4+ runtime with Composer 2 installed. Verify the toolchain before promising a command ran.
|
PHP and Composer are **not on PATH** in this environment, and `vendor/` and `node_modules/` are absent. Every `composer …` / `php artisan …` command in `AGENTS.md` and `README.md` assumes a PHP 8.4+ runtime with Composer 2 installed. Verify the toolchain before promising a command ran.
|
||||||
|
|
||||||
## Git remote auth — two GitHub accounts
|
## Git remotes — Gitea origin, GitHub legacy
|
||||||
|
|
||||||
`origin` is `git@github.com:manoel-freitas/amore-site.git`, owned by the **`manoel-freitas`** account. The machine's default SSH identity is a different account (`manoel-freitas-neto`) that cannot see this repo, so pushes fail with `ERROR: Repository not found.` — an access error that reads like a missing repo.
|
`origin` is `git@git.hellomanoel.com:manoel-freitas/amare.git` (self-hosted Gitea). CI/CD and the container registry live there (`git.hellomanoel.com`). Verify SSH with `ssh -T git@git.hellomanoel.com` → should greet `Hi there, manoel-freitas!`.
|
||||||
|
|
||||||
- Correct key: `~/.ssh/id_github_pessoal`. Verify with `ssh -i ~/.ssh/id_github_pessoal -o IdentitiesOnly=yes -T git@github.com` → should greet `Hi manoel-freitas!`.
|
The remote named `github` is the legacy mirror `git@github.com:manoel-freitas/amore-site.git`. Push there only when intentionally syncing the backup. That GitHub account still needs `~/.ssh/id_github_pessoal` (or an equivalent key) when the machine's default identity is a different GitHub user (`manoel-freitas-neto`) that cannot see the repo.
|
||||||
- The repo has `core.sshCommand = ssh -i ~/.ssh/id_github_pessoal -o IdentitiesOnly=yes` set locally, so plain `git push` works. If that config is lost, restore it instead of editing the remote URL.
|
|
||||||
- **`gh` authenticates separately**, by token rather than SSH key. As of 2026-08-10 it is logged in as `manoel-freitas`, so `gh pr create` / `gh repo view` work. Confirm with `gh auth status` before assuming: if it reports `manoel-freitas-neto`, that account cannot see this repo and every `gh` call fails on it. Recovering needs an interactive `gh auth login` (or `gh auth switch` with both accounts added), so ask the user to run it.
|
- Prefer plain `git push` / `git push origin <branch>` against Gitea.
|
||||||
|
- **`gh`** talks to GitHub only. Use the Gitea web UI or API for PRs on `amare`. If you still need `gh` against the legacy remote, confirm `gh auth status` shows `manoel-freitas`.
|
||||||
|
|
||||||
## Request spine for the public site
|
## Request spine for the public site
|
||||||
|
|
||||||
|
|||||||
@@ -138,10 +138,10 @@ Após `php artisan db:seed`:
|
|||||||
- [docs/adr/](docs/adr/) — ADRs aceitas
|
- [docs/adr/](docs/adr/) — ADRs aceitas
|
||||||
- [docs/conventions/php-strict-types.md](docs/conventions/php-strict-types.md) — convenção de strict types
|
- [docs/conventions/php-strict-types.md](docs/conventions/php-strict-types.md) — convenção de strict types
|
||||||
- [docs/operations/atualizacao-de-conteudo.md](docs/operations/atualizacao-de-conteudo.md) — runbook de atualização de conteúdo do site pelo painel admin
|
- [docs/operations/atualizacao-de-conteudo.md](docs/operations/atualizacao-de-conteudo.md) — runbook de atualização de conteúdo do site pelo painel admin
|
||||||
- [docs/deployment/dokploy.md](docs/deployment/dokploy.md) — deploy staging/produção no Dokploy + GHCR
|
- [docs/deployment/dokploy.md](docs/deployment/dokploy.md) — deploy staging/produção no Dokploy + registry Gitea
|
||||||
|
|
||||||
## Deploy (Dokploy)
|
## Deploy (Dokploy)
|
||||||
|
|
||||||
Staging publica automaticamente após CI verde em `main` (imagem GHCR por SHA + alias `:staging`). Produção promove a **mesma digest** com workflow manual `Promote production` (sem rebuild).
|
Staging publica automaticamente após CI verde em `main` (imagem no registry Gitea por SHA + alias `:staging`). Produção promove a **mesma digest** com workflow manual `Promote production` (sem rebuild).
|
||||||
|
|
||||||
Ver runbook completo: [docs/deployment/dokploy.md](docs/deployment/dokploy.md).
|
Ver runbook completo: [docs/deployment/dokploy.md](docs/deployment/dokploy.md).
|
||||||
|
|||||||
4
SPEC.md
4
SPEC.md
@@ -2073,7 +2073,7 @@ quality → Pint check + PHPStan/Larastan + audits + testes
|
|||||||
### 14.3 Branches e ambientes
|
### 14.3 Branches e ambientes
|
||||||
|
|
||||||
- PR: testes e preview opcional;
|
- PR: testes e preview opcional;
|
||||||
- `main`: build imutável por SHA publicado no GHCR e deploy automático em staging via Dokploy;
|
- `main`: build imutável por SHA publicado no registry Gitea (`git.hellomanoel.com`) e deploy automático em staging via Dokploy;
|
||||||
- staging: Dokploy Compose executa migração, healthcheck `/up` e smoke pós-deploy (`/up`, `/`, `/admin/login`);
|
- staging: Dokploy Compose executa migração, healthcheck `/up` e smoke pós-deploy (`/up`, `/`, `/admin/login`);
|
||||||
- produção: promoção da mesma imagem aprovada, sem rebuild (retag do digest em `:production`);
|
- produção: promoção da mesma imagem aprovada, sem rebuild (retag do digest em `:production`);
|
||||||
- produção requer aprovação humana explícita no MVP (`workflow_dispatch` com confirmação);
|
- produção requer aprovação humana explícita no MVP (`workflow_dispatch` com confirmação);
|
||||||
@@ -2487,7 +2487,7 @@ Toda operação financeira deve:
|
|||||||
| ADR-011 | Cloudflare R2 (S3-compatible) como storage de objetos em produção | Aceita |
|
| ADR-011 | Cloudflare R2 (S3-compatible) como storage de objetos em produção | Aceita |
|
||||||
| ADR-012 | E-mail transacional via Resend (mailer nativo Laravel) | Aceita |
|
| ADR-012 | E-mail transacional via Resend (mailer nativo Laravel) | Aceita |
|
||||||
| ADR-013 | Design system Heritage Editorial para o site público | Aceita |
|
| ADR-013 | Design system Heritage Editorial para o site público | Aceita |
|
||||||
| ADR-014 | Deploy via Dokploy Compose com imagem imutável por SHA no GHCR | Aceita |
|
| ADR-014 | Deploy via Dokploy Compose com imagem imutável por SHA no registry Gitea | Aceita |
|
||||||
| ADR-015 | Site público permanece Blade + JS vanilla; Livewire e Alpine ficam restritos ao Filament até o gatilho de §22. Emenda o texto da ADR-002 | Aceita |
|
| ADR-015 | Site público permanece Blade + JS vanilla; Livewire e Alpine ficam restritos ao Filament até o gatilho de §22. Emenda o texto da ADR-002 | Aceita |
|
||||||
| ADR-016 | Lançamento de 31/08/2026 entrega apenas o site institucional (Fases 0–1); Fases 2–5 seguem especificadas e adiadas, sem data | Aceita |
|
| ADR-016 | Lançamento de 31/08/2026 entrega apenas o site institucional (Fases 0–1); Fases 2–5 seguem especificadas e adiadas, sem data | Aceita |
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Shared Compose for Dokploy staging and production.
|
# Shared Compose for Dokploy staging and production.
|
||||||
# Both stacks use the same file with different env:
|
# Both stacks use the same file with different env:
|
||||||
# APP_IMAGE=ghcr.io/<owner>/<repo>
|
# APP_IMAGE=git.hellomanoel.com/manoel-freitas/amare
|
||||||
# IMAGE_TAG=staging|production|<git-sha>
|
# IMAGE_TAG=staging|production|<git-sha>
|
||||||
# PostgreSQL is a separate Dokploy database service (not defined here).
|
# PostgreSQL is a separate Dokploy database service (not defined here).
|
||||||
# Traefik/Dokploy domains should target service `web` port 8000.
|
# Traefik/Dokploy domains should target service `web` port 8000.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Deploy Dokploy (staging → production)
|
# Deploy Dokploy (staging → production)
|
||||||
|
|
||||||
Runbook for operating Amare on a VPS with Dokploy connected to GitHub, publishing immutable images to GHCR.
|
Runbook for operating Amare on a VPS with Dokploy connected to Gitea (git.hellomanoel.com), publishing immutable images to Gitea's container registry.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
CI (main) → build FrankenPHP image → GHCR :<sha> + :staging
|
CI (main) → build FrankenPHP image → git.hellomanoel.com registry :<sha> + :staging
|
||||||
→ Dokploy staging compose.deploy
|
→ Dokploy staging compose.deploy
|
||||||
→ smoke /up / /admin/login
|
→ smoke /up / /admin/login
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Promote (manual) → retag same digest as :production (no rebuild)
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Compose file | [`docker-compose.deploy.yml`](../../docker-compose.deploy.yml) |
|
| Compose file | [`docker-compose.deploy.yml`](../../docker-compose.deploy.yml) |
|
||||||
| Processes | `migrate` (one-shot) → `web` / `queue` / `scheduler` |
|
| Processes | `migrate` (one-shot) → `web` / `queue` / `scheduler` |
|
||||||
| Image | `ghcr.io/<owner>/<repo>:<sha>` (+ aliases `:staging`, `:production`) |
|
| Image | `git.hellomanoel.com/manoel-freitas/amare:<sha>` (+ aliases `:staging`, `:production`) |
|
||||||
| Database | Dokploy PostgreSQL **per environment** (not in the app image) |
|
| Database | Dokploy PostgreSQL **per environment** (not in the app image) |
|
||||||
| Media | Cloudflare R2 (`FILESYSTEM_DISK=r2`), separate buckets per environment |
|
| Media | Cloudflare R2 (`FILESYSTEM_DISK=r2`), separate buckets per environment |
|
||||||
| Mail | Resend (`MAIL_MAILER=resend`) |
|
| Mail | Resend (`MAIL_MAILER=resend`) |
|
||||||
@@ -26,11 +26,14 @@ Promote (manual) → retag same digest as :production (no rebuild)
|
|||||||
|
|
||||||
## Prerequisites (manual)
|
## Prerequisites (manual)
|
||||||
|
|
||||||
1. Dokploy installed on the VPS; GitHub provider connected.
|
1. Gitea repository `manoel-freitas/amare` at `https://git.hellomanoel.com`; **Repository Actions enabled** in repo settings; a registered **Gitea Actions runner** (see [docs.gitea.com usage/actions/quickstart](https://docs.gitea.com/usage/actions/quickstart)) with an `ubuntu-latest` label.
|
||||||
2. GHCR registry in Dokploy (`ghcr.io`) with a PAT that can **read** packages (`read:packages`). Prefer a dedicated bot/token; do not store write tokens on the VPS.
|
2. Dokploy installed on the VPS.
|
||||||
3. Two PostgreSQL services in Dokploy (staging + production), private (no public port).
|
3. Container registry in Dokploy (`git.hellomanoel.com`) with a PAT that can **read** packages (`read:package`). Prefer a dedicated bot/token; do not store write tokens on the VPS. The **write** PAT (`write:package`) lives only in Gitea repo secrets for the pipeline.
|
||||||
4. Two R2 buckets (or prefixes) and Resend credentials for each environment as needed.
|
4. Two PostgreSQL services in Dokploy (staging + production), private (no public port).
|
||||||
5. Domains (or temporary Dokploy/traefik.me hosts) pointing at the VPS with TLS.
|
5. Two R2 buckets (or prefixes) and Resend credentials for each environment as needed.
|
||||||
|
6. Domains (or temporary Dokploy/traefik.me hosts) pointing at the VPS with TLS.
|
||||||
|
|
||||||
|
Note: Gitea's `GITEA_TOKEN` cannot push OCI packages ([gitea#23642](https://github.com/go-gitea/gitea/issues/23642)); registry authentication in the workflows uses a PAT (`REGISTRY_PAT`), not the token. Secret names must not use the reserved `GITEA_` prefix (Gitea rejects them as invalid).
|
||||||
|
|
||||||
## Create Compose stacks
|
## Create Compose stacks
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ Create **two** Dokploy Compose services (same repo, same compose path):
|
|||||||
Dokploy Environment for each stack must set:
|
Dokploy Environment for each stack must set:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
APP_IMAGE=ghcr.io/<owner>/<repo>
|
APP_IMAGE=git.hellomanoel.com/manoel-freitas/amare
|
||||||
IMAGE_TAG=staging # or production
|
IMAGE_TAG=staging # or production
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -52,11 +55,11 @@ Point Dokploy domain(s) at service **`web`**, port **`8000`**. Do not publish Po
|
|||||||
|
|
||||||
Compose services must join the external Docker network `dokploy-network` (declared in `docker-compose.deploy.yml`) so they can resolve the Dokploy-managed Postgres internal host (e.g. `amare-stg-pez43e`). Set `DB_HOST` to that **Internal Host** from the Dokploy database UI — not a public hostname.
|
Compose services must join the external Docker network `dokploy-network` (declared in `docker-compose.deploy.yml`) so they can resolve the Dokploy-managed Postgres internal host (e.g. `amare-stg-pez43e`). Set `DB_HOST` to that **Internal Host** from the Dokploy database UI — not a public hostname.
|
||||||
|
|
||||||
Source can be GitHub (so Dokploy clones the compose file) or Raw paste of `docker-compose.deploy.yml`. Prefer GitHub + fixed compose path so updates stay in sync with `main`.
|
Source can be Gitea (so Dokploy clones the compose file) or Raw paste of `docker-compose.deploy.yml`. Prefer Gitea + fixed compose path so updates stay in sync with `main`.
|
||||||
|
|
||||||
## Required Laravel env (Dokploy only)
|
## Required Laravel env (Dokploy only)
|
||||||
|
|
||||||
Set these in Dokploy Environment UI (written to `.env` next to the compose file). **Never** put them in GitHub Actions secrets or image layers.
|
Set these in Dokploy Environment UI (written to `.env` next to the compose file). **Never** put them in Gitea Actions secrets or image layers.
|
||||||
|
|
||||||
```env
|
```env
|
||||||
APP_NAME=Amare
|
APP_NAME=Amare
|
||||||
@@ -120,9 +123,9 @@ Upload path does not need R2 CORS with the local temp-disk default. Still useful
|
|||||||
|
|
||||||
Also enable public access / custom domain for `R2_URL` so `<img>` URLs work after save.
|
Also enable public access / custom domain for `R2_URL` so `<img>` URLs work after save.
|
||||||
|
|
||||||
## GitHub Actions secrets
|
## Gitea Actions secrets
|
||||||
|
|
||||||
Repository secrets used by workflows:
|
Repository secrets (Gitea → Settings → Actions → Secrets) used by workflows:
|
||||||
|
|
||||||
| Secret | Purpose |
|
| Secret | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -132,32 +135,48 @@ Repository secrets used by workflows:
|
|||||||
| `DOKPLOY_PRODUCTION_COMPOSE_ID` | Production **Compose** service id (not an Application id) |
|
| `DOKPLOY_PRODUCTION_COMPOSE_ID` | Production **Compose** service id (not an Application id) |
|
||||||
| `STAGING_URL` | Public origin for staging smoke (e.g. `https://staging.example.com`) |
|
| `STAGING_URL` | Public origin for staging smoke (e.g. `https://staging.example.com`) |
|
||||||
| `PRODUCTION_URL` | Public origin for production smoke |
|
| `PRODUCTION_URL` | Public origin for production smoke |
|
||||||
|
| `REGISTRY_PAT` | Personal Access Token with `read:package` + `write:package` scopes — used to push images to `git.hellomanoel.com` (do not name secrets `GITEA_*`; that prefix is reserved) |
|
||||||
|
| `REGISTRY_USER` | Gitea username that owns `REGISTRY_PAT` (`manoel-freitas`) |
|
||||||
|
|
||||||
HTTP 404 from `compose.deploy` usually means the compose id is wrong (Application id instead of Compose) or `DOKPLOY_URL` still includes `/api`.
|
HTTP 404 from `compose.deploy` usually means the compose id is wrong (Application id instead of Compose) or `DOKPLOY_URL` still includes `/api`.
|
||||||
|
|
||||||
`GITHUB_TOKEN` (automatic) publishes to GHCR with `packages:write`. No Laravel/`APP_KEY`/DB/R2/Resend secrets belong in GitHub for this pipeline.
|
The automatic `GITEA_TOKEN` runs workflows but **cannot push OCI packages** ([gitea#23642](https://github.com/go-gitea/gitea/issues/23642)); registry authentication therefore uses `REGISTRY_PAT` + `REGISTRY_USER`. No Laravel/`APP_KEY`/DB/R2/Resend secrets belong in Gitea for this pipeline.
|
||||||
|
|
||||||
## Workflows
|
## Workflows
|
||||||
|
|
||||||
|
Workflows live in [`.gitea/workflows/`](../../.gitea/workflows/).
|
||||||
|
|
||||||
|
`actions/cache` and Docker Buildx `type=gha` are **not** used: the act_runner cache server is not reachable from job containers by default (`getCacheEntry` ETIMEDOUT). Re-enable only after configuring a reachable `cache.host`/`external_server` on the runner.
|
||||||
|
|
||||||
|
CI Postgres services must **not** publish host port `5432` (use service hostname `postgres` on the job network). Publishing `5432:5432` on a shared VPS runner fails with `Bind for 0.0.0.0:5432 failed: port is already allocated` when another job/orphan still holds the port.
|
||||||
|
|
||||||
|
Parallel CI jobs need the act_runner `config.yaml` to keep `container.network` **empty** (per-job Docker network + service DNS) and `runner.capacity` ≥ 2. Setting `network: bridge` puts every job on the default bridge and makes parallel Postgres collide. Nested app containers (browser/container jobs) must join that job network by name and must **not** publish host `:8000`. On the current 1 vCPU / ~4 GiB VPS, `capacity: 2` is the safe ceiling.
|
||||||
|
|
||||||
### Staging (automatic)
|
### Staging (automatic)
|
||||||
|
|
||||||
[`.github/workflows/deploy-staging.yml`](../../.github/workflows/deploy-staging.yml)
|
[`.gitea/workflows/deploy-staging.yml`](../../.gitea/workflows/deploy-staging.yml) — separate workflow, triggered by `workflow_run` when **CI** completes on `main`.
|
||||||
|
|
||||||
1. Waits for workflow `CI` success on push to `main`.
|
Requires **Gitea ≥ 1.25** (`workflow_run` is not implemented as an Actions trigger in 1.24.x). Match both workflow display name `CI` and file id `ci.yml`.
|
||||||
2. Builds once; pushes `:<full-sha>` and `:staging`.
|
|
||||||
|
1. Waits for workflow `CI` success (`workflow_run`) on push to `main`.
|
||||||
|
2. Builds once; pushes `:<full-sha>` and `:staging` to the Gitea registry.
|
||||||
3. Calls Dokploy `compose.deploy` and polls until done.
|
3. Calls Dokploy `compose.deploy` and polls until done.
|
||||||
4. Runs [`scripts/deploy/smoke.sh`](../../scripts/deploy/smoke.sh) against `STAGING_URL`.
|
4. Runs [`scripts/deploy/smoke.sh`](../../scripts/deploy/smoke.sh) against `STAGING_URL`.
|
||||||
|
|
||||||
|
Manual re-deploy: **Actions → Deploy staging → Run workflow** (`workflow_dispatch`).
|
||||||
|
|
||||||
|
`DOKPLOY_API_KEY` must be the **plaintext** key from Dokploy → Profile → API (starts like `amare…`). Do not paste the hashed `apikey.key` column from Postgres — that yields HTTP 401.
|
||||||
|
|
||||||
### Production (manual)
|
### Production (manual)
|
||||||
|
|
||||||
[`.github/workflows/promote-production.yml`](../../.github/workflows/promote-production.yml)
|
[`.gitea/workflows/promote-production.yml`](../../.gitea/workflows/promote-production.yml)
|
||||||
|
|
||||||
1. Operator runs **Actions → Promote production**.
|
1. Operator runs **Gitea → Actions → Promote production**.
|
||||||
2. Inputs: full `sha` already on GHCR; `confirm` must be exactly `PRODUCTION`.
|
2. Inputs: full `sha` already in the Gitea registry; `confirm` must be exactly `PRODUCTION`.
|
||||||
3. Retags the **same digest** as `:production` (no rebuild).
|
3. Retags the **same digest** as `:production` (no rebuild).
|
||||||
4. Deploys production compose + smoke.
|
4. Deploys production compose + smoke.
|
||||||
|
|
||||||
Private repos on GitHub Free do not get Environment required reviewers; human approval is the explicit `workflow_dispatch` + confirmation string. GitHub Pro Environment reviewers are optional later.
|
Human approval is the explicit `workflow_dispatch` + confirmation string (Gitea does not support GitHub Environment required reviewers).
|
||||||
|
|
||||||
## First admin and authorized production seeding
|
## First admin and authorized production seeding
|
||||||
|
|
||||||
@@ -256,10 +275,10 @@ No rebuild. Move the environment alias to a previous SHA digest and redeploy.
|
|||||||
### Staging
|
### Staging
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Locally or in a one-off Actions shell with GHCR login
|
# Locally or in a one-off Actions shell with Gitea registry login
|
||||||
docker buildx imagetools create \
|
docker buildx imagetools create \
|
||||||
--tag ghcr.io/<owner>/<repo>:staging \
|
--tag git.hellomanoel.com/manoel-freitas/amare:staging \
|
||||||
ghcr.io/<owner>/<repo>:<previous-sha>
|
git.hellomanoel.com/manoel-freitas/amare:<previous-sha>
|
||||||
|
|
||||||
# Then trigger Dokploy deploy (UI Deploy, or):
|
# Then trigger Dokploy deploy (UI Deploy, or):
|
||||||
DOKPLOY_URL=... DOKPLOY_API_KEY=... DOKPLOY_COMPOSE_ID=... \
|
DOKPLOY_URL=... DOKPLOY_API_KEY=... DOKPLOY_COMPOSE_ID=... \
|
||||||
@@ -297,7 +316,7 @@ Expects HTTP 200 for `/up`, `/`, and `/admin/login`.
|
|||||||
## Local validation of Compose
|
## Local validation of Compose
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
APP_IMAGE=ghcr.io/<owner>/<repo> IMAGE_TAG=staging \
|
APP_IMAGE=git.hellomanoel.com/manoel-freitas/amare IMAGE_TAG=staging \
|
||||||
docker compose -f docker-compose.deploy.yml config
|
docker compose -f docker-compose.deploy.yml config
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Define immutable staging and production promotion through GHCR and Dokploy Compose, including migration, health, smoke, rollback, and backup evidence.
|
Define immutable staging and production promotion through the Gitea container registry (`git.hellomanoel.com`) and Dokploy Compose, including migration, health, smoke, rollback, and backup evidence.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Staging deploys an immutable application image by commit SHA
|
### Requirement: Staging deploys an immutable application image by commit SHA
|
||||||
|
|
||||||
The system SHALL deploy staging from a single FrankenPHP application image tagged with the Git commit SHA and published to GHCR (SPEC §14.3, §15.2). Web, queue worker, and scheduler processes MUST use that same image digest. Rebuilds per process on the staging host MUST NOT be the promotion path.
|
The system SHALL deploy staging from a single FrankenPHP application image tagged with the Git commit SHA and published to the Gitea container registry (SPEC §14.3, §15.2). Web, queue worker, and scheduler processes MUST use that same image digest. Rebuilds per process on the staging host MUST NOT be the promotion path.
|
||||||
|
|
||||||
#### Scenario: Same image serves all application processes
|
#### Scenario: Same image serves all application processes
|
||||||
|
|
||||||
- **WHEN** a staging deployment is promoted for commit SHA `abc123`
|
- **WHEN** a staging deployment is promoted for commit SHA `abc123`
|
||||||
- **THEN** web, queue, and scheduler MUST run from `ghcr.io/<owner>/<repo>:abc123` (or equivalent digest)
|
- **THEN** web, queue, and scheduler MUST run from `git.hellomanoel.com/<owner>/<repo>:abc123` (or equivalent digest)
|
||||||
- **AND** MUST NOT rebuild distinct images per process
|
- **AND** MUST NOT rebuild distinct images per process
|
||||||
|
|
||||||
#### Scenario: CI publishes the image before Dokploy deploy
|
#### Scenario: CI publishes the image before Dokploy deploy
|
||||||
@@ -69,7 +69,7 @@ Production SHALL be promoted from an already-published SHA-tagged image without
|
|||||||
#### Scenario: Operator promotes a staging-approved SHA to production
|
#### Scenario: Operator promotes a staging-approved SHA to production
|
||||||
|
|
||||||
- **WHEN** the operator confirms promotion of commit SHA `abc123`
|
- **WHEN** the operator confirms promotion of commit SHA `abc123`
|
||||||
- **THEN** production web, queue, and scheduler MUST run the same digest previously published as `ghcr.io/<owner>/<repo>:abc123`
|
- **THEN** production web, queue, and scheduler MUST run the same digest previously published as `git.hellomanoel.com/<owner>/<repo>:abc123`
|
||||||
- **AND** MUST NOT rebuild from source for that promotion
|
- **AND** MUST NOT rebuild from source for that promotion
|
||||||
|
|
||||||
### Requirement: Database backups exist before production cutover
|
### Requirement: Database backups exist before production cutover
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# DOKPLOY_API_KEY x-api-key value
|
# DOKPLOY_API_KEY x-api-key value
|
||||||
# DOKPLOY_COMPOSE_ID target compose id
|
# DOKPLOY_COMPOSE_ID target compose id
|
||||||
# Optional env:
|
# Optional env:
|
||||||
# DEPLOY_TITLE deployment title (default: GitHub deploy)
|
# DEPLOY_TITLE deployment title (default: Gitea deploy)
|
||||||
# DEPLOY_TIMEOUT_SEC total wait seconds (default: 900)
|
# DEPLOY_TIMEOUT_SEC total wait seconds (default: 900)
|
||||||
# DEPLOY_POLL_SEC poll interval (default: 10)
|
# DEPLOY_POLL_SEC poll interval (default: 10)
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ DOKPLOY_URL="${DOKPLOY_URL%/}"
|
|||||||
DOKPLOY_URL="${DOKPLOY_URL%/api}"
|
DOKPLOY_URL="${DOKPLOY_URL%/api}"
|
||||||
DOKPLOY_URL="${DOKPLOY_URL%/}"
|
DOKPLOY_URL="${DOKPLOY_URL%/}"
|
||||||
|
|
||||||
DEPLOY_TITLE="${DEPLOY_TITLE:-GitHub deploy}"
|
DEPLOY_TITLE="${DEPLOY_TITLE:-Gitea deploy}"
|
||||||
DEPLOY_TIMEOUT_SEC="${DEPLOY_TIMEOUT_SEC:-900}"
|
DEPLOY_TIMEOUT_SEC="${DEPLOY_TIMEOUT_SEC:-900}"
|
||||||
DEPLOY_POLL_SEC="${DEPLOY_POLL_SEC:-10}"
|
DEPLOY_POLL_SEC="${DEPLOY_POLL_SEC:-10}"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user