fix: pin staging compose selectors
This commit is contained in:
@@ -101,7 +101,7 @@ jobs:
|
||||
PREVIOUS_SHA="$(curl --fail --silent --show-error --max-time 10 "$STAGING_URL/api/health" 2>/dev/null | jq -r '.deployment.gitCommit // empty' || true)"
|
||||
test -n "$PREVIOUS_SHA" || PREVIOUS_SHA="not-deployed"
|
||||
PREVIOUS_IMAGES="$(ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
|
||||
"if [ -f '$DEPLOY_PATH/.env.staging' ] && [ -f '$DEPLOY_PATH/deploy/docker-compose.server.yml' ]; then cd '$DEPLOY_PATH' && docker compose --env-file .env.staging -f deploy/docker-compose.server.yml images --quiet; else echo not-deployed; fi")"
|
||||
"if [ -f '$DEPLOY_PATH/.env.staging' ] && [ -f '$DEPLOY_PATH/deploy/docker-compose.server.yml' ]; then cd '$DEPLOY_PATH' && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml images --quiet; else echo not-deployed; fi")"
|
||||
test -n "$PREVIOUS_IMAGES" || PREVIOUS_IMAGES="not-deployed"
|
||||
{
|
||||
echo "### Staging deployment state"
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
-e "$RSYNC_SSH" \
|
||||
./ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"
|
||||
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
|
||||
"cd '$DEPLOY_PATH' && bash deploy/validate-staging-env.sh .env.staging && docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet && GITHUB_SHA='$DEPLOY_GIT_SHA' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml up -d --build --remove-orphans"
|
||||
"cd '$DEPLOY_PATH' && bash deploy/validate-staging-env.sh .env.staging && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' GITHUB_SHA='$DEPLOY_GIT_SHA' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml up -d --build --remove-orphans"
|
||||
|
||||
- name: Verify staging
|
||||
env:
|
||||
@@ -140,5 +140,5 @@ jobs:
|
||||
ssh -i ~/.ssh/jyotisha-staging -p "$DEPLOY_PORT" \
|
||||
-o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
|
||||
"$DEPLOY_USER@$DEPLOY_HOST" \
|
||||
"cd '$DEPLOY_PATH' && docker compose --env-file .env.staging -f deploy/docker-compose.server.yml exec -T web node -e 'fetch(\"http://api:5200/api/health\").then(async r => { const body = await r.json(); if (!r.ok || body.status !== \"ok\" || body.swisseph_available !== true) process.exit(1); console.log(JSON.stringify(body)); })'"
|
||||
"cd '$DEPLOY_PATH' && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml exec -T web node -e 'fetch(\"http://api:5200/api/health\").then(async r => { const body = await r.json(); if (!r.ok || body.status !== \"ok\" || body.swisseph_available !== true) process.exit(1); console.log(JSON.stringify(body)); })'"
|
||||
echo "- Verified deployed SHA: \`$DEPLOY_GIT_SHA\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ CADDYFILE_PATH=./Caddyfile.staging
|
||||
SITE_ADDRESS=https://staging.jyotisha.chat
|
||||
```
|
||||
|
||||
After source sync and before `up`, the workflow validates `.env.staging` mode/selectors and runs `docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet`. For later manual inspections, run the same checks only after the tracked deployment files exist on the server. The first deployment should be manual:
|
||||
After source sync and before `up`, the workflow validates `.env.staging` mode/selectors, explicitly pins the three staging selectors against ambient shell overrides, and runs `docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet`. For later manual inspections, run the same checks only after the tracked deployment files exist on the server. The first deployment should be manual:
|
||||
|
||||
1. Confirm `/opt/jyotisha-staging/.env.staging` exists, has mode `0600`, and contains the three selectors above.
|
||||
2. Open GitHub Actions -> Jyotish Skill CI -> Run workflow, using workflow from `main`.
|
||||
|
||||
@@ -347,7 +347,7 @@ jobs:
|
||||
-e "$RSYNC_SSH" \
|
||||
./ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"
|
||||
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
|
||||
"cd '$DEPLOY_PATH' && bash deploy/validate-staging-env.sh .env.staging && docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet && GITHUB_SHA='$DEPLOY_GIT_SHA' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml up -d --build --remove-orphans"
|
||||
"cd '$DEPLOY_PATH' && bash deploy/validate-staging-env.sh .env.staging && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml config --quiet && APP_ENV_FILE='../.env.staging' CADDYFILE_PATH='./Caddyfile.staging' SITE_ADDRESS='https://staging.jyotisha.chat' GITHUB_SHA='$DEPLOY_GIT_SHA' docker compose --env-file .env.staging -f deploy/docker-compose.server.yml up -d --build --remove-orphans"
|
||||
|
||||
- name: Verify staging
|
||||
env:
|
||||
|
||||
@@ -71,7 +71,7 @@ push staging
|
||||
-> checkout 已测试 SHA
|
||||
-> 记录旧 SHA 和镜像 ID
|
||||
-> rsync 到 /opt/jyotisha-staging(排除所有 .env*)
|
||||
-> 校验 .env.staging 权限、固定选择器和 Compose 配置
|
||||
-> 校验 .env.staging 权限、固定选择器和 Compose 配置,并在 Compose 进程上显式钉死 staging 选择器
|
||||
-> docker compose build/up
|
||||
-> login、401 account、Python health smoke tests
|
||||
-> 记录部署 SHA
|
||||
|
||||
@@ -120,6 +120,17 @@ test("staging deploy consumes only the isolated staging environment and tested r
|
||||
assert.match(workflow, /deployment\.gitCommit/);
|
||||
assert.doesNotMatch(workflow, /PRODUCTION_SSH_PRIVATE_KEY/);
|
||||
assert.doesNotMatch(workflow, /103\.117\.123\.53/);
|
||||
|
||||
const composeLines = workflow
|
||||
.split("\n")
|
||||
.filter((line) => line.includes("docker compose"));
|
||||
assert.equal(workflow.match(/docker compose/g)?.length, 4);
|
||||
assert.equal(composeLines.length, 3);
|
||||
for (const line of composeLines) {
|
||||
assert.match(line, /APP_ENV_FILE='\.\.\/\.env\.staging'/);
|
||||
assert.match(line, /CADDYFILE_PATH='\.\/Caddyfile\.staging'/);
|
||||
assert.match(line, /SITE_ADDRESS='https:\/\/staging\.jyotisha\.chat'/);
|
||||
}
|
||||
});
|
||||
|
||||
test("staging rsync preserves every destination env variant during delete", () => {
|
||||
@@ -209,6 +220,28 @@ test("staging env validator rejects selector drift, duplicates, and unsafe permi
|
||||
);
|
||||
writeEnv(validSelectors);
|
||||
assert.equal(run().status, 0);
|
||||
const shellOverride = spawnSync(
|
||||
"docker",
|
||||
[
|
||||
"compose",
|
||||
"--env-file",
|
||||
envFile,
|
||||
"-f",
|
||||
composeFile,
|
||||
"config",
|
||||
"--format",
|
||||
"json",
|
||||
],
|
||||
{
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, APP_ENV_FILE: "../.env.production" },
|
||||
},
|
||||
);
|
||||
assert.equal(shellOverride.status, 0, shellOverride.stderr);
|
||||
assert.equal(
|
||||
JSON.parse(shellOverride.stdout).services.probe.environment.SELECTED,
|
||||
"../.env.production",
|
||||
);
|
||||
|
||||
writeEnv(["APP_ENV_FILE=../.env.production", ...validSelectors.slice(1)]);
|
||||
assert.notEqual(run().status, 0);
|
||||
|
||||
Reference in New Issue
Block a user