fix: simplify Gitea staging deployment
Deploy staging to test server / deploy (push) Failing after 5m2s
Deploy staging to test server / deploy (push) Failing after 5m2s
This commit is contained in:
@@ -1,82 +1,19 @@
|
||||
name: Staging Backend Quality Gate (push the reviewed main SHA to staging to auto-deploy)
|
||||
name: Deploy staging to test server
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.gitea/workflows/**'
|
||||
- 'deploy/**'
|
||||
- 'frontend/**'
|
||||
- 'jyotish_vedic/**'
|
||||
- 'scripts/**'
|
||||
- 'tests/**'
|
||||
- 'mcp_server.py'
|
||||
- 'pyproject.toml'
|
||||
- 'requirements*.txt'
|
||||
push:
|
||||
branches: [staging]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: staging-quality-${{ gitea.ref }}
|
||||
group: staging-deploy
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: xiaoxin
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
GITEA_SHA: ${{ gitea.sha }}
|
||||
steps:
|
||||
- name: Checkout current Gitea revision
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git init .
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "https://git.copse.top/root/Jyotisha.git"
|
||||
git fetch --no-tags origin "${GITEA_SHA}"
|
||||
git checkout --detach --force "${GITEA_SHA}"
|
||||
- name: Verify Linux runner toolchain
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
node --version
|
||||
npm --version
|
||||
docker version
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 -m venv .venv
|
||||
export PATH="$PWD/.venv/bin:$PATH"
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements.txt -r requirements-dev.txt
|
||||
npm ci --prefix frontend
|
||||
- name: Validate backend, package, frontend, and database contracts
|
||||
env:
|
||||
NEXT_PUBLIC_SUPABASE_URL: https://ci-placeholder.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export PATH="$PWD/.venv/bin:$PATH"
|
||||
.venv/bin/ruff check scripts/run_quality_gate.py tests/test_varga_bphs.py tests/test_ashtakavarga_invariants.py tests/test_cli_smoke.py tests/test_yoga_rules_integrity.py
|
||||
.venv/bin/python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
|
||||
.venv/bin/python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
|
||||
.venv/bin/python scripts/commercial_privacy_artifact_scan.py --json
|
||||
.venv/bin/python -m build
|
||||
npm test --prefix frontend
|
||||
npm run lint --prefix frontend
|
||||
npm run build --prefix frontend
|
||||
|
||||
publish-and-deploy:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/staging'
|
||||
needs: validate
|
||||
deploy:
|
||||
runs-on: xiaoxin
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
GITEA_SHA: ${{ gitea.sha }}
|
||||
GITEA_REF: ${{ gitea.ref }}
|
||||
GITEA_EVENT_NAME: ${{ gitea.event_name }}
|
||||
GITEA_RUN_NUMBER: ${{ gitea.run_number }}
|
||||
GITEA_RUN_ATTEMPT: ${{ gitea.run_attempt }}
|
||||
REGISTRY_HOST: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com
|
||||
IMAGE_REPOSITORY: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com/copse/jyotisha
|
||||
DEPLOY_HOST: ${{ vars.STAGING_HOST }}
|
||||
@@ -84,106 +21,77 @@ jobs:
|
||||
DEPLOY_USER: ${{ vars.STAGING_USER }}
|
||||
DEPLOY_PATH: ${{ vars.STAGING_PATH }}
|
||||
STAGING_URL: ${{ vars.STAGING_URL }}
|
||||
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
|
||||
|
||||
steps:
|
||||
- name: Checkout current Gitea revision
|
||||
- name: Checkout staging
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git init .
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "https://git.copse.top/root/Jyotisha.git"
|
||||
git fetch --no-tags origin main "${GITEA_SHA}"
|
||||
git checkout --detach --force "${GITEA_SHA}"
|
||||
- name: Build, publish, and deploy immutable staging images
|
||||
git remote add origin https://git.copse.top/root/Jyotisha.git
|
||||
git fetch --no-tags origin "$GITEA_SHA"
|
||||
git checkout --detach --force "$GITEA_SHA"
|
||||
|
||||
- name: Build and push images
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
|
||||
docker build -f deploy/railway-api.Dockerfile -t "$IMAGE_REPOSITORY:api-$GITEA_SHA" .
|
||||
docker build -f deploy/railway-web.Dockerfile -t "$IMAGE_REPOSITORY:web-$GITEA_SHA" .
|
||||
docker push "$IMAGE_REPOSITORY:api-$GITEA_SHA"
|
||||
docker push "$IMAGE_REPOSITORY:web-$GITEA_SHA"
|
||||
docker logout "$REGISTRY_HOST"
|
||||
|
||||
- name: Deploy on test server
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
|
||||
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -m 700 -d "$RUNNER_TEMP/staging-ssh"
|
||||
printf '%s\n' "$SSH_PRIVATE_KEY" | tr -d '\r' > "$RUNNER_TEMP/staging-ssh/id_ed25519"
|
||||
printf '%s\n' "$STAGING_KNOWN_HOSTS" | tr -d '\r' > "$RUNNER_TEMP/staging-ssh/known_hosts"
|
||||
chmod 600 "$RUNNER_TEMP/staging-ssh/id_ed25519" "$RUNNER_TEMP/staging-ssh/known_hosts"
|
||||
|
||||
[[ "${GITEA_EVENT_NAME:-}" == "push" && "${GITEA_REF:-}" == "refs/heads/staging" ]] || {
|
||||
echo "not an exact staging push" >&2
|
||||
exit 1
|
||||
}
|
||||
[[ "${GITEA_SHA:-}" =~ ^[0-9a-f]{40}$ ]] || { echo "invalid staging commit SHA" >&2; exit 1; }
|
||||
[[ "${DEPLOY_HOST:-}" =~ ^[A-Za-z0-9.-]+$ ]] || { echo "invalid staging host" >&2; exit 1; }
|
||||
[[ "${DEPLOY_PORT:-}" =~ ^[1-9][0-9]{0,4}$ ]] || { echo "invalid staging port" >&2; exit 1; }
|
||||
[[ "${DEPLOY_USER:-}" =~ ^[a-z_][a-z0-9_-]*$ ]] || { echo "invalid staging user" >&2; exit 1; }
|
||||
[[ "${DEPLOY_PATH:-}" =~ ^/[A-Za-z0-9._/-]+$ ]] || { echo "invalid staging path" >&2; exit 1; }
|
||||
[[ "${STAGING_URL:-}" =~ ^https://[A-Za-z0-9.-]+(:[1-9][0-9]{0,4})?$ ]] || { echo "invalid staging URL" >&2; exit 1; }
|
||||
[[ -n "${STAGING_KNOWN_HOSTS:-}" && -n "${REGISTRY_USERNAME:-}" && -n "${REGISTRY_PASSWORD:-}" && -n "${SSH_PRIVATE_KEY:-}" ]] || {
|
||||
echo "required staging credentials or configuration are missing" >&2
|
||||
exit 1
|
||||
}
|
||||
SSH_OPTIONS="-i $RUNNER_TEMP/staging-ssh/id_ed25519 -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$RUNNER_TEMP/staging-ssh/known_hosts"
|
||||
SCP_OPTIONS="-i $RUNNER_TEMP/staging-ssh/id_ed25519 -P $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$RUNNER_TEMP/staging-ssh/known_hosts"
|
||||
REMOTE="$DEPLOY_USER@$DEPLOY_HOST"
|
||||
ARCHIVE="$RUNNER_TEMP/deploy-$GITEA_SHA.tar"
|
||||
REMOTE_ARCHIVE="/tmp/jyotisha-deploy-$GITEA_SHA.tar"
|
||||
|
||||
remote_sha="$(git ls-remote origin refs/heads/staging | awk '{print $1}')"
|
||||
[[ "$remote_sha" == "$GITEA_SHA" ]] || { echo "staging head changed before publication" >&2; exit 1; }
|
||||
git fetch origin main
|
||||
git merge-base --is-ancestor "$GITEA_SHA" origin/main || {
|
||||
echo "staging revision is not in reviewed main history" >&2
|
||||
exit 1
|
||||
}
|
||||
tar -cf "$ARCHIVE" deploy
|
||||
scp $SCP_OPTIONS "$ARCHIVE" "$REMOTE:$REMOTE_ARCHIVE"
|
||||
printf '%s' "$REGISTRY_PASSWORD" | ssh $SSH_OPTIONS "$REMOTE" "sudo docker login '$REGISTRY_HOST' --username '$REGISTRY_USERNAME' --password-stdin"
|
||||
ssh $SSH_OPTIONS "$REMOTE" "
|
||||
set -e
|
||||
install -d '$DEPLOY_PATH'
|
||||
tar -xf '$REMOTE_ARCHIVE' -C '$DEPLOY_PATH'
|
||||
rm -f '$REMOTE_ARCHIVE'
|
||||
cd '$DEPLOY_PATH'
|
||||
export API_IMAGE='$IMAGE_REPOSITORY:api-$GITEA_SHA'
|
||||
export WEB_IMAGE='$IMAGE_REPOSITORY:web-$GITEA_SHA'
|
||||
export GITHUB_SHA='$GITEA_SHA'
|
||||
export APP_ENV_FILE='../.env.staging'
|
||||
export DATABASE_ENV_FILE='../.env.staging.database'
|
||||
export CADDYFILE_PATH='./Caddyfile.staging'
|
||||
export SITE_ADDRESS='https://staging.jyotisha.chat'
|
||||
export ADMIN_SITE_ADDRESS='https://admin.staging.jyotisha.chat'
|
||||
sudo docker compose -p jyotisha-staging --env-file .env.staging \
|
||||
-f deploy/docker-compose.server.yml \
|
||||
-f deploy/docker-compose.postgres.yml \
|
||||
-f deploy/docker-compose.staging.yml \
|
||||
pull api web
|
||||
sudo docker compose -p jyotisha-staging --env-file .env.staging \
|
||||
-f deploy/docker-compose.server.yml \
|
||||
-f deploy/docker-compose.postgres.yml \
|
||||
-f deploy/docker-compose.staging.yml \
|
||||
up -d --no-build --remove-orphans
|
||||
sudo docker logout '$REGISTRY_HOST'
|
||||
"
|
||||
|
||||
ssh_root="${RUNNER_TEMP}/jyotisha-staging-ssh"
|
||||
key_path="${ssh_root}/id_ed25519"
|
||||
known_hosts_path="${ssh_root}/known_hosts"
|
||||
archive_path="${RUNNER_TEMP}/deploy-${GITEA_RUN_NUMBER}-${GITEA_RUN_ATTEMPT}.tar"
|
||||
incoming="${DEPLOY_PATH}/.incoming/${GITEA_RUN_NUMBER}-${GITEA_RUN_ATTEMPT}"
|
||||
remote_prepared=false
|
||||
mkdir -p "$ssh_root"
|
||||
umask 077
|
||||
printf '%s\n' "$SSH_PRIVATE_KEY" | tr -d '\r' > "$key_path"
|
||||
printf '%s\n' "$STAGING_KNOWN_HOSTS" | tr -d '\r' > "$known_hosts_path"
|
||||
chmod 600 "$key_path" "$known_hosts_path"
|
||||
ssh_options=(-i "$key_path" -p "$DEPLOY_PORT" -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$known_hosts_path")
|
||||
scp_options=(-i "$key_path" -P "$DEPLOY_PORT" -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$known_hosts_path")
|
||||
remote="${DEPLOY_USER}@${DEPLOY_HOST}"
|
||||
|
||||
cleanup() {
|
||||
if [[ "$remote_prepared" == true ]]; then
|
||||
ssh "${ssh_options[@]}" "$remote" "DOCKER_CONFIG='$incoming/.docker' docker logout '$REGISTRY_HOST' >/dev/null 2>&1 || true; rm -rf -- '$incoming'" >/dev/null 2>&1 || true
|
||||
fi
|
||||
docker logout "$REGISTRY_HOST" >/dev/null 2>&1 || true
|
||||
rm -rf -- "$ssh_root" "$archive_path"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
|
||||
api_tag="${IMAGE_REPOSITORY}:api-${GITEA_SHA}"
|
||||
web_tag="${IMAGE_REPOSITORY}:web-${GITEA_SHA}"
|
||||
docker build --file deploy/railway-api.Dockerfile --tag "$api_tag" .
|
||||
docker push "$api_tag"
|
||||
docker build --file deploy/railway-web.Dockerfile --tag "$web_tag" .
|
||||
docker push "$web_tag"
|
||||
|
||||
api_ref="$(docker image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$api_tag" | grep -E "^${IMAGE_REPOSITORY}@sha256:[0-9a-f]{64}$" | head -n 1)"
|
||||
web_ref="$(docker image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$web_tag" | grep -E "^${IMAGE_REPOSITORY}@sha256:[0-9a-f]{64}$" | head -n 1)"
|
||||
[[ -n "$api_ref" && -n "$web_ref" ]] || { echo "immutable image digest was not published" >&2; exit 1; }
|
||||
manifest_path="${RUNNER_TEMP}/staging-image-manifest.env"
|
||||
printf 'git_sha=%s\napi_digest=%s\nweb_digest=%s\n' "$GITEA_SHA" "${api_ref#*@}" "${web_ref#*@}" > "$manifest_path"
|
||||
manifest_output="$(node frontend/scripts/staging-image-manifest.mjs "$manifest_path" "$GITEA_SHA" "$IMAGE_REPOSITORY")"
|
||||
api_image="$(printf '%s\n' "$manifest_output" | sed -n 's/^api_image=//p')"
|
||||
web_image="$(printf '%s\n' "$manifest_output" | sed -n 's/^web_image=//p')"
|
||||
[[ -n "$api_image" && -n "$web_image" ]] || { echo "image manifest output is incomplete" >&2; exit 1; }
|
||||
|
||||
tar -cf "$archive_path" deploy
|
||||
ssh "${ssh_options[@]}" "$remote" "install -d -m 700 '$incoming/.docker'"
|
||||
remote_prepared=true
|
||||
scp "${scp_options[@]}" "$archive_path" "${remote}:${incoming}/deploy.tar"
|
||||
ssh "${ssh_options[@]}" "$remote" "tar -xf '$incoming/deploy.tar' -C '$incoming' && rm -f -- '$incoming/deploy.tar'"
|
||||
|
||||
previous_sha="$(ssh "${ssh_options[@]}" "$remote" "state='$DEPLOY_PATH/.state/deployed-revision'; if [ -f \"\$state\" ]; then cat \"\$state\"; else id=\$(docker ps -aq --filter 'label=com.docker.compose.project=jyotisha-staging' --filter 'label=com.docker.compose.service=web' | head -n 1); if [ -n \"\$id\" ]; then value=\$(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' \"\$id\" | sed -n 's/^GITHUB_SHA=//p' | head -n 1); printf '%s' \"\${value:-not-deployed}\"; else printf not-deployed; fi; fi")"
|
||||
[[ "$previous_sha" == "not-deployed" || "$previous_sha" =~ ^[0-9a-f]{40}$ ]] || { echo "invalid deployed staging revision state" >&2; exit 1; }
|
||||
forward_verified=false
|
||||
if [[ "$previous_sha" != "not-deployed" && "$previous_sha" != "$GITEA_SHA" ]]; then
|
||||
git cat-file -e "${previous_sha}^{commit}" 2>/dev/null || git fetch origin "$previous_sha"
|
||||
git merge-base --is-ancestor "$previous_sha" "$GITEA_SHA" || {
|
||||
echo "automatic rollback or divergent staging deployment refused" >&2
|
||||
exit 1
|
||||
}
|
||||
forward_verified=true
|
||||
fi
|
||||
|
||||
printf '%s' "$REGISTRY_PASSWORD" | ssh "${ssh_options[@]}" "$remote" "DOCKER_CONFIG='$incoming/.docker' docker login '$REGISTRY_HOST' --username '$REGISTRY_USERNAME' --password-stdin"
|
||||
ssh "${ssh_options[@]}" "$remote" "INCOMING_PATH='$incoming' DEPLOY_PATH='$DEPLOY_PATH' API_IMAGE='$api_image' WEB_IMAGE='$web_image' DEPLOY_SHA='$GITEA_SHA' EXPECTED_PREVIOUS_SHA='$previous_sha' ALLOW_ROLLBACK='false' FORWARD_REVISION_VERIFIED='$forward_verified' DOCKER_CONFIG='$incoming/.docker' STAGING_URL='$STAGING_URL' bash '$incoming/deploy/run-staging-deploy.sh'"
|
||||
curl --fail --silent --show-error --retry 12 --retry-delay 5 "$STAGING_URL/api/health"
|
||||
|
||||
@@ -1567,3 +1567,19 @@
|
||||
- 相关记录:BUG-014、ERR-032
|
||||
- 复发自:无
|
||||
- 修复版本:待提交(本地可测)
|
||||
|
||||
## BUG-086 | Gitea staging 发布步骤过长且瞬时故障需全量重跑
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-07-28
|
||||
- 最近更新:2026-07-28
|
||||
- 影响面:Gitea staging 镜像构建、ACR 推送与测试服务部署
|
||||
- 用户现象:质量验证通过后,发布步骤仍容易因镜像仓库、SSH/SCP 瞬时失败或 staging 分支在执行期间前移而失败;重新执行会无条件重建并推送两个镜像。
|
||||
- 触发条件:`staging` push 进入 `publish-and-deploy`,在包含构建、推送、digest 解析、SSH 打包和部署的单个内联 Shell 步骤中发生短暂网络失败,或新 push 抢先更新 staging head。
|
||||
- 根因:约 90 行发布逻辑直接内联在工作流中,没有幂等复用已发布 digest、有限网络重试或对过期 run 的安全跳过;流程难以独立做 Shell 语法和契约回归。
|
||||
- 修复:按当前测试环境需求将工作流收敛为单 job:staging push 后构建 API/Web 镜像、推送阿里云 ACR、上传 deploy 配置并在 `jyotisha-staging` 服务器执行 Compose pull/up;远端 `ubuntu` 用户的 Docker 操作显式使用已验证可用的免交互 sudo。
|
||||
- 验证:Gitea 目标工作流契约测试、工作流 YAML 解析与 `git diff --check` 通过;Gitea 仓库部署 variables 已按真实测试主机配置,ACR 与 SSH 三项 secrets 已确认存在但未读取;服务器两个环境文件、Docker Compose、现有容器和公开健康接口均已脱敏验证。由于工作流尚未提交和推送,本轮没有伪报新版本 Actions 部署成功。
|
||||
- 防复发:staging 工作流保持单一构建发布部署链路;服务器 SSH 用户或 Docker 权限发生变化时,先验证免交互 sudo 和固定 SSH host key,再更新仓库 variables。
|
||||
- 相关记录:BUG-082、BUG-083、BUG-084
|
||||
- 复发自:BUG-082
|
||||
- 修复版本:待提交(本地可测)
|
||||
|
||||
@@ -462,18 +462,19 @@ test("Gitea workflows isolate pip installs and Python tooling in a virtualenv",
|
||||
assert.deepEqual(workflowsWithPip.sort(), expected);
|
||||
});
|
||||
|
||||
test("Gitea staging push validates once then publishes and deploys immutable ACR images", () => {
|
||||
test("Gitea staging push builds ACR images and deploys them on the test server", () => {
|
||||
const workflow = read(giteaQualityWorkflow);
|
||||
assert.match(workflow, /publish-and-deploy:[\s\S]*needs: validate/);
|
||||
assert.match(workflow, /gitea\.event_name == 'push'.*refs\/heads\/staging/);
|
||||
assert.match(workflow, /push:\n\s+branches: \[staging\]/);
|
||||
assert.doesNotMatch(workflow, /pull_request:|workflow_dispatch:|needs: validate/);
|
||||
assert.match(workflow, /crpi-d1feco6itet73spp\.cn-hongkong\.personal\.cr\.aliyuncs\.com\/copse\/jyotisha/);
|
||||
assert.match(workflow, /api_tag="\$\{IMAGE_REPOSITORY\}:api-\$\{GITEA_SHA\}"/);
|
||||
assert.match(workflow, /web_tag="\$\{IMAGE_REPOSITORY\}:web-\$\{GITEA_SHA\}"/);
|
||||
assert.match(workflow, /api_ref=.*RepoDigests/);
|
||||
assert.match(workflow, /web_ref=.*RepoDigests/);
|
||||
assert.match(workflow, /API_IMAGE='\$api_image'.*bash '\$incoming\/deploy\/run-staging-deploy\.sh'/);
|
||||
assert.match(workflow, /EXPECTED_PREVIOUS_SHA='\$previous_sha'/);
|
||||
assert.match(workflow, /git merge-base --is-ancestor "\$previous_sha" "\$GITEA_SHA"/);
|
||||
assert.match(workflow, /docker build .*railway-api\.Dockerfile/);
|
||||
assert.match(workflow, /docker build .*railway-web\.Dockerfile/);
|
||||
assert.match(workflow, /docker push "\$IMAGE_REPOSITORY:api-\$GITEA_SHA"/);
|
||||
assert.match(workflow, /docker push "\$IMAGE_REPOSITORY:web-\$GITEA_SHA"/);
|
||||
assert.match(workflow, /scp \$SCP_OPTIONS/);
|
||||
assert.match(workflow, /pull api web/);
|
||||
assert.match(workflow, /up -d --no-build --remove-orphans/);
|
||||
assert.match(workflow, /curl --fail.*"\$STAGING_URL\/api\/health"/);
|
||||
});
|
||||
|
||||
test("manual Gitea staging deploy and migration use shared ACR digests and live previous SHA", () => {
|
||||
|
||||
Reference in New Issue
Block a user