-
Notifications
You must be signed in to change notification settings - Fork 1
114 lines (102 loc) · 5.54 KB
/
Copy pathprune-sessions.yml
File metadata and controls
114 lines (102 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# Daily cleanup: delete chat sessions whose auto_delete_at has passed — platform transcript
# first, then the DB row (lik-ui/scripts/prune_sessions.py). Runs unattended on a schedule
# with no internet-facing endpoint and no long-lived shared secret: GitHub OIDC assumes the
# SSM-read role, fetches the shared Anthropic key + DB master password from SSM, and connects
# to the public Lightsail Postgres directly. See
# docs/plans/2026-07-28-001-feat-session-auto-delete-plan.md.
#
# Prerequisites (set once in repo Settings, on the `prod` environment):
# - Variable AWS_SSM_READ_ROLE_ARN = the github_ssm_read_role_arn Terraform output.
# - Variable AWS_REGION = us-east-1.
# - Variable SSM_PREFIX = the Terraform var.ssm_prefix (e.g. /ik-arch/prod). Required, set on
# the environment (resolves per the job's `environment:`), alongside AWS_REGION and the role.
# No LIK_UI_DB_* variables are needed: the DB password + Anthropic key are read from SSM, the DB
# target (instance + db name) from the Terraform-authored $SSM_PREFIX/config/ params, and the DB
# host/port/master-user are discovered from the Lightsail instance. All at run time — never GitHub
# secrets/variables.
name: Prune expired sessions
on:
schedule:
- cron: "17 8 * * *" # daily at 08:17 UTC
workflow_dispatch: {} # manual run for verification / catch-up
permissions:
id-token: write # required for OIDC
contents: read
issues: write # so a failed run can open a tracking issue (this is the only alerting)
# Never let two cleanups overlap (a slow run + the next tick).
concurrency:
group: prune-sessions
cancel-in-progress: false
jobs:
prune:
runs-on: ubuntu-latest
# Fail fast instead of hanging: an unattended run that stalls on a slow DB/API otherwise
# holds the concurrency group and starves the next scheduled tick.
timeout-minutes: 15
# The prod environment scopes the vars/role below and its `main` branch policy; scheduled
# runs execute on the default branch, which satisfies it. Env-scoped vars only resolve
# when the job declares this.
environment: prod
env:
SSM_PREFIX: ${{ vars.SSM_PREFIX }}
LIK_UI_ENV: prod
LIK_UI_DB_SSLMODE: require
# DB_INSTANCE + LIK_UI_DB_NAME are no longer hardcoded here: they are read at run time from
# the Terraform-authored $SSM_PREFIX/config/ params (infra/config.tf), so a CI copy can
# never drift from infra.
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_SSM_READ_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Fetch config + secrets from SSM
run: |
# Non-secret config (String params) — resolve the DB target together, one source.
# A missing param makes get-parameter exit non-zero, which aborts the step (bash -e):
# a "staging" run can never half-resolve to the prod instance, or vice versa.
inst=$(aws ssm get-parameter --name "$SSM_PREFIX/config/DB_INSTANCE" \
--query Parameter.Value --output text)
echo "DB_INSTANCE=$inst" >> "$GITHUB_ENV"
uidb=$(aws ssm get-parameter --name "$SSM_PREFIX/config/LIK_UI_DB_NAME" \
--query Parameter.Value --output text)
echo "LIK_UI_DB_NAME=$uidb" >> "$GITHUB_ENV"
# Secrets (SecureString) — masked before they touch the log.
key=$(aws ssm get-parameter --name "$SSM_PREFIX/shared/ANTHROPIC_API_KEY" \
--with-decryption --query Parameter.Value --output text)
echo "::add-mask::$key"
echo "LIK_UI_ANTHROPIC_API_KEY=$key" >> "$GITHUB_ENV"
pw=$(aws ssm get-parameter --name "$SSM_PREFIX/shared/DB_MASTER_PASSWORD" \
--with-decryption --query Parameter.Value --output text)
echo "::add-mask::$pw"
echo "LIK_UI_DB_PASSWORD=$pw" >> "$GITHUB_ENV"
- name: Discover DB host/port/user from Lightsail
run: |
read -r host port user < <(aws lightsail get-relational-database \
--relational-database-name "$DB_INSTANCE" \
--query 'relationalDatabase.[masterEndpoint.address,masterEndpoint.port,masterUsername]' \
--output text)
if [ -z "$host" ] || [ "$host" = "None" ]; then
echo "::error::could not resolve Lightsail endpoint for $DB_INSTANCE"; exit 1
fi
{ echo "LIK_UI_DB_HOST=$host"; echo "LIK_UI_DB_PORT=$port"; echo "LIK_UI_DB_USER=$user"; } >> "$GITHUB_ENV"
- name: Prune expired sessions
working-directory: lik-ui
run: uv run --frozen python scripts/prune_sessions.py
# This is the only alerting for an otherwise-invisible unattended job: a failed run (bad
# credentials, DB unreachable, a partial-failure non-zero exit) opens a tracking issue so
# a silent stop-pruning doesn't go unnoticed for weeks.
- name: Open a tracking issue on failure
if: failure()
env:
GH_TOKEN: ${{ github.token }}
run: |
run_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
gh issue create \
--title "prune-sessions cron failed" \
--body "The daily session-cleanup workflow failed: $run_url
Expired sessions were not pruned this run. Investigate before the next scheduled tick." \
|| echo "::warning::prune-sessions failed and the tracking issue could not be created"