fix: run staging automation on xiaoxin
Move validation and immutable ACR deployment to the online Linux runner and restore Bash-native deployment handling.
This commit is contained in:
@@ -22,11 +22,8 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: manman-win
|
||||
runs-on: xiaoxin
|
||||
timeout-minutes: 30
|
||||
defaults:
|
||||
run:
|
||||
shell: powershell
|
||||
steps:
|
||||
- uses: https://github.com/actions/checkout@v4
|
||||
- uses: https://github.com/actions/setup-python@v5
|
||||
@@ -39,49 +36,30 @@ jobs:
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
set -euo pipefail
|
||||
python -m pip install --upgrade pip
|
||||
if ($LASTEXITCODE -ne 0) { throw 'pip upgrade failed' }
|
||||
python -m pip install -r requirements.txt -r requirements-dev.txt
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Python dependency installation failed' }
|
||||
npm ci --prefix frontend
|
||||
if ($LASTEXITCODE -ne 0) { throw 'frontend dependency installation failed' }
|
||||
- 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: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
set -euo pipefail
|
||||
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
|
||||
if ($LASTEXITCODE -ne 0) { throw 'ruff validation failed' }
|
||||
$pythonFiles = @(
|
||||
Get-ChildItem scripts -Filter '*.py' -File | ForEach-Object FullName
|
||||
Get-ChildItem jyotish_vedic -Filter '*.py' -File | ForEach-Object FullName
|
||||
(Resolve-Path mcp_server.py).Path
|
||||
)
|
||||
python -m py_compile @pythonFiles
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Python compilation failed' }
|
||||
python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
|
||||
python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
|
||||
if ($LASTEXITCODE -ne 0) { throw 'quick quality gate failed' }
|
||||
python scripts/commercial_privacy_artifact_scan.py --json
|
||||
if ($LASTEXITCODE -ne 0) { throw 'privacy scan failed' }
|
||||
python -m build
|
||||
if ($LASTEXITCODE -ne 0) { throw 'package build failed' }
|
||||
npm test --prefix frontend
|
||||
if ($LASTEXITCODE -ne 0) { throw 'frontend tests failed' }
|
||||
npm run lint --prefix frontend
|
||||
if ($LASTEXITCODE -ne 0) { throw 'frontend lint failed' }
|
||||
npm run build --prefix frontend
|
||||
if ($LASTEXITCODE -ne 0) { throw 'frontend build failed' }
|
||||
|
||||
publish-and-deploy:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/staging'
|
||||
needs: validate
|
||||
runs-on: manman-win
|
||||
runs-on: xiaoxin
|
||||
timeout-minutes: 45
|
||||
defaults:
|
||||
run:
|
||||
shell: powershell
|
||||
env:
|
||||
REGISTRY_HOST: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com
|
||||
IMAGE_REPOSITORY: crpi-d1feco6itet73spp.cn-hongkong.personal.cr.aliyuncs.com/copse/jyotisha
|
||||
@@ -100,109 +78,90 @@ jobs:
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
function Assert-Exit([string]$Message) {
|
||||
if ($LASTEXITCODE -ne 0) { throw $Message }
|
||||
set -euo pipefail
|
||||
|
||||
[[ "${GITEA_EVENT_NAME:-}" == "push" && "${GITEA_REF:-}" == "refs/heads/staging" ]] || {
|
||||
echo "not an exact staging push" >&2
|
||||
exit 1
|
||||
}
|
||||
function Invoke-Ssh([string]$Command) {
|
||||
& ssh @script:SshOptions "$env:DEPLOY_USER@$env:DEPLOY_HOST" $Command
|
||||
Assert-Exit 'remote SSH command failed'
|
||||
[[ "${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
|
||||
}
|
||||
|
||||
if ($env:GITEA_EVENT_NAME -ne 'push' -or $env:GITEA_REF -ne 'refs/heads/staging') { throw 'not an exact staging push' }
|
||||
if ($env:GITEA_SHA -notmatch '^[0-9a-f]{40}$') { throw 'invalid staging commit SHA' }
|
||||
if ($env:DEPLOY_HOST -notmatch '^[A-Za-z0-9.-]+$' -or $env:DEPLOY_PORT -notmatch '^[1-9][0-9]{0,4}$' -or $env:DEPLOY_USER -notmatch '^[a-z_][a-z0-9_-]*$' -or $env:DEPLOY_PATH -notmatch '^/[A-Za-z0-9._/-]+$') { throw 'invalid staging SSH target' }
|
||||
if ($env:STAGING_URL -notmatch '^https://[A-Za-z0-9.-]+(?::[1-9][0-9]{0,4})?$' -or [string]::IsNullOrWhiteSpace($env:STAGING_KNOWN_HOSTS)) { throw 'invalid staging endpoint configuration' }
|
||||
$remoteLine = (& git ls-remote origin refs/heads/staging)
|
||||
Assert-Exit 'unable to resolve remote staging head'
|
||||
$remoteSha = (($remoteLine -split '\s+')[0])
|
||||
if ($remoteSha -ne $env:GITEA_SHA) { throw 'staging head changed before publication' }
|
||||
& git fetch origin main
|
||||
Assert-Exit 'unable to fetch reviewed main history'
|
||||
& git merge-base --is-ancestor $env:GITEA_SHA origin/main
|
||||
Assert-Exit 'staging revision is not in reviewed main history'
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($env:REGISTRY_USERNAME) -or [string]::IsNullOrWhiteSpace($env:REGISTRY_PASSWORD)) { throw 'registry credentials are missing' }
|
||||
$env:REGISTRY_PASSWORD | & docker login $env:REGISTRY_HOST --username $env:REGISTRY_USERNAME --password-stdin
|
||||
Assert-Exit 'registry login failed'
|
||||
|
||||
$sshRoot = Join-Path $env:RUNNER_TEMP 'jyotisha-staging-ssh'
|
||||
$keyPath = Join-Path $sshRoot 'id_ed25519'
|
||||
$knownHostsPath = Join-Path $sshRoot 'known_hosts'
|
||||
$archivePath = Join-Path $env:RUNNER_TEMP "deploy-$($env:GITEA_RUN_NUMBER)-$($env:GITEA_RUN_ATTEMPT).tar"
|
||||
$incoming = "$env:DEPLOY_PATH/.incoming/$env:GITEA_RUN_NUMBER-$env:GITEA_RUN_ATTEMPT"
|
||||
$script:SshOptions = @('-i', $keyPath, '-p', $env:DEPLOY_PORT, '-o', 'BatchMode=yes', '-o', 'IdentitiesOnly=yes', '-o', 'StrictHostKeyChecking=yes', '-o', "UserKnownHostsFile=$knownHostsPath")
|
||||
$scpOptions = @('-i', $keyPath, '-P', $env:DEPLOY_PORT, '-o', 'BatchMode=yes', '-o', 'IdentitiesOnly=yes', '-o', 'StrictHostKeyChecking=yes', '-o', "UserKnownHostsFile=$knownHostsPath")
|
||||
$remotePrepared = $false
|
||||
try {
|
||||
$apiTag = "$env:IMAGE_REPOSITORY`:api-$env:GITEA_SHA"
|
||||
$webTag = "$env:IMAGE_REPOSITORY`:web-$env:GITEA_SHA"
|
||||
& docker build --file deploy/railway-api.Dockerfile --tag $apiTag .
|
||||
Assert-Exit 'API image build failed'
|
||||
& docker push $apiTag
|
||||
Assert-Exit 'API image push failed'
|
||||
& docker build --file deploy/railway-web.Dockerfile --tag $webTag .
|
||||
Assert-Exit 'web image build failed'
|
||||
& docker push $webTag
|
||||
Assert-Exit 'web image push failed'
|
||||
|
||||
$apiDigests = (& docker image inspect --format '{{json .RepoDigests}}' $apiTag | ConvertFrom-Json)
|
||||
Assert-Exit 'unable to inspect API image digests'
|
||||
$webDigests = (& docker image inspect --format '{{json .RepoDigests}}' $webTag | ConvertFrom-Json)
|
||||
Assert-Exit 'unable to inspect web image digests'
|
||||
$apiRef = @($apiDigests | Where-Object { $_ -match "^$([regex]::Escape($env:IMAGE_REPOSITORY))@sha256:[0-9a-f]{64}$" })[0]
|
||||
$webRef = @($webDigests | Where-Object { $_ -match "^$([regex]::Escape($env:IMAGE_REPOSITORY))@sha256:[0-9a-f]{64}$" })[0]
|
||||
if (-not $apiRef -or -not $webRef) { throw 'immutable image digest was not published' }
|
||||
$apiDigest = ($apiRef -split '@', 2)[1]
|
||||
$webDigest = ($webRef -split '@', 2)[1]
|
||||
$manifestPath = Join-Path $env:RUNNER_TEMP 'staging-image-manifest.env'
|
||||
[IO.File]::WriteAllText($manifestPath, "git_sha=$env:GITEA_SHA`napi_digest=$apiDigest`nweb_digest=$webDigest`n", [Text.UTF8Encoding]::new($false))
|
||||
$manifestOutput = & node frontend/scripts/staging-image-manifest.mjs $manifestPath $env:GITEA_SHA $env:IMAGE_REPOSITORY
|
||||
Assert-Exit 'immutable image manifest validation failed'
|
||||
$images = @{}
|
||||
foreach ($line in $manifestOutput) {
|
||||
$parts = $line -split '=', 2
|
||||
if ($parts.Count -eq 2) { $images[$parts[0]] = $parts[1] }
|
||||
}
|
||||
if (-not $images.api_image -or -not $images.web_image) { throw 'image manifest output is incomplete' }
|
||||
|
||||
New-Item -ItemType Directory -Path $sshRoot -Force | Out-Null
|
||||
$normalizedKey = ($env:SSH_PRIVATE_KEY -replace "`r`n", "`n" -replace "`r", "`n").TrimEnd("`n") + "`n"
|
||||
[IO.File]::WriteAllText($keyPath, $normalizedKey, [Text.UTF8Encoding]::new($false))
|
||||
$normalizedHosts = ($env:STAGING_KNOWN_HOSTS -replace "`r`n", "`n" -replace "`r", "`n").TrimEnd("`n") + "`n"
|
||||
[IO.File]::WriteAllText($knownHostsPath, $normalizedHosts, [Text.UTF8Encoding]::new($false))
|
||||
|
||||
& tar -cf $archivePath deploy
|
||||
Assert-Exit 'deploy archive creation failed'
|
||||
Invoke-Ssh "install -d -m 700 '$incoming/.docker'"
|
||||
$remotePrepared = $true
|
||||
& scp @scpOptions $archivePath "$env:DEPLOY_USER@$env:DEPLOY_HOST`:$incoming/deploy.tar"
|
||||
Assert-Exit 'deploy archive upload failed'
|
||||
Invoke-Ssh "tar -xf '$incoming/deploy.tar' -C '$incoming' && rm -f -- '$incoming/deploy.tar'"
|
||||
|
||||
$previousSha = (& ssh @script:SshOptions "$env:DEPLOY_USER@$env:DEPLOY_HOST" "state='$env: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").Trim()
|
||||
Assert-Exit 'unable to read deployed staging revision'
|
||||
if ($previousSha -ne 'not-deployed' -and $previousSha -notmatch '^[0-9a-f]{40}$') { throw 'invalid deployed staging revision state' }
|
||||
$forwardVerified = 'false'
|
||||
if ($previousSha -ne 'not-deployed' -and $previousSha -ne $env:GITEA_SHA) {
|
||||
& git cat-file -e "$previousSha^{commit}"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
& git fetch origin $previousSha
|
||||
Assert-Exit 'unable to fetch deployed staging revision'
|
||||
}
|
||||
& git merge-base --is-ancestor $previousSha $env:GITEA_SHA
|
||||
Assert-Exit 'automatic rollback or divergent staging deployment refused'
|
||||
$forwardVerified = 'true'
|
||||
}
|
||||
|
||||
$env:REGISTRY_PASSWORD | & ssh @script:SshOptions "$env:DEPLOY_USER@$env:DEPLOY_HOST" "DOCKER_CONFIG='$incoming/.docker' docker login '$env:REGISTRY_HOST' --username '$env:REGISTRY_USERNAME' --password-stdin"
|
||||
Assert-Exit 'remote registry login failed'
|
||||
Invoke-Ssh "INCOMING_PATH='$incoming' DEPLOY_PATH='$env:DEPLOY_PATH' API_IMAGE='$($images.api_image)' WEB_IMAGE='$($images.web_image)' DEPLOY_SHA='$env:GITEA_SHA' EXPECTED_PREVIOUS_SHA='$previousSha' ALLOW_ROLLBACK='false' FORWARD_REVISION_VERIFIED='$forwardVerified' DOCKER_CONFIG='$incoming/.docker' STAGING_URL='$env:STAGING_URL' bash '$incoming/deploy/run-staging-deploy.sh'"
|
||||
} finally {
|
||||
if ($remotePrepared) {
|
||||
& ssh @script:SshOptions "$env:DEPLOY_USER@$env:DEPLOY_HOST" "DOCKER_CONFIG='$incoming/.docker' docker logout '$env:REGISTRY_HOST' >/dev/null 2>&1 || true; rm -rf -- '$incoming'" 2>$null
|
||||
}
|
||||
& docker logout $env:REGISTRY_HOST 2>$null | Out-Null
|
||||
Remove-Item $archivePath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $sshRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
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
|
||||
}
|
||||
|
||||
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'"
|
||||
|
||||
@@ -380,19 +380,19 @@ test("production remains manual-only and separate from staging database automati
|
||||
assert.doesNotMatch(production, /docker-compose\.postgres\.yml|db:migrate/);
|
||||
});
|
||||
|
||||
test("Gitea staging push uses the Windows runner and immutable ACR images", () => {
|
||||
test("Gitea staging push uses the xiaoxin Linux runner and immutable ACR images", () => {
|
||||
const workflow = read(giteaQualityWorkflow);
|
||||
assert.match(workflow, /runs-on: manman-win/);
|
||||
assert.match(workflow, /shell: powershell/);
|
||||
assert.equal(workflow.match(/runs-on: xiaoxin/g)?.length, 2);
|
||||
assert.match(workflow, /set -euo pipefail/);
|
||||
assert.match(workflow, /crpi-d1feco6itet73spp\.cn-hongkong\.personal\.cr\.aliyuncs\.com\/copse\/jyotisha/);
|
||||
assert.match(workflow, /secrets\.REGISTRY_USERNAME/);
|
||||
assert.match(workflow, /secrets\.REGISTRY_PASSWORD/);
|
||||
assert.match(workflow, /:api-\$env:GITEA_SHA/);
|
||||
assert.match(workflow, /:web-\$env:GITEA_SHA/);
|
||||
assert.match(workflow, /EXPECTED_PREVIOUS_SHA='\$previousSha'/);
|
||||
assert.match(workflow, /git merge-base --is-ancestor \$previousSha \$env:GITEA_SHA/);
|
||||
assert.match(workflow, /\$scpOptions = @\([^\n]*'-P'/);
|
||||
assert.doesNotMatch(workflow, /17631000304|copse\.ai\.2026/);
|
||||
assert.match(workflow, /api_tag="\$\{IMAGE_REPOSITORY\}:api-\$\{GITEA_SHA\}"/);
|
||||
assert.match(workflow, /web_tag="\$\{IMAGE_REPOSITORY\}:web-\$\{GITEA_SHA\}"/);
|
||||
assert.match(workflow, /EXPECTED_PREVIOUS_SHA='\$previous_sha'/);
|
||||
assert.match(workflow, /git merge-base --is-ancestor "\$previous_sha" "\$GITEA_SHA"/);
|
||||
assert.match(workflow, /scp_options=\(-i "\$key_path" -P "\$DEPLOY_PORT"/);
|
||||
assert.doesNotMatch(workflow, /shell: powershell|17631000304|copse\.ai\.2026/);
|
||||
});
|
||||
|
||||
test("staging scripts pass shell syntax validation", () => {
|
||||
|
||||
Reference in New Issue
Block a user