ci: remove GitHub mirror workflows and dead Gitea manual workflows

Gitea (git.copse.top) is the only CI/CD control plane; GitHub is a
read-only mirror whose Actions are being disabled in repository settings.

- Delete all 11 `.github/workflows/*.yml` (stale copies of the old design).
- Delete unused Gitea manual workflows `ci.yml`, `test.yml`,
  `publish-pypi.yml`, `apply-supabase-profile-migrations.yml`
  (0-1 historical runs, no remaining target).
- Fold the full `python -m pytest` tree and `tests/run_all.py` into
  `release-quality-gate.yml`, which previously only ran the curated
  release profile; update the `run_quality_gate.py` comment accordingly.
- Port `reset-staging-account.yml` to Gitea: `runs-on: xiaoxin`, bounded
  exact-SHA checkout, `refs/heads/staging` only, `staging-mutation`
  concurrency, same email/confirmation/host/port/user/path assertions.
- Repoint frontend workflow tests at `.gitea/workflows/`, drop the
  GitHub-only assertions, add coverage for the new reset workflow, and
  remove `tests/test_supabase_profile_migration_workflow.py`.
- Update AGENTS.md §6.8, README.md, and deploy/README.md to the current
  production/mirror facts and document the staging account reset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VawU7Xfd5jS9wUEXz1XYmS
This commit is contained in:
Jesse_Chen
2026-09-02 04:02:04 +00:00
co-authored by Claude Fable 5.1
parent 1a3e14e726
commit fb69e43c90
25 changed files with 208 additions and 1586 deletions
@@ -1,176 +0,0 @@
name: Apply production rectification migrations
on:
workflow_dispatch:
inputs:
operation:
description: Check pending migrations or apply them
required: true
default: check
type: choice
options:
- check
- apply
permissions:
contents: read
concurrency:
group: production-database-migrations
cancel-in-progress: false
env:
DEPLOY_HOST: 103.117.123.53
DEPLOY_PORT: "22000"
DEPLOY_USER: root
DEPLOY_PATH: /opt/jyotisha-app
jobs:
migrate:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout current main revision
uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
- name: Reject stale revision
run: |
tested_sha="$(git rev-parse HEAD)"
main_sha="$(git ls-remote origin refs/heads/main | awk '{print $1}')"
test "$tested_sha" = "$main_sha" || {
echo "Refusing stale migration revision $tested_sha; current main is $main_sha" >&2
exit 1
}
echo "Using current main revision $tested_sha"
- name: Configure SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_PRIVATE_KEY }}
run: |
install -m 700 -d ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/jyotisha-production
chmod 600 ~/.ssh/jyotisha-production
printf '%s\n' '[103.117.123.53]:22000 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHQJvN2Mo3Yq8e6ZIK4P2blJ5Vjj0HbknEuk7TyjhMbO' > ~/.ssh/known_hosts
- name: Upload reviewed migration files
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-production -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes"
SCP_OPTIONS="-i $HOME/.ssh/jyotisha-production -P $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes"
REMOTE_DIR="$DEPLOY_PATH/tmp/production-migrations/$GITHUB_RUN_ID"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -m 700 -d '$REMOTE_DIR'"
scp $SCP_OPTIONS \
frontend/supabase/migrations/20260723030000_align_conversational_follow_up_request.sql \
frontend/supabase/migrations/20260724010000_global_birth_locations.sql \
frontend/supabase/migrations/20260724020000_align_global_birthplace_rectification_contract.sql \
frontend/supabase/migrations/20260724030000_allow_assistant_only_rectification_regenerate.sql \
frontend/supabase/migrations/20260725010000_structured_conversational_date_confirmation.sql \
frontend/supabase/migrations/20260725020000_repair_structured_conversational_date_validator.sql \
frontend/supabase/migrations/20260726010000_backfill_reported_birth_time_status.sql \
"$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_DIR/"
- name: Check or apply reviewed migrations
env:
OPERATION: ${{ inputs.operation }}
run: |
set -euo pipefail
set +x
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-production -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=20"
REMOTE_DIR="$DEPLOY_PATH/tmp/production-migrations/$GITHUB_RUN_ID"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"cd '$DEPLOY_PATH' && OPERATION='$OPERATION' REMOTE_DIR='$REMOTE_DIR' bash -s" <<'REMOTE'
set -euo pipefail
set +x
trap 'rm -rf "$REMOTE_DIR"' EXIT
case "$OPERATION" in
check|apply) ;;
*) echo "invalid migration operation" >&2; exit 1 ;;
esac
ENV_FILE="$PWD/.env.production"
if [ ! -f "$ENV_FILE" ]; then
echo ".env.production missing" >&2
exit 1
fi
set -a
. "$ENV_FILE"
set +a
DB_URL="${SUPABASE_DB_URL:-${DATABASE_URL:-}}"
if [ -z "$DB_URL" ]; then
echo "production database URL is missing" >&2
exit 1
fi
psql_query() {
docker run --rm postgres:16-alpine \
psql "$DB_URL" --set ON_ERROR_STOP=1 --tuples-only --no-align --quiet --command "$1"
}
ledger="$(psql_query "select to_regclass('migration.schema_migrations')")"
if [ "$ledger" != "migration.schema_migrations" ]; then
echo "production migration ledger is missing" >&2
exit 1
fi
pending=0
for sql_file in \
"$REMOTE_DIR/20260723030000_align_conversational_follow_up_request.sql" \
"$REMOTE_DIR/20260724010000_global_birth_locations.sql" \
"$REMOTE_DIR/20260724020000_align_global_birthplace_rectification_contract.sql" \
"$REMOTE_DIR/20260724030000_allow_assistant_only_rectification_regenerate.sql" \
"$REMOTE_DIR/20260725010000_structured_conversational_date_confirmation.sql" \
"$REMOTE_DIR/20260725020000_repair_structured_conversational_date_validator.sql" \
"$REMOTE_DIR/20260726010000_backfill_reported_birth_time_status.sql"
do
filename="$(basename "$sql_file")"
checksum="$(sha256sum "$sql_file" | awk '{print $1}')"
recorded="$(psql_query "select checksum from migration.schema_migrations where filename = '$filename'")"
if [ -n "$recorded" ]; then
test "$recorded" = "$checksum" || {
echo "migration checksum mismatch: $filename" >&2
exit 1
}
echo "already applied $filename"
continue
fi
pending=$((pending + 1))
if [ "$OPERATION" = "check" ]; then
echo "pending $filename"
continue
fi
wrapped="$REMOTE_DIR/.wrapped-$filename"
python3 - "$sql_file" "$wrapped" "$filename" "$checksum" <<'PY'
import re
import sys
from pathlib import Path
source_path, target_path, filename, checksum = sys.argv[1:]
source = Path(source_path).read_text(encoding="utf-8")
source = re.sub(r"\A\s*begin\s*;\s*", "", source, count=1, flags=re.I)
source = re.sub(r"\s*commit\s*;\s*\Z", "\n", source, count=1, flags=re.I)
ledger = (
"\ninsert into migration.schema_migrations (filename, checksum) "
f"values ('{filename}', '{checksum}');\n"
)
Path(target_path).write_text(source + ledger, encoding="utf-8")
PY
docker run --rm -i postgres:16-alpine \
psql "$DB_URL" --set ON_ERROR_STOP=1 --single-transaction --quiet < "$wrapped"
verified="$(psql_query "select checksum from migration.schema_migrations where filename = '$filename'")"
test "$verified" = "$checksum" || {
echo "migration ledger verification failed: $filename" >&2
exit 1
}
echo "applied $filename"
done
if [ "$OPERATION" = "check" ] && [ "$pending" -gt 0 ]; then
echo "$pending reviewed production migrations are pending"
else
echo "production migration state is current"
fi
REMOTE
@@ -1,87 +0,0 @@
name: Apply Supabase profile migrations
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: supabase-profile-migrations
cancel-in-progress: false
env:
DEPLOY_HOST: 103.117.123.53
DEPLOY_PORT: "22000"
DEPLOY_USER: root
DEPLOY_PATH: /opt/jyotisha-app
jobs:
apply:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout migration files
uses: actions/checkout@v4
- name: Configure SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_PRIVATE_KEY }}
run: |
install -m 700 -d ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/jyotisha-production
chmod 600 ~/.ssh/jyotisha-production
printf '%s\n' '[103.117.123.53]:22000 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHQJvN2Mo3Yq8e6ZIK4P2blJ5Vjj0HbknEuk7TyjhMbO' > ~/.ssh/known_hosts
- name: Copy profile migrations to VPS
run: |
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-production -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes"
RSYNC_SSH="ssh $SSH_OPTIONS"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -m 700 -d '$DEPLOY_PATH/tmp/profile-migrations'"
rsync -az -e "$RSYNC_SSH" \
frontend/supabase/migrations/20260718050000_profiles_service_role_upsert_grants.sql \
frontend/supabase/migrations/20260718060000_profiles_service_role_least_privilege.sql \
frontend/supabase/migrations/20260718070000_profiles_service_role_upsert_id.sql \
frontend/supabase/migrations/20260718080000_profiles_service_role_account_upsert_selects.sql \
frontend/supabase/migrations/20260718100000_repair_missing_chart_profiles.sql \
frontend/supabase/migrations/20260718102000_recover_missing_profile_rows.sql \
frontend/supabase/migrations/20260718103000_profile_birth_time_declaration_grants.sql \
frontend/supabase/migrations/20260718104000_chart_profiles_upsert_id_grant.sql \
frontend/supabase/migrations/20260721100000_chat_sessions_delete_grant.sql \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/tmp/profile-migrations/"
- name: Apply profile migrations using VPS database URL
run: |
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-production -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "cd '$DEPLOY_PATH' && bash -s" <<'REMOTE'
set -euo pipefail
set +x
ENV_FILE="$PWD/.env.production"
if [ ! -f "$ENV_FILE" ]; then
echo ".env.production missing" >&2
exit 1
fi
set -a
. "$ENV_FILE"
set +a
DB_URL="${SUPABASE_DB_URL:-${DATABASE_URL:-}}"
if [ -z "$DB_URL" ]; then
echo "SUPABASE_DB_URL or DATABASE_URL is required in .env.production" >&2
exit 1
fi
for SQL_FILE in \
tmp/profile-migrations/20260718050000_profiles_service_role_upsert_grants.sql \
tmp/profile-migrations/20260718060000_profiles_service_role_least_privilege.sql \
tmp/profile-migrations/20260718070000_profiles_service_role_upsert_id.sql \
tmp/profile-migrations/20260718080000_profiles_service_role_account_upsert_selects.sql \
tmp/profile-migrations/20260718100000_repair_missing_chart_profiles.sql \
tmp/profile-migrations/20260718102000_recover_missing_profile_rows.sql \
tmp/profile-migrations/20260718103000_profile_birth_time_declaration_grants.sql \
tmp/profile-migrations/20260718104000_chart_profiles_upsert_id_grant.sql \
tmp/profile-migrations/20260721100000_chat_sessions_delete_grant.sql
do
echo "applying $(basename "$SQL_FILE")"
cat "$SQL_FILE" | docker run --rm -i postgres:16-alpine \
psql "$DB_URL" --set ON_ERROR_STOP=1 --quiet
done
REMOTE
-145
View File
@@ -1,145 +0,0 @@
name: Staging Backend Quality Gate
on:
pull_request:
paths:
- '.github/workflows/backend-quality-gate.yml'
- '.github/workflows/deploy-staging.yml'
- '.github/workflows/migrate-staging-database.yml'
- 'deploy/**'
- 'frontend/**'
- 'jyotish_vedic/**'
- 'scripts/**'
- 'tests/**'
- 'mcp_server.py'
- 'pyproject.toml'
- 'requirements*.txt'
push:
branches: [staging]
workflow_dispatch:
concurrency:
group: backend-quality-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
python -m pip install playwright
python -m playwright install --with-deps chrome
npm ci --prefix frontend
- name: Run Python quick quality gate
shell: bash
run: |
set -o 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
python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
mkdir -p artifacts
python scripts/run_quality_gate.py \
--profile quick --skip-yoga-logic --skip-frontend-runtime \
2>&1 | tee artifacts/quick-quality-gate.log
python scripts/commercial_privacy_artifact_scan.py --json
python -m build
- name: Upload quick quality gate diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: quick-quality-gate-diagnostics
path: artifacts/quick-quality-gate.log
- name: Validate frontend and database contracts
run: |
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend
publish:
if: github.event_name == 'push' && github.ref == 'refs/heads/staging'
needs: validate
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish API image
id: api_build
uses: docker/build-push-action@v6
with:
context: .
file: deploy/railway-api.Dockerfile
push: true
tags: ghcr.io/jesse-ux/jyotisha-api:${{ github.sha }}
- name: Build and publish web image
id: web_build
uses: docker/build-push-action@v6
with:
context: .
file: deploy/railway-web.Dockerfile
build-args: |
NEXT_DEPLOYMENT_ID=${{ github.sha }}
push: true
tags: ghcr.io/jesse-ux/jyotisha-web:${{ github.sha }}
- name: Record immutable staging image manifest
env:
API_DIGEST: ${{ steps.api_build.outputs.digest }}
WEB_DIGEST: ${{ steps.web_build.outputs.digest }}
run: |
set -euo pipefail
[[ "$GITHUB_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$API_DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]
[[ "$WEB_DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]
install -d -m 700 artifacts/staging-images
umask 077
printf 'git_sha=%s\napi_digest=%s\nweb_digest=%s\n' \
"$GITHUB_SHA" "$API_DIGEST" "$WEB_DIGEST" \
> artifacts/staging-images/manifest.env
node frontend/scripts/staging-image-manifest.mjs \
artifacts/staging-images/manifest.env "$GITHUB_SHA" >/dev/null
- name: Upload immutable staging image manifest
uses: actions/upload-artifact@v4
with:
name: staging-image-manifest-${{ github.sha }}-${{ github.run_attempt }}
path: artifacts/staging-images/manifest.env
if-no-files-found: error
retention-days: 30
-68
View File
@@ -1,68 +0,0 @@
name: Jyotish Skill CI
on:
push:
branches: [staging]
workflow_dispatch:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
npm ci --prefix frontend
- name: Print environment diagnostics
run: |
python --version
node --version
npm --version
- name: Run Ruff lint for quality gate files
run: 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
- name: Run Python syntax check
run: python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
- name: Run quick quality gate
run: |
mkdir -p artifacts
python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime 2>&1 | tee artifacts/quick-quality-gate.log
- name: Run commercial privacy artifact gate
run: python scripts/commercial_privacy_artifact_scan.py --json
- name: Upload quick quality gate diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: quick-quality-gate-diagnostics
path: artifacts/
- name: Validate production web
env:
NEXT_PUBLIC_SUPABASE_URL: https://ci-placeholder.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend
- name: Build Python package
run: python -m build --no-isolation
-16
View File
@@ -1,16 +0,0 @@
name: Production deployment moved to Gitea
on:
workflow_dispatch:
permissions:
contents: read
jobs:
retired:
runs-on: ubuntu-latest
steps:
- name: Refuse deployment from the mirror
run: |
echo "Production deployment is controlled by .gitea/workflows/deploy-production.yml in git.copse.top." >&2
exit 1
-254
View File
@@ -1,254 +0,0 @@
name: Deploy staging
on:
workflow_run:
workflows: ["Staging Backend Quality Gate"]
types: [completed]
workflow_dispatch:
inputs:
deploy_sha:
description: Exact tested 40-character staging commit SHA
required: true
type: string
allow_rollback:
description: Explicitly permit a manual rollback to an older tested SHA
required: true
default: false
type: boolean
permissions:
contents: read
actions: read
packages: read
concurrency:
group: staging-mutation
cancel-in-progress: false
queue: max
jobs:
deploy:
if: github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'staging')
runs-on: ubuntu-latest
timeout-minutes: 30
environment:
name: staging
url: ${{ vars.STAGING_URL }}
env:
DEPLOY_HOST: ${{ vars.STAGING_HOST }}
DEPLOY_PORT: ${{ vars.STAGING_PORT }}
DEPLOY_USER: ${{ vars.STAGING_USER }}
DEPLOY_PATH: ${{ vars.STAGING_PATH }}
STAGING_URL: ${{ vars.STAGING_URL }}
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
steps:
- name: Validate tested revision and gate run
id: revision
env:
REQUESTED_SHA: ${{ github.event.workflow_run.head_sha || inputs.deploy_sha }}
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
WORKFLOW_RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
REQUESTED_ROLLBACK: ${{ inputs.allow_rollback || 'false' }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
[[ "$REQUESTED_SHA" =~ ^[0-9a-f]{40}$ ]] || {
echo "deploy_sha must be a lowercase full commit SHA" >&2
exit 1
}
allow_rollback=false
if [ "$REQUESTED_ROLLBACK" = "true" ]; then
[ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] || {
echo "rollback authorization is manual-only" >&2
exit 1
}
allow_rollback=true
fi
gate_run_id="$WORKFLOW_RUN_ID"
gate_run_attempt="$WORKFLOW_RUN_ATTEMPT"
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
runs="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/workflows/backend-quality-gate.yml/runs?head_sha=$REQUESTED_SHA&branch=staging&event=push&status=success&per_page=100")"
selected_run="$(jq -cer --arg sha "$REQUESTED_SHA" '
[.workflow_runs[] | select(
.head_sha == $sha and .head_branch == "staging" and
.event == "push" and .conclusion == "success"
)] | sort_by(.id) | reverse | first
' <<<"$runs")"
gate_run_id="$(jq -er '.id' <<<"$selected_run")"
gate_run_attempt="$(jq -er '.run_attempt' <<<"$selected_run")"
fi
[[ "$gate_run_id" =~ ^[0-9]+$ ]] || {
echo "no successful exact-SHA staging quality gate run found" >&2
exit 1
}
[[ "$gate_run_attempt" =~ ^[1-9][0-9]*$ ]] || {
echo "invalid staging quality gate run attempt" >&2
exit 1
}
staging_head="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/git/ref/heads/staging" |
jq -er '.object.sha')"
if [ "$allow_rollback" = "false" ] && [ "$REQUESTED_SHA" != "$staging_head" ]; then
echo "stale staging revision refused; use explicit manual rollback only when intended" >&2
exit 1
fi
{
echo "sha=$REQUESTED_SHA"
echo "gate_run_id=$gate_run_id"
echo "gate_run_attempt=$gate_run_attempt"
echo "allow_rollback=$allow_rollback"
} >>"$GITHUB_OUTPUT"
- name: Checkout trusted main controller
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
persist-credentials: false
- name: Download gate-produced image manifest
uses: actions/download-artifact@v4
with:
name: staging-image-manifest-${{ steps.revision.outputs.sha }}-${{ steps.revision.outputs.gate_run_attempt }}
path: artifacts/staging-image
github-token: ${{ github.token }}
run-id: ${{ steps.revision.outputs.gate_run_id }}
- name: Validate immutable image manifest
id: images
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
run: |
set -euo pipefail
node frontend/scripts/staging-image-manifest.mjs \
artifacts/staging-image/manifest.env "$DEPLOY_SHA" >>"$GITHUB_OUTPUT"
- name: Verify reviewed revision and staging target
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
run: |
set -euo pipefail
git cat-file -e "$DEPLOY_SHA^{commit}"
git merge-base --is-ancestor "$DEPLOY_SHA" HEAD || {
echo "staging revision is not in the reviewed main history" >&2
exit 1
}
test "$DEPLOY_HOST" = "118.26.111.127"
test "$DEPLOY_PORT" = "22"
test "$DEPLOY_USER" = "deploy"
test "$DEPLOY_PATH" = "/opt/jyotisha-staging"
test "$STAGING_URL" = "https://staging.jyotisha.chat"
test -n "$STAGING_KNOWN_HOSTS"
- name: Configure pinned staging SSH
env:
SSH_PRIVATE_KEY_BASE64: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
run: |
set -euo pipefail
test -n "$SSH_PRIVATE_KEY_BASE64"
install -m 700 -d ~/.ssh
printf '%s' "$SSH_PRIVATE_KEY_BASE64" | base64 --decode >~/.ssh/jyotisha-staging
chmod 600 ~/.ssh/jyotisha-staging
ssh-keygen -y -f ~/.ssh/jyotisha-staging >/dev/null
printf '%s\n' "$STAGING_KNOWN_HOSTS" >~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Verify forward-only deployed revision
id: previous
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
ALLOW_ROLLBACK: ${{ steps.revision.outputs.allow_rollback }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
previous_sha="$(ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"state='$DEPLOY_PATH/.state/deployed-revision'; if [ -r \"\$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")"
if [ "$previous_sha" != "not-deployed" ] && [[ ! "$previous_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "invalid deployed staging revision state" >&2
exit 1
fi
forward_verified=true
if [ "$ALLOW_ROLLBACK" = "false" ] &&
[ "$previous_sha" != "not-deployed" ] &&
[ "$previous_sha" != "$DEPLOY_SHA" ]; then
comparison="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/compare/$previous_sha...$DEPLOY_SHA")"
jq -e --arg base "$previous_sha" '
.status == "ahead" and .merge_base_commit.sha == $base
' <<<"$comparison" >/dev/null || {
echo "automatic staging rollback or divergent deploy refused" >&2
exit 1
}
fi
{
echo "sha=$previous_sha"
echo "forward_verified=$forward_verified"
} >>"$GITHUB_OUTPUT"
- name: Stage trusted controller files in an isolated incoming directory
id: incoming
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=20"
RSYNC_SSH="ssh $SSH_OPTIONS"
incoming="$DEPLOY_PATH/.incoming/$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -d -m 700 '$incoming'"
echo "path=$incoming" >>"$GITHUB_OUTPUT"
rsync -az --delete --prune-empty-dirs \
--include='/deploy/' --include='/deploy/***' --exclude='*' \
-e "$RSYNC_SSH" ./ "$DEPLOY_USER@$DEPLOY_HOST:$incoming/"
- name: Log in to GHCR with run-local Docker state
env:
GHCR_TOKEN: ${{ github.token }}
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -d -m 700 '$INCOMING_PATH/.docker'"
printf '%s' "$GHCR_TOKEN" | ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"DOCKER_CONFIG='$INCOMING_PATH/.docker' docker login ghcr.io --username '$GITHUB_ACTOR' --password-stdin"
- name: Deploy and verify exact image digests under host lock
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
API_IMAGE: ${{ steps.images.outputs.api_image }}
WEB_IMAGE: ${{ steps.images.outputs.web_image }}
ALLOW_ROLLBACK: ${{ steps.revision.outputs.allow_rollback }}
EXPECTED_PREVIOUS_SHA: ${{ steps.previous.outputs.sha }}
FORWARD_REVISION_VERIFIED: ${{ steps.previous.outputs.forward_verified }}
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=20"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"INCOMING_PATH='$INCOMING_PATH' DEPLOY_PATH='$DEPLOY_PATH' API_IMAGE='$API_IMAGE' WEB_IMAGE='$WEB_IMAGE' DEPLOY_SHA='$DEPLOY_SHA' EXPECTED_PREVIOUS_SHA='$EXPECTED_PREVIOUS_SHA' ALLOW_ROLLBACK='$ALLOW_ROLLBACK' FORWARD_REVISION_VERIFIED='$FORWARD_REVISION_VERIFIED' DOCKER_CONFIG='$INCOMING_PATH/.docker' STAGING_URL='$STAGING_URL' bash '$INCOMING_PATH/deploy/run-staging-deploy.sh'" |
tee staging-deploy-result.txt
sed 's/^/- /' staging-deploy-result.txt >>"$GITHUB_STEP_SUMMARY"
- name: Remove run-local staging files
if: always() && steps.incoming.outputs.path != ''
continue-on-error: true
env:
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"DOCKER_CONFIG='$INCOMING_PATH/.docker' docker logout ghcr.io >/dev/null 2>&1 || true; rm -rf -- '$INCOMING_PATH'"
@@ -1,234 +0,0 @@
name: Migrate Staging Database
on:
workflow_dispatch:
inputs:
deploy_sha:
description: Full tested staging commit SHA to migrate
required: true
type: string
concurrency:
group: staging-mutation
cancel-in-progress: false
queue: max
permissions:
contents: read
actions: write
packages: read
jobs:
migrate:
environment: staging
runs-on: ubuntu-latest
timeout-minutes: 20
env:
DEPLOY_HOST: ${{ vars.STAGING_HOST }}
DEPLOY_PORT: ${{ vars.STAGING_PORT }}
DEPLOY_USER: ${{ vars.STAGING_USER }}
DEPLOY_PATH: ${{ vars.STAGING_PATH }}
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
steps:
- name: Validate current tested staging revision
id: revision
env:
REQUESTED_SHA: ${{ inputs.deploy_sha }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
[[ "$REQUESTED_SHA" =~ ^[0-9a-f]{40}$ ]] || {
echo "deploy_sha must be a lowercase full commit SHA" >&2
exit 1
}
runs="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/workflows/backend-quality-gate.yml/runs?head_sha=$REQUESTED_SHA&branch=staging&event=push&status=success&per_page=100")"
selected_run="$(jq -cer --arg sha "$REQUESTED_SHA" '
[.workflow_runs[] | select(
.head_sha == $sha and .head_branch == "staging" and
.event == "push" and .conclusion == "success"
)] | sort_by(.id) | reverse | first
' <<<"$runs")"
gate_run_id="$(jq -er '.id' <<<"$selected_run")"
gate_run_attempt="$(jq -er '.run_attempt' <<<"$selected_run")"
[[ "$gate_run_id" =~ ^[0-9]+$ ]]
[[ "$gate_run_attempt" =~ ^[1-9][0-9]*$ ]]
staging_head="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/git/ref/heads/staging" |
jq -er '.object.sha')"
[ "$REQUESTED_SHA" = "$staging_head" ] || {
echo "stale staging migration refused; migrate the current staging head" >&2
exit 1
}
{
echo "sha=$REQUESTED_SHA"
echo "gate_run_id=$gate_run_id"
echo "gate_run_attempt=$gate_run_attempt"
} >>"$GITHUB_OUTPUT"
- name: Checkout trusted main controller
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
persist-credentials: false
- name: Download gate-produced image manifest
uses: actions/download-artifact@v4
with:
name: staging-image-manifest-${{ steps.revision.outputs.sha }}-${{ steps.revision.outputs.gate_run_attempt }}
path: artifacts/staging-image
github-token: ${{ github.token }}
run-id: ${{ steps.revision.outputs.gate_run_id }}
- name: Validate immutable migration image
id: images
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
run: |
set -euo pipefail
node frontend/scripts/staging-image-manifest.mjs \
artifacts/staging-image/manifest.env "$DEPLOY_SHA" >>"$GITHUB_OUTPUT"
- name: Verify reviewed revision and staging target
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
run: |
set -euo pipefail
git cat-file -e "$DEPLOY_SHA^{commit}"
git merge-base --is-ancestor "$DEPLOY_SHA" HEAD || {
echo "staging revision is not in the reviewed main history" >&2
exit 1
}
test "$DEPLOY_HOST" = "118.26.111.127"
test "$DEPLOY_PORT" = "22"
test "$DEPLOY_USER" = "deploy"
test "$DEPLOY_PATH" = "/opt/jyotisha-staging"
test -n "$STAGING_KNOWN_HOSTS"
- name: Configure pinned staging SSH
env:
SSH_PRIVATE_KEY_BASE64: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
run: |
set -euo pipefail
test -n "$SSH_PRIVATE_KEY_BASE64"
install -m 700 -d ~/.ssh
printf '%s' "$SSH_PRIVATE_KEY_BASE64" | base64 --decode >~/.ssh/jyotisha-staging
chmod 600 ~/.ssh/jyotisha-staging
ssh-keygen -y -f ~/.ssh/jyotisha-staging >/dev/null
printf '%s\n' "$STAGING_KNOWN_HOSTS" >~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Verify forward-only migration revision
id: previous
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
previous_sha="$(ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"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")"
if [ "$previous_sha" != "not-deployed" ] && [[ ! "$previous_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "invalid deployed staging revision state" >&2
exit 1
fi
if [ "$previous_sha" != "not-deployed" ] && [ "$previous_sha" != "$DEPLOY_SHA" ]; then
comparison="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/compare/$previous_sha...$DEPLOY_SHA")"
jq -e --arg base "$previous_sha" '
.status == "ahead" and .merge_base_commit.sha == $base
' <<<"$comparison" >/dev/null || {
echo "stale or divergent staging migration refused" >&2
exit 1
}
fi
{
echo "sha=$previous_sha"
echo "forward_verified=true"
} >>"$GITHUB_OUTPUT"
- name: Stage trusted controller files in an isolated incoming directory
id: incoming
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=20"
RSYNC_SSH="ssh $SSH_OPTIONS"
incoming="$DEPLOY_PATH/.incoming/$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -d -m 700 '$incoming'"
echo "path=$incoming" >>"$GITHUB_OUTPUT"
rsync -az --delete --prune-empty-dirs \
--include='/deploy/' --include='/deploy/***' --exclude='*' \
-e "$RSYNC_SSH" ./ "$DEPLOY_USER@$DEPLOY_HOST:$incoming/"
- name: Log in to GHCR with run-local Docker state
env:
GHCR_TOKEN: ${{ github.token }}
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" "install -d -m 700 '$INCOMING_PATH/.docker'"
printf '%s' "$GHCR_TOKEN" | ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"DOCKER_CONFIG='$INCOMING_PATH/.docker' docker login ghcr.io --username '$GITHUB_ACTOR' --password-stdin"
- name: Apply exact-image migrations under host lock
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
WEB_IMAGE: ${{ steps.images.outputs.web_image }}
EXPECTED_PREVIOUS_SHA: ${{ steps.previous.outputs.sha }}
FORWARD_REVISION_VERIFIED: ${{ steps.previous.outputs.forward_verified }}
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=20"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"INCOMING_PATH='$INCOMING_PATH' DEPLOY_PATH='$DEPLOY_PATH' WEB_IMAGE='$WEB_IMAGE' DEPLOY_SHA='$DEPLOY_SHA' EXPECTED_PREVIOUS_SHA='$EXPECTED_PREVIOUS_SHA' FORWARD_REVISION_VERIFIED='$FORWARD_REVISION_VERIFIED' DOCKER_CONFIG='$INCOMING_PATH/.docker' bash '$INCOMING_PATH/deploy/run-staging-migration.sh'"
- name: Dispatch current exact-SHA staging deployment
env:
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
staging_head="$(curl --fail --silent --show-error \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/git/ref/heads/staging" |
jq -er '.object.sha')"
[ "$DEPLOY_SHA" = "$staging_head" ] || {
echo "staging advanced during migration; refusing stale deployment dispatch" >&2
exit 1
}
payload="$(jq -cn --arg deploy_sha "$DEPLOY_SHA" \
'{ref:"main",inputs:{deploy_sha:$deploy_sha,allow_rollback:"false"}}')"
curl --fail --silent --show-error --request POST \
--header "Authorization: Bearer $GH_TOKEN" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
--header "Content-Type: application/json" \
--data "$payload" \
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/workflows/deploy-staging.yml/dispatches"
- name: Remove run-local staging files
if: always() && steps.incoming.outputs.path != ''
continue-on-error: true
env:
INCOMING_PATH: ${{ steps.incoming.outputs.path }}
run: |
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"DOCKER_CONFIG='$INCOMING_PATH/.docker' docker logout ghcr.io >/dev/null 2>&1 || true; rm -rf -- '$INCOMING_PATH'"
-33
View File
@@ -1,33 +0,0 @@
name: Publish to PyPI
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build tools
run: pip install build twine
- name: Build package
run: python -m build
- name: Check package metadata
run: twine check dist/*
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
@@ -1,54 +0,0 @@
name: Jyotish Release Quality Gate
on:
workflow_dispatch:
jobs:
release-quality-gate:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
python -m pip install playwright
python -m playwright install --with-deps chromium
npm ci --prefix frontend
- name: Print environment diagnostics
run: |
python --version
node --version
npm --version
npm --prefix frontend exec -- next --version
- name: Run release quality gate
env:
NEXT_PUBLIC_SUPABASE_URL: https://ci-placeholder.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
mkdir -p artifacts
python scripts/run_quality_gate.py --profile release 2>&1 | tee artifacts/release-quality-gate.log
- name: Upload release quality gate diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: release-quality-gate-diagnostics
path: artifacts/
@@ -1,82 +0,0 @@
name: Reset Staging Account
on:
workflow_dispatch:
inputs:
expected_deploy_sha:
description: Exact 40-character SHA currently deployed to staging
required: true
type: string
email:
description: Exact staging account email
required: true
type: string
confirmation:
description: Type RESET followed by a space and the exact email
required: true
type: string
permissions:
contents: read
concurrency:
group: staging-mutation
cancel-in-progress: false
jobs:
reset:
runs-on: ubuntu-latest
timeout-minutes: 10
environment:
name: staging
url: ${{ vars.STAGING_URL }}
env:
DEPLOY_HOST: ${{ vars.STAGING_HOST }}
DEPLOY_PORT: ${{ vars.STAGING_PORT }}
DEPLOY_USER: ${{ vars.STAGING_USER }}
DEPLOY_PATH: ${{ vars.STAGING_PATH }}
STAGING_KNOWN_HOSTS: ${{ vars.STAGING_KNOWN_HOSTS }}
EXPECTED_DEPLOY_SHA: ${{ inputs.expected_deploy_sha }}
RESET_EMAIL: ${{ inputs.email }}
RESET_CONFIRMATION: ${{ inputs.confirmation }}
steps:
- name: Checkout trusted controller
uses: actions/checkout@v4
with:
ref: main
persist-credentials: false
- name: Validate account reset request and staging target
run: |
set -euo pipefail
[[ "$EXPECTED_DEPLOY_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$RESET_EMAIL" =~ ^[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,63}$ ]]
test "$RESET_CONFIRMATION" = "RESET $RESET_EMAIL"
test "$DEPLOY_HOST" = "118.26.111.127"
test "$DEPLOY_PORT" = "22"
test "$DEPLOY_USER" = "deploy"
test "$DEPLOY_PATH" = "/opt/jyotisha-staging"
test -n "$STAGING_KNOWN_HOSTS"
bash -n deploy/reset-staging-account.sh
- name: Configure pinned staging SSH
env:
SSH_PRIVATE_KEY_BASE64: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
run: |
set -euo pipefail
test -n "$SSH_PRIVATE_KEY_BASE64"
install -d -m 700 ~/.ssh
printf '%s' "$SSH_PRIVATE_KEY_BASE64" | base64 --decode >~/.ssh/jyotisha-staging
chmod 600 ~/.ssh/jyotisha-staging
ssh-keygen -y -f ~/.ssh/jyotisha-staging >/dev/null
printf '%s\n' "$STAGING_KNOWN_HOSTS" >~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Reset one staging account under host lock
run: |
set -euo pipefail
SSH_OPTIONS="-i $HOME/.ssh/jyotisha-staging -p $DEPLOY_PORT -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=10"
ssh $SSH_OPTIONS "$DEPLOY_USER@$DEPLOY_HOST" \
"DEPLOY_PATH='$DEPLOY_PATH' EXPECTED_DEPLOY_SHA='$EXPECTED_DEPLOY_SHA' RESET_EMAIL='$RESET_EMAIL' RESET_CONFIRMATION='$RESET_CONFIRMATION' bash -s" \
< deploy/reset-staging-account.sh
-47
View File
@@ -1,47 +0,0 @@
name: Jyotish Skill Tests
on:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.11' }
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
npm ci --prefix frontend
- name: Print environment diagnostics
run: |
python --version
node --version
npm --version
- name: Run pytest suite
run: |
mkdir -p artifacts
python -m pytest -vv --maxfail=1 --junitxml=artifacts/pytest.xml 2>&1 | tee artifacts/pytest.log
- name: Upload pytest diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-diagnostics
path: artifacts/
- name: Run legacy runner
run: python tests/run_all.py
- name: Validate production web
env:
NEXT_PUBLIC_SUPABASE_URL: https://ci-placeholder.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend