ci: require manual gated releases from main
All checks were successful
Build and deploy / Validate source (push) Successful in 1m28s
Build and deploy / Integration suite on a real stack (push) Successful in 4m3s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
All checks were successful
Build and deploy / Validate source (push) Successful in 1m28s
Build and deploy / Integration suite on a real stack (push) Successful in 4m3s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
This commit is contained in:
@@ -48,6 +48,9 @@ jobs:
|
||||
# by whoever follows them. The suites run inside the network, so it has to
|
||||
# be the service name, not a published port on the host.
|
||||
S3_PUBLIC_ENDPOINT: http://storage:9000
|
||||
PUBLIC_ORIGIN: http://site
|
||||
ALLOWED_HOSTS: localhost,127.0.0.1,site,kanban
|
||||
ALLOWED_ORIGINS: http://site,http://kanban,http://localhost:28080,http://localhost:28081
|
||||
COMPOSE: docker compose -f compose.local.yaml
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -70,7 +73,7 @@ jobs:
|
||||
# one a developer exercises on localhost.
|
||||
- name: API and workflow regressions
|
||||
run: |
|
||||
for suite in smoke_test workflow_test security_test scanning_test payment_test; do
|
||||
for suite in smoke_test workflow_test security_test scanning_test payment_test quote_pagination_test; do
|
||||
echo "--- $suite"
|
||||
$COMPOSE exec -T \
|
||||
-e SITE_BASE_URL=http://site \
|
||||
@@ -83,35 +86,13 @@ jobs:
|
||||
$COMPOSE exec -T api python -m tests.retention_test
|
||||
$COMPOSE exec -T api python -m tests.runtime_security_test
|
||||
|
||||
# These need a real Chrome. They are the only coverage for the artwork
|
||||
# editor and the full customer journey, so install google-chrome-stable
|
||||
# (or set CHROME_BIN) on the runner to make them gate deployments. The
|
||||
# suites above stay hard gates either way.
|
||||
# Run Chrome on the Compose network. It must resolve the same storage:9000
|
||||
# hostname used in presigned URLs, and absence of Chrome must fail CI.
|
||||
- name: Browser regressions
|
||||
run: |
|
||||
for candidate in "$CHROME_BIN" /usr/bin/google-chrome-stable \
|
||||
/usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do
|
||||
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
|
||||
export CHROME_BIN="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ ! -x "${CHROME_BIN:-}" ]; then
|
||||
echo "::warning::No Chrome on this runner; browser regressions were NOT run."
|
||||
echo "Install google-chrome-stable or set CHROME_BIN to gate on them."
|
||||
exit 0
|
||||
fi
|
||||
# Chrome runs here, in the runner container, and reaches the stack only
|
||||
# through ports published on the host. When the runner is itself a
|
||||
# container those are in another namespace, so check before running
|
||||
# rather than failing with a bare connection error. See ROADMAP 5.10.
|
||||
if ! wget -q -T 5 -O /dev/null "http://localhost:${SITE_PORT}/health"; then
|
||||
echo "::warning::Stack not reachable from the runner; browser regressions were NOT run."
|
||||
exit 0
|
||||
fi
|
||||
echo "Using $CHROME_BIN"
|
||||
node tests/artwork_browser_test.mjs
|
||||
node tests/browser_test.mjs
|
||||
$COMPOSE build browser-tests
|
||||
$COMPOSE run --rm --no-deps browser-tests sh -ec \
|
||||
'node tests/artwork_browser_test.mjs && node tests/browser_test.mjs'
|
||||
|
||||
- name: Diagnostics on failure
|
||||
if: failure()
|
||||
@@ -145,13 +126,9 @@ jobs:
|
||||
fs --scanners secret --exit-code 1 --severity HIGH,CRITICAL \
|
||||
--no-progress /src
|
||||
|
||||
# docs/PORTAINER.md described this as blocking publication. It never ran at
|
||||
# all, and turning it on unconditionally would block every deploy: the
|
||||
# source preflight refuses a release while the payment and messaging
|
||||
# adapters are fake, which is the deliberate state the stack runs in
|
||||
# today. So its verdict is always printed, and enforcement is opt-in.
|
||||
# Set the repository variable ENFORCE_PRODUCTION_PREFLIGHT to "true" once
|
||||
# real adapters land, and this becomes the gate the documentation claims.
|
||||
# Keep push feedback advisory while the provider adapters are fake.
|
||||
# The manual release job enforces the source preflight unconditionally.
|
||||
# ENFORCE_PRODUCTION_PREFLIGHT can make push checks fail on blockers too.
|
||||
- name: Production source preflight
|
||||
run: |
|
||||
set +e
|
||||
@@ -171,7 +148,7 @@ jobs:
|
||||
publish-and-deploy:
|
||||
name: Publish images and notify Portainer
|
||||
needs: [validate, integration, scan]
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
if: gitea.event_name == 'workflow_dispatch' && gitea.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
@@ -181,6 +158,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- name: Require production readiness
|
||||
env:
|
||||
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
||||
run: |
|
||||
python3 deploy/production_preflight.py --source-only
|
||||
test -n "$PORTAINER_WEBHOOK"
|
||||
- name: Sign in to the Gitea Container Registry
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
@@ -190,7 +173,7 @@ jobs:
|
||||
test -n "$REGISTRY_TOKEN"
|
||||
echo "$REGISTRY_TOKEN" | docker login gitea.blyzer.com.br \
|
||||
--username "$REGISTRY_USERNAME" --password-stdin
|
||||
- name: Build and publish API
|
||||
- name: Build API
|
||||
run: |
|
||||
image="gitea.blyzer.com.br/blyzer/dtf-api"
|
||||
# The Dockerfiles pin digests themselves; these variables let a base be
|
||||
@@ -201,9 +184,7 @@ jobs:
|
||||
docker build --file deploy/Dockerfile.api "$@" \
|
||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||
docker push "$image:latest"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
- name: Build and publish web
|
||||
- name: Build web
|
||||
run: |
|
||||
image="gitea.blyzer.com.br/blyzer/dtf-web"
|
||||
set --
|
||||
@@ -212,8 +193,6 @@ jobs:
|
||||
docker build --file deploy/Dockerfile.web "$@" \
|
||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||
docker push "$image:latest"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
# CRITICAL blocks, HIGH is reported. Both images carry zero CRITICAL after
|
||||
# the base pinning and OS upgrades, so this gate holds the line already
|
||||
# reached. The remaining HIGH findings have no upstream fix, so failing on
|
||||
@@ -227,25 +206,29 @@ jobs:
|
||||
"gitea.blyzer.com.br/blyzer/dtf-web:${{ gitea.sha }}"; do
|
||||
echo "--- $target (HIGH, reported)"
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
||||
image --scanners vuln --severity HIGH --no-progress \
|
||||
image --image-src docker --scanners vuln --severity HIGH --no-progress \
|
||||
--format table --exit-code 0 "$target" ||
|
||||
echo "::warning::Could not scan $target for HIGH findings"
|
||||
echo "--- $target (CRITICAL, blocking)"
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
||||
image --scanners vuln --severity CRITICAL --no-progress \
|
||||
image --image-src docker --scanners vuln --severity CRITICAL --no-progress \
|
||||
--format table --exit-code 1 "$target" || failed=1
|
||||
done
|
||||
if [ "$failed" -ne 0 ]; then
|
||||
echo "::error::A CRITICAL vulnerability was found in a published image."
|
||||
echo "::error::A CRITICAL vulnerability was found in a release image."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Publish validated images
|
||||
run: |
|
||||
for name in dtf-api dtf-web; do
|
||||
image="gitea.blyzer.com.br/blyzer/$name"
|
||||
docker push "$image:${{ gitea.sha }}"
|
||||
docker push "$image:latest"
|
||||
done
|
||||
|
||||
- name: Trigger Portainer redeployment
|
||||
env:
|
||||
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
||||
run: |
|
||||
if [ -z "$PORTAINER_WEBHOOK" ]; then
|
||||
echo "PORTAINER_WEBHOOK is not configured; images were published but deployment was skipped."
|
||||
exit 0
|
||||
fi
|
||||
curl --fail --silent --show-error --max-time 30 --request POST "$PORTAINER_WEBHOOK"
|
||||
|
||||
@@ -23,9 +23,9 @@ x-app: &app
|
||||
OPERATOR_PASSWORD: ${OPERATOR_PASSWORD:-local-operator-only}
|
||||
# The browser reaches the API through the Site gateway, so the published
|
||||
# Site/Kanban origins must be accepted or every write is rejected 403.
|
||||
PUBLIC_ORIGIN: http://localhost:${SITE_PORT:-8080}
|
||||
ALLOWED_HOSTS: localhost,127.0.0.1
|
||||
ALLOWED_ORIGINS: http://localhost:${SITE_PORT:-8080},http://localhost:${KANBAN_PORT:-8081},http://127.0.0.1:${SITE_PORT:-8080},http://127.0.0.1:${KANBAN_PORT:-8081}
|
||||
PUBLIC_ORIGIN: ${PUBLIC_ORIGIN:-http://localhost:${SITE_PORT:-8080}}
|
||||
ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1}
|
||||
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-http://localhost:${SITE_PORT:-8080},http://localhost:${KANBAN_PORT:-8081},http://127.0.0.1:${SITE_PORT:-8080},http://127.0.0.1:${KANBAN_PORT:-8081}}
|
||||
COOKIE_SECURE: "false"
|
||||
PAYMENT_ADAPTER: fake
|
||||
PAYMENT_WEBHOOK_SECRET: ${PAYMENT_WEBHOOK_SECRET:-local-webhook-secret}
|
||||
@@ -208,6 +208,28 @@ services:
|
||||
timeout: 3s
|
||||
retries: 12
|
||||
|
||||
browser-tests:
|
||||
profiles: [ci]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: infra/Dockerfile.browser-tests
|
||||
environment:
|
||||
CHROME_BIN: /usr/bin/chromium
|
||||
CHROME_NO_SANDBOX: "1"
|
||||
CHROME_TRUST_TEST_ORIGINS: "1"
|
||||
SITE_BROWSER_ORIGIN: http://site
|
||||
KANBAN_BROWSER_ORIGIN: http://kanban
|
||||
OPERATOR_EMAIL: ${OPERATOR_EMAIL:-operator@example.test}
|
||||
OPERATOR_PASSWORD: ${OPERATOR_PASSWORD:-local-operator-only}
|
||||
shm_size: 1gb
|
||||
networks: [local]
|
||||
depends_on:
|
||||
site: {condition: service_healthy}
|
||||
kanban: {condition: service_healthy}
|
||||
storage: {condition: service_healthy}
|
||||
security_opt: [no-new-privileges:true]
|
||||
cap_drop: [ALL]
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
storage-data:
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from .production_preflight import config_errors, source_errors
|
||||
|
||||
|
||||
class ReleaseWorkflowTests(unittest.TestCase):
|
||||
def test_main_push_cannot_publish_and_manual_release_is_gated(self):
|
||||
workflow = (Path(__file__).resolve().parents[1] /
|
||||
'.gitea/workflows/deploy.yml').read_text()
|
||||
release = workflow.split(' publish-and-deploy:\n', 1)[1]
|
||||
self.assertIn("if: gitea.event_name == 'workflow_dispatch' && "
|
||||
"gitea.ref == 'refs/heads/main'", release)
|
||||
preflight = release.index('python3 deploy/production_preflight.py --source-only')
|
||||
webhook = release.index('test -n "$PORTAINER_WEBHOOK"')
|
||||
scan = release.index('- name: Image vulnerabilities')
|
||||
publish = release.index('- name: Publish validated images')
|
||||
redeploy = release.index('- name: Trigger Portainer redeployment')
|
||||
self.assertLess(preflight, scan)
|
||||
self.assertLess(webhook, scan)
|
||||
self.assertLess(scan, publish)
|
||||
self.assertLess(publish, redeploy)
|
||||
|
||||
|
||||
def valid_config():
|
||||
digest = '1' * 64
|
||||
values = {
|
||||
|
||||
@@ -17,8 +17,9 @@ Cloudflare R2, so MinIO is not part of this stack.
|
||||
|
||||
The single workflow is `.gitea/workflows/deploy.yml`. Every push and pull request
|
||||
runs static validation, the integration suite against a real stack, and a secret
|
||||
scan. A push to `main` then builds the production images, publishes both `latest`
|
||||
and the full commit SHA, reports their vulnerabilities, and calls Portainer.
|
||||
scan. A push to `main` does not publish or deploy. A manual workflow run on
|
||||
`main` repeats those checks, builds and scans the images, then publishes both
|
||||
`latest` and the full commit SHA and calls Portainer.
|
||||
|
||||
What actually gates a deployment:
|
||||
|
||||
@@ -26,17 +27,19 @@ What actually gates a deployment:
|
||||
|---|---|
|
||||
| `py_compile` and the unit tests | yes |
|
||||
| Integration suite on a live stack (smoke, workflow, security, scanning, retention, runtime) | yes |
|
||||
| Browser suites | only when the runner has Chrome; otherwise warns and continues |
|
||||
| Browser suites | yes; Chrome runs in the Compose test container |
|
||||
| Trivy secret scan (HIGH/CRITICAL) | yes |
|
||||
| Source preflight (`deploy/production_preflight.py --source-only`) | only when `ENFORCE_PRODUCTION_PREFLIGHT` is `true` |
|
||||
| Source preflight (`deploy/production_preflight.py --source-only`) | yes for manual release; advisory on pushes unless `ENFORCE_PRODUCTION_PREFLIGHT` is `true` |
|
||||
| Trivy image vulnerabilities, CRITICAL | yes |
|
||||
| Trivy image vulnerabilities, HIGH | no — reported after the build |
|
||||
| Trivy image vulnerabilities, HIGH | no — reported before publication |
|
||||
| Configured Portainer webhook | yes for manual release |
|
||||
|
||||
The source preflight is advisory by default because it refuses a release while
|
||||
the payment and messaging adapters are fake, which is the deliberate state the
|
||||
stack runs in today. Enforcing it now would block every deployment. Set the
|
||||
repository variable `ENFORCE_PRODUCTION_PREFLIGHT` to `true` once real adapters
|
||||
land, and it becomes a hard gate.
|
||||
The source preflight refuses a release while the payment and messaging adapters
|
||||
are fake. It remains advisory on push checks so development can continue, but a
|
||||
manual release is blocked until those adapters are replaced. Set the repository
|
||||
variable `ENFORCE_PRODUCTION_PREFLIGHT` to `true` when all pushes should also
|
||||
fail on those blockers. Before a manual release, run the full configuration
|
||||
preflight below against the actual Portainer values; CI checks source only.
|
||||
|
||||
CRITICAL image findings block. Both images carry none: the bases are pinned by
|
||||
digest, both Dockerfiles upgrade their OS packages, and the web image moved off
|
||||
@@ -60,10 +63,10 @@ Repository secrets:
|
||||
- `REGISTRY_USERNAME` and `REGISTRY_TOKEN` — package write credentials.
|
||||
- `PORTAINER_WEBHOOK` — webhook generated by the `dtf-cloud` Portainer stack.
|
||||
|
||||
The webhook is called only after the gating checks in the table above pass.
|
||||
The webhook is called only after the release gates above pass.
|
||||
`ENFORCE_PRODUCTION_PREFLIGHT` and `TRIVY_IMAGE` are optional repository
|
||||
variables; without them the preflight is advisory and a pinned default scanner
|
||||
image is used.
|
||||
variables; the former affects push checks and the latter defaults to a pinned
|
||||
scanner image.
|
||||
|
||||
## 2. One-time Portainer resources
|
||||
|
||||
@@ -124,10 +127,13 @@ The `db-init` service completing and stopping is expected. The other six
|
||||
services must be healthy. A failed `db-init` task or an unhealthy service blocks
|
||||
acceptance.
|
||||
|
||||
## 4. Normal deployment
|
||||
## 4. Manual deployment
|
||||
|
||||
Push to `main`. Gitea validates, tests, scans, publishes these images, and calls
|
||||
the webhook:
|
||||
Push the reviewed commit to `main` and wait for its validation workflow to pass.
|
||||
After validating the actual Portainer configuration with the full preflight in
|
||||
section 3, use Gitea Actions to manually run **Build and deploy** on `main` at
|
||||
that commit. The workflow repeats validation, tests and scans, then publishes
|
||||
these images and calls the webhook:
|
||||
|
||||
```text
|
||||
gitea.blyzer.com.br/blyzer/dtf-api:latest
|
||||
|
||||
@@ -14,6 +14,9 @@ kept through the approved order; 4.6 now pages pending and approved unpaid
|
||||
quotes, including a tested 101st pending quote. Operational entrypoints in
|
||||
5.12 are repaired and locally exercised. Image decoding, mixed-sheet grading,
|
||||
rotation-sensitive DPI, and PDF page geometry are corrected in 3.9/4.4.
|
||||
`main` pushes now validate without publishing; manual release requires a passing
|
||||
source preflight. The containerized browser gate passes locally, pending a Gitea
|
||||
runner run.
|
||||
Next address the upload/scanner safety gate and unsupported PDF image evidence.
|
||||
The customer/API upload admission now stops above the scanner's effective limit
|
||||
before transfer; the 5 GiB large-file product path still needs agreement and
|
||||
@@ -598,14 +601,9 @@ print-file evidence still need correction before this item can close.
|
||||
integration job should run `down -v` before `up` — or a dedicated step should
|
||||
apply `schema.sql` twice to a fresh database, proving both a first install and
|
||||
a re-run.
|
||||
- `[ ]` 5.10 — The browser suites do not run in CI. Chrome runs in the runner
|
||||
container and can only reach the stack through ports published on the host, which
|
||||
is a different network namespace when the runner is itself a container. The API,
|
||||
workflow, security, scanning, retention and runtime suites were moved inside the
|
||||
stack's network and do gate. The browser suites are the only coverage for the
|
||||
artwork editor and the full customer journey, so they need either Chrome in a
|
||||
container on that network, or a runner with host networking. Until then they gate
|
||||
locally only, and CI warns when it skips them.
|
||||
- `[ ]` 5.10 — The browser suites were moved into a Chrome container on the
|
||||
Compose network and made required in CI. Confirm the complete checkout journey
|
||||
passes in that topology and on the actual Gitea runner before closing this item.
|
||||
- `[ ]` 5.9 — `local/browser_test.mjs` failed once and passed on an immediate
|
||||
re-run, with no code change in between (2026-09-21). It is a deploy gate when the
|
||||
runner has Chrome, so an intermittent failure there blocks releases for no reason.
|
||||
@@ -627,10 +625,12 @@ print-file evidence still need correction before this item can close.
|
||||
- `[ ]` 5.13 — Define production recovery: scheduled encrypted offsite database
|
||||
and object backups, a consistent snapshot boundary, Swarm data placement and
|
||||
a restore rehearsal that opens every required live order file.
|
||||
- `[ ]` 5.14 — Promote and verify one immutable release. Scan before publishing
|
||||
mutable tags, make the source preflight validate the active stack, require the
|
||||
browser tests to run, test clean install and upgrade, and check application
|
||||
readiness after Portainer redeploys. Isolate concurrent CI stacks.
|
||||
- `[ ]` 5.14 — Promote and verify one immutable release. Normal `main` pushes
|
||||
now run checks only; manual dispatch requires source preflight and a configured
|
||||
webhook, and scans images before publishing. Still make the full preflight
|
||||
validate the active stack, deploy the tested immutable image references, test
|
||||
clean install and upgrade, check application readiness after Portainer
|
||||
redeploys, and isolate concurrent CI stacks.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -132,15 +132,12 @@ change when the advisory database or selected base digest changes.
|
||||
The files in `deploy/` and `.gitea/workflows/` are a guarded delivery mechanism,
|
||||
not an approval to operate the current application on the public internet.
|
||||
|
||||
Corrected 2026-09-21: an earlier version of this section described gates the
|
||||
workflow did not contain. The workflow now runs static validation, the
|
||||
integration suite against a live stack, and a blocking Trivy secret scan before
|
||||
publishing. The source preflight is advisory unless
|
||||
`ENFORCE_PRODUCTION_PREFLIGHT` is set, and image vulnerabilities are reported
|
||||
rather than enforced, because the current bases carry HIGH/CRITICAL findings
|
||||
with no upstream fix. Base images are still mutable tags, not digests.
|
||||
`PORTAINER.md` holds the authoritative table of what gates and what does not.
|
||||
It publishes both `latest` and the full commit SHA, then calls the Portainer
|
||||
Updated 2026-09-23: pushes to `main` run checks only. A manual workflow run on
|
||||
`main` requires the source preflight and a configured Portainer webhook before
|
||||
building. It scans built images before publication; CRITICAL findings block and
|
||||
HIGH findings are reported. The browser suites run in a required Compose Chrome
|
||||
container. `PORTAINER.md` holds the authoritative gate table. A permitted release
|
||||
publishes both `latest` and the full commit SHA, then calls the Portainer
|
||||
webhook. Application/provider secrets are created directly as versioned external
|
||||
Swarm secrets and never cross the workflow. Rollback selects the prior commit SHA
|
||||
in Portainer and does not roll back the database.
|
||||
|
||||
13
infra/Dockerfile.browser-tests
Normal file
13
infra/Dockerfile.browser-tests
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM node:22-bookworm-slim
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends chromium ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /workspace
|
||||
COPY tests /workspace/tests
|
||||
COPY web /workspace/web
|
||||
RUN mkdir -p /workspace/output/local \
|
||||
&& chown -R node:node /workspace/output
|
||||
USER node
|
||||
ENV CHROME_BIN=/usr/bin/chromium
|
||||
@@ -1,8 +1,8 @@
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/s;
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
if ($host !~ ^(localhost|127\.0\.0\.1)$) { return 400; }
|
||||
server_name localhost site kanban;
|
||||
if ($host !~ ^(localhost|127\.0\.0\.1|site|kanban)$) { return 400; }
|
||||
root /usr/share/nginx/html;
|
||||
index ${WEB_INDEX};
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
@@ -23,8 +23,8 @@ server {
|
||||
}
|
||||
server {
|
||||
listen 81;
|
||||
server_name localhost;
|
||||
if ($host !~ ^(localhost|127\.0\.0\.1)$) { return 400; }
|
||||
server_name localhost site kanban;
|
||||
if ($host !~ ^(localhost|127\.0\.0\.1|site|kanban)$) { return 400; }
|
||||
client_max_body_size 2m;
|
||||
location / {
|
||||
limit_req zone=api_limit burst=100 nodelay;
|
||||
|
||||
@@ -15,6 +15,10 @@ const profile=await mkdtemp(tmpdir()+'/dtf-browser-');
|
||||
const chrome=spawn(process.env.CHROME_BIN||'/usr/bin/google-chrome-stable',[
|
||||
'--headless=new','--disable-gpu','--no-first-run','--no-default-browser-check',
|
||||
...(process.env.CHROME_NO_SANDBOX==='1'?['--no-sandbox']:[]),
|
||||
// Production uses HTTPS; internal Compose HTTP names need a secure context
|
||||
// for Web Crypto during the upload and checkout journey.
|
||||
...(process.env.CHROME_TRUST_TEST_ORIGINS==='1'
|
||||
? ['--unsafely-treat-insecure-origin-as-secure=http://site,http://kanban'] : []),
|
||||
'--remote-debugging-port=0','--user-data-dir='+profile,'about:blank'
|
||||
],{stdio:['ignore','ignore','pipe']});
|
||||
const pause=ms=>new Promise(r=>setTimeout(r,ms));
|
||||
|
||||
Reference in New Issue
Block a user