| 1 | /** SQLite schema of a repository Durable Object. Applied idempotently on construction. */ |
| 2 | export const REPO_SCHEMA = ` |
| 3 | CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL); |
| 4 | CREATE TABLE IF NOT EXISTS refs (name TEXT PRIMARY KEY, oid TEXT NOT NULL); |
| 5 | CREATE TABLE IF NOT EXISTS objects ( |
| 6 | oid TEXT PRIMARY KEY, type INTEGER NOT NULL, size INTEGER NOT NULL, data BLOB, r2 INTEGER NOT NULL DEFAULT 0 |
| 7 | ); |
| 8 | CREATE TABLE IF NOT EXISTS counters (name TEXT PRIMARY KEY, value INTEGER NOT NULL); |
| 9 | -- Git LFS objects stored in R2 (repos/<id>/lfs/...), for the owner's storage quota (lib/quotas.ts). |
| 10 | CREATE TABLE IF NOT EXISTS lfs_objects (oid TEXT PRIMARY KEY, size INTEGER NOT NULL); |
| 11 | |
| 12 | CREATE TABLE IF NOT EXISTS issues ( |
| 13 | number INTEGER PRIMARY KEY, kind TEXT NOT NULL, title TEXT NOT NULL, body TEXT NOT NULL DEFAULT '', |
| 14 | state TEXT NOT NULL DEFAULT 'open', state_reason TEXT, author_id TEXT NOT NULL, author_login TEXT NOT NULL, |
| 15 | author_kind TEXT NOT NULL DEFAULT 'user', assignees TEXT NOT NULL DEFAULT '[]', labels TEXT NOT NULL DEFAULT '[]', |
| 16 | created_at TEXT NOT NULL, updated_at TEXT NOT NULL, closed_at TEXT, locked INTEGER NOT NULL DEFAULT 0 |
| 17 | ); |
| 18 | CREATE TABLE IF NOT EXISTS pulls ( |
| 19 | number INTEGER PRIMARY KEY, head_ref TEXT NOT NULL, head_sha TEXT NOT NULL, base_ref TEXT NOT NULL, |
| 20 | draft INTEGER NOT NULL DEFAULT 0, merged INTEGER NOT NULL DEFAULT 0, merged_at TEXT, merged_by TEXT, |
| 21 | merge_commit TEXT, requested_reviewers TEXT NOT NULL DEFAULT '[]', intent TEXT |
| 22 | ); |
| 23 | CREATE INDEX IF NOT EXISTS pulls_head ON pulls(head_ref); |
| 24 | -- The last mergeability check of an open pull request (repo/collab.ts), for the base and head it saw: |
| 25 | -- clean, conflicting, or in_base (its head is already part of the base branch). Today reads it (agentic/today-repo.ts). |
| 26 | CREATE TABLE IF NOT EXISTS pull_merge_state (number INTEGER PRIMARY KEY, base_sha TEXT NOT NULL, head_sha TEXT NOT NULL, state TEXT NOT NULL); |
| 27 | CREATE TABLE IF NOT EXISTS comments ( |
| 28 | id TEXT PRIMARY KEY, number INTEGER NOT NULL, author_id TEXT NOT NULL, author_login TEXT NOT NULL, |
| 29 | author_kind TEXT NOT NULL DEFAULT 'user', body TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, |
| 30 | review_id TEXT, path TEXT, line INTEGER, side TEXT, commit_id TEXT, in_reply_to TEXT, resolved INTEGER NOT NULL DEFAULT 0 |
| 31 | ); |
| 32 | CREATE INDEX IF NOT EXISTS comments_number ON comments(number); |
| 33 | CREATE TABLE IF NOT EXISTS reviews ( |
| 34 | id TEXT PRIMARY KEY, number INTEGER NOT NULL, author_id TEXT NOT NULL, author_login TEXT NOT NULL, |
| 35 | author_kind TEXT NOT NULL DEFAULT 'user', state TEXT NOT NULL, body TEXT NOT NULL DEFAULT '', commit_id TEXT NOT NULL, |
| 36 | submitted_at TEXT NOT NULL |
| 37 | ); |
| 38 | CREATE INDEX IF NOT EXISTS reviews_number ON reviews(number); |
| 39 | CREATE TABLE IF NOT EXISTS labels (name TEXT PRIMARY KEY, color TEXT NOT NULL, description TEXT NOT NULL DEFAULT ''); |
| 40 | CREATE TABLE IF NOT EXISTS protections (pattern TEXT PRIMARY KEY, rules TEXT NOT NULL); |
| 41 | CREATE TABLE IF NOT EXISTS secrets (name TEXT PRIMARY KEY, ciphertext TEXT NOT NULL, updated_at TEXT NOT NULL); |
| 42 | CREATE TABLE IF NOT EXISTS variables (name TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TEXT NOT NULL); |
| 43 | CREATE TABLE IF NOT EXISTS webhooks ( |
| 44 | id TEXT PRIMARY KEY, url TEXT NOT NULL, secret TEXT NOT NULL, events TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1, |
| 45 | created_at TEXT NOT NULL, last_status INTEGER, last_delivery_at TEXT |
| 46 | ); |
| 47 | CREATE TABLE IF NOT EXISTS checks ( |
| 48 | id TEXT PRIMARY KEY, sha TEXT NOT NULL, name TEXT NOT NULL, status TEXT NOT NULL, conclusion TEXT, |
| 49 | run_id TEXT, job_id TEXT, details_url TEXT, output TEXT, app TEXT NOT NULL DEFAULT 'diffinery-actions', |
| 50 | started_at TEXT, completed_at TEXT |
| 51 | ); |
| 52 | CREATE INDEX IF NOT EXISTS checks_sha ON checks(sha); |
| 53 | CREATE TABLE IF NOT EXISTS runs ( |
| 54 | id TEXT PRIMARY KEY, run_number INTEGER NOT NULL, workflow_path TEXT NOT NULL, workflow_name TEXT NOT NULL, |
| 55 | event TEXT NOT NULL, ref TEXT NOT NULL, sha TEXT NOT NULL, actor TEXT NOT NULL, status TEXT NOT NULL, |
| 56 | conclusion TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, pr_number INTEGER, attempt INTEGER NOT NULL DEFAULT 1, |
| 57 | event_payload TEXT NOT NULL, inputs TEXT NOT NULL DEFAULT '{}', concurrency_group TEXT, display_title TEXT NOT NULL DEFAULT '' |
| 58 | ); |
| 59 | CREATE INDEX IF NOT EXISTS runs_created ON runs(created_at); |
| 60 | CREATE TABLE IF NOT EXISTS jobs ( |
| 61 | id TEXT PRIMARY KEY, run_id TEXT NOT NULL, job_key TEXT NOT NULL, name TEXT NOT NULL, status TEXT NOT NULL, |
| 62 | conclusion TEXT, matrix TEXT NOT NULL DEFAULT '{}', strategy TEXT NOT NULL DEFAULT '{}', started_at TEXT, completed_at TEXT, |
| 63 | outputs TEXT NOT NULL DEFAULT '{}', steps TEXT NOT NULL DEFAULT '[]', failure TEXT, token_hash TEXT, summary TEXT, |
| 64 | container TEXT, timeout_minutes INTEGER NOT NULL DEFAULT 360, labels TEXT |
| 65 | ); |
| 66 | CREATE INDEX IF NOT EXISTS jobs_run ON jobs(run_id); |
| 67 | CREATE TABLE IF NOT EXISTS logs (job_id TEXT NOT NULL, seq INTEGER NOT NULL, t TEXT NOT NULL, step INTEGER NOT NULL, text TEXT NOT NULL, PRIMARY KEY (job_id, seq)); |
| 68 | CREATE TABLE IF NOT EXISTS caches (key TEXT PRIMARY KEY, size INTEGER NOT NULL, created_at TEXT NOT NULL, last_used TEXT NOT NULL); |
| 69 | -- Where older runners' artifacts were recorded before rt_artifacts; migrateRepoSchema moves them over. |
| 70 | CREATE TABLE IF NOT EXISTS artifacts (id TEXT PRIMARY KEY, run_id TEXT NOT NULL, name TEXT NOT NULL, size INTEGER NOT NULL, created_at TEXT NOT NULL, UNIQUE(run_id, name)); |
| 71 | CREATE TABLE IF NOT EXISTS merge_queue ( |
| 72 | number INTEGER PRIMARY KEY, base_ref TEXT NOT NULL, seq INTEGER NOT NULL, head_sha TEXT NOT NULL, method TEXT NOT NULL, |
| 73 | delete_branch INTEGER NOT NULL DEFAULT 0, enqueued_by TEXT NOT NULL, enqueued_at TEXT NOT NULL, |
| 74 | parent_sha TEXT, group_sha TEXT, group_ref TEXT, built_at TEXT |
| 75 | ); |
| 76 | CREATE TABLE IF NOT EXISTS sandboxes (id TEXT PRIMARY KEY, secret TEXT NOT NULL, created_by TEXT NOT NULL, ref TEXT NOT NULL, created_at TEXT NOT NULL, status TEXT NOT NULL); |
| 77 | |
| 78 | -- Actions runtime services (repo/runtime.ts): the cache and artifact services the |
| 79 | -- official toolkit talks to, with numeric ids like GitHub's. Content lives in R2. |
| 80 | CREATE TABLE IF NOT EXISTS rt_caches ( |
| 81 | id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL, version TEXT NOT NULL, scope TEXT NOT NULL, |
| 82 | size INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL, job_id TEXT, created_at TEXT NOT NULL, last_used TEXT NOT NULL, |
| 83 | UNIQUE(key, version, scope) |
| 84 | ); |
| 85 | CREATE INDEX IF NOT EXISTS rt_caches_used ON rt_caches(last_used); |
| 86 | CREATE TABLE IF NOT EXISTS rt_artifacts ( |
| 87 | id INTEGER PRIMARY KEY AUTOINCREMENT, run_id TEXT NOT NULL, job_id TEXT NOT NULL, name TEXT NOT NULL, |
| 88 | size INTEGER NOT NULL DEFAULT 0, digest TEXT, mime_type TEXT NOT NULL DEFAULT 'application/zip', status TEXT NOT NULL, |
| 89 | created_at TEXT NOT NULL, updated_at TEXT NOT NULL, expires_at TEXT NOT NULL |
| 90 | ); |
| 91 | CREATE INDEX IF NOT EXISTS rt_artifacts_run ON rt_artifacts(run_id, name); |
| 92 | -- A partial re-run's attempt lists the artifacts of the jobs it kept by their job (repo/runtime.ts runArtifacts). |
| 93 | CREATE INDEX IF NOT EXISTS rt_artifacts_job ON rt_artifacts(job_id); |
| 94 | -- What a job's GITHUB_TOKEN may do, from the workflow's permissions (repo/runtime.ts). |
| 95 | CREATE TABLE IF NOT EXISTS job_grants (job_id TEXT PRIMARY KEY, role TEXT NOT NULL, permissions TEXT NOT NULL); |
| 96 | -- GitHub-compatible extras (see ./releases.ts, ./statuses.ts, ./deployments.ts, ./social.ts). |
| 97 | CREATE TABLE IF NOT EXISTS releases ( |
| 98 | id INTEGER PRIMARY KEY AUTOINCREMENT, tag_name TEXT NOT NULL, target_commitish TEXT NOT NULL, name TEXT NOT NULL DEFAULT '', |
| 99 | body TEXT NOT NULL DEFAULT '', draft INTEGER NOT NULL DEFAULT 0, prerelease INTEGER NOT NULL DEFAULT 0, |
| 100 | author_id TEXT NOT NULL, author_login TEXT NOT NULL, author_kind TEXT NOT NULL DEFAULT 'user', |
| 101 | created_at TEXT NOT NULL, published_at TEXT, updated_at TEXT NOT NULL |
| 102 | ); |
| 103 | CREATE INDEX IF NOT EXISTS releases_tag ON releases(tag_name); |
| 104 | CREATE TABLE IF NOT EXISTS release_assets ( |
| 105 | id INTEGER PRIMARY KEY AUTOINCREMENT, release_id INTEGER NOT NULL, name TEXT NOT NULL, label TEXT NOT NULL DEFAULT '', |
| 106 | content_type TEXT NOT NULL, size INTEGER NOT NULL DEFAULT 0, state TEXT NOT NULL DEFAULT 'starter', download_count INTEGER NOT NULL DEFAULT 0, |
| 107 | uploader_id TEXT, uploader_login TEXT, uploader_kind TEXT, r2_key TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, |
| 108 | UNIQUE (release_id, name) |
| 109 | ); |
| 110 | CREATE TABLE IF NOT EXISTS statuses ( |
| 111 | id INTEGER PRIMARY KEY AUTOINCREMENT, sha TEXT NOT NULL, state TEXT NOT NULL, context TEXT NOT NULL, description TEXT, target_url TEXT, |
| 112 | creator_id TEXT NOT NULL, creator_login TEXT NOT NULL, creator_kind TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL |
| 113 | ); |
| 114 | CREATE INDEX IF NOT EXISTS statuses_sha ON statuses(sha, context); |
| 115 | CREATE TABLE IF NOT EXISTS deployments ( |
| 116 | id INTEGER PRIMARY KEY AUTOINCREMENT, sha TEXT NOT NULL, ref TEXT NOT NULL, task TEXT NOT NULL DEFAULT 'deploy', environment TEXT NOT NULL, |
| 117 | original_environment TEXT NOT NULL, description TEXT, payload TEXT NOT NULL DEFAULT '{}', transient INTEGER NOT NULL DEFAULT 0, |
| 118 | production INTEGER NOT NULL DEFAULT 0, creator_id TEXT NOT NULL, creator_login TEXT NOT NULL, creator_kind TEXT NOT NULL, |
| 119 | job_id TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL |
| 120 | ); |
| 121 | CREATE INDEX IF NOT EXISTS deployments_env ON deployments(environment); |
| 122 | CREATE TABLE IF NOT EXISTS deployment_statuses ( |
| 123 | id INTEGER PRIMARY KEY AUTOINCREMENT, deployment_id INTEGER NOT NULL, state TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', |
| 124 | environment TEXT NOT NULL, environment_url TEXT NOT NULL DEFAULT '', log_url TEXT NOT NULL DEFAULT '', |
| 125 | creator_id TEXT NOT NULL, creator_login TEXT NOT NULL, creator_kind TEXT NOT NULL, created_at TEXT NOT NULL |
| 126 | ); |
| 127 | CREATE INDEX IF NOT EXISTS deployment_statuses_dep ON deployment_statuses(deployment_id); |
| 128 | CREATE TABLE IF NOT EXISTS environments ( |
| 129 | id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE COLLATE NOCASE, branch_policy TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL |
| 130 | ); |
| 131 | -- Required reviewers' decisions on a run's environments (repo/deployments.ts): one approval per run and environment is enough. |
| 132 | CREATE TABLE IF NOT EXISTS deployment_reviews ( |
| 133 | id INTEGER PRIMARY KEY AUTOINCREMENT, run_id TEXT NOT NULL, environment TEXT NOT NULL COLLATE NOCASE, state TEXT NOT NULL, comment TEXT NOT NULL DEFAULT '', |
| 134 | user_id TEXT NOT NULL, user_login TEXT NOT NULL, created_at TEXT NOT NULL |
| 135 | ); |
| 136 | CREATE INDEX IF NOT EXISTS deployment_reviews_run ON deployment_reviews(run_id); |
| 137 | CREATE TABLE IF NOT EXISTS environment_secrets (environment TEXT NOT NULL COLLATE NOCASE, name TEXT NOT NULL, ciphertext TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (environment, name)); |
| 138 | CREATE TABLE IF NOT EXISTS environment_variables (environment TEXT NOT NULL COLLATE NOCASE, name TEXT NOT NULL, value TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (environment, name)); |
| 139 | CREATE TABLE IF NOT EXISTS reactions ( |
| 140 | id INTEGER PRIMARY KEY AUTOINCREMENT, subject TEXT NOT NULL, content TEXT NOT NULL, user_id TEXT NOT NULL, user_login TEXT NOT NULL, |
| 141 | user_kind TEXT NOT NULL, created_at TEXT NOT NULL, UNIQUE (subject, content, user_id) |
| 142 | ); |
| 143 | CREATE TABLE IF NOT EXISTS issue_events ( |
| 144 | id INTEGER PRIMARY KEY AUTOINCREMENT, number INTEGER NOT NULL, event TEXT NOT NULL, actor_id TEXT NOT NULL, actor_login TEXT NOT NULL, |
| 145 | actor_kind TEXT NOT NULL, detail TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL |
| 146 | ); |
| 147 | CREATE INDEX IF NOT EXISTS issue_events_number ON issue_events(number); |
| 148 | -- Who last moved each ref: tags made by agents never match environment tag patterns |
| 149 | -- (./deployments.ts), and runs of code an agent pushed act as the agent (./token-policy.ts). |
| 150 | CREATE TABLE IF NOT EXISTS ref_actors (ref TEXT PRIMARY KEY, actor_id TEXT NOT NULL, actor_kind TEXT NOT NULL, updated_at TEXT NOT NULL); |
| 151 | -- Commits an agent pushed that aren't on the default branch yet: a run of code that includes one acts |
| 152 | -- as an agent, whoever pushed last or made the tag (./token-policy.ts). Dropped once they land. |
| 153 | CREATE TABLE IF NOT EXISTS agent_commits (oid TEXT PRIMARY KEY, pushed_at TEXT NOT NULL); |
| 154 | -- GitHub imports and milestones (see ./imports.ts). |
| 155 | CREATE TABLE IF NOT EXISTS milestones ( |
| 156 | number INTEGER PRIMARY KEY, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', state TEXT NOT NULL DEFAULT 'open', |
| 157 | due_on TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, closed_at TEXT, creator TEXT |
| 158 | ); |
| 159 | CREATE TABLE IF NOT EXISTS issue_milestones (number INTEGER PRIMARY KEY, milestone INTEGER NOT NULL); |
| 160 | CREATE INDEX IF NOT EXISTS issue_milestones_milestone ON issue_milestones(milestone); |
| 161 | CREATE TABLE IF NOT EXISTS import_findings (blob TEXT NOT NULL, kind TEXT NOT NULL, at TEXT NOT NULL, PRIMARY KEY (blob, kind)); |
| 162 | -- Refs a running import wrote, with the oid it set, so a retry only moves refs it owns (never a ref people adopted). |
| 163 | CREATE TABLE IF NOT EXISTS import_refs (ref TEXT PRIMARY KEY, oid TEXT NOT NULL, import_id TEXT NOT NULL); |
| 164 | -- A copy of the owning organization's Actions variables (D1 org_variables), kept in sync by the Directory. |
| 165 | CREATE TABLE IF NOT EXISTS org_variables (name TEXT PRIMARY KEY, value TEXT NOT NULL); |
| 166 | -- Self-hosted runners registered with a registration token (repo/actions.ts registerRunner). A runner |
| 167 | -- claims jobs with its own token, never a person's; an ephemeral one's token ends with its first job. |
| 168 | CREATE TABLE IF NOT EXISTS runners (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, labels TEXT NOT NULL, token_hash TEXT, ephemeral INTEGER NOT NULL DEFAULT 0, created_by TEXT NOT NULL, created_at TEXT NOT NULL, last_seen_at TEXT); |
| 169 | CREATE TABLE IF NOT EXISTS runner_registrations (token_hash TEXT PRIMARY KEY, created_by TEXT NOT NULL, expires_at TEXT NOT NULL); |
| 170 | `; |
| 171 | |
| 172 | /** Columns added after a table was first created; added with ALTER TABLE when missing. */ |
| 173 | const ADDED_COLUMNS: Array<[table: string, column: string, definition: string]> = [ |
| 174 | // The commit a run reports its checks on: the PR head for pull_request runs, whose |
| 175 | // GITHUB_SHA is the test merge commit. Null means the run's own sha. |
| 176 | ["runs", "head_sha", "TEXT"], |
| 177 | // 1 when a webhook's secret is an encrypted envelope (repo/collab.ts); older rows hold it in plaintext until their next delivery. |
| 178 | ["webhooks", "secret_encrypted", "INTEGER NOT NULL DEFAULT 0"], |
| 179 | // 1 when the code a run executes came from an agent (repo/actions.ts agentOrigin): its jobs act as an agent. |
| 180 | ["runs", "agent_origin", "INTEGER NOT NULL DEFAULT 0"], |
| 181 | // The repository a pull request's head branch lives in, when it is not this one (a fork). |
| 182 | ["pulls", "head_repo_id", "TEXT"], |
| 183 | // Bytes an upload in progress has staged (counted against the budgets), and the |
| 184 | // committed object's etag (a later overwrite of the object is refused). |
| 185 | ["rt_caches", "staged", "INTEGER NOT NULL DEFAULT 0"], |
| 186 | ["rt_caches", "etag", "TEXT"], |
| 187 | ["rt_artifacts", "staged", "INTEGER NOT NULL DEFAULT 0"], |
| 188 | ["rt_artifacts", "etag", "TEXT"], |
| 189 | // 1 for an artifact an older runner uploaded through the runner API: a tarball at its legacy key (repo/runtime.ts). |
| 190 | ["rt_artifacts", "legacy", "INTEGER NOT NULL DEFAULT 0"], |
| 191 | // Who created a check run over the REST API (null for Diffinery Actions), and GitHub's numeric id. |
| 192 | ["checks", "creator_id", "TEXT"], |
| 193 | ["checks", "creator_login", "TEXT"], |
| 194 | ["checks", "creator_kind", "TEXT"], |
| 195 | ["checks", "seq", "INTEGER"], |
| 196 | // Per matrix leg: job-level continue-on-error, and the job-level concurrency group with its cancel-in-progress. |
| 197 | ["jobs", "continue_on_error", "INTEGER NOT NULL DEFAULT 0"], |
| 198 | ["jobs", "concurrency_group", "TEXT"], |
| 199 | ["jobs", "concurrency_cancel", "INTEGER NOT NULL DEFAULT 0"], |
| 200 | // Characters of log text stored so far (repo/actions.ts MAX_JOB_LOG). |
| 201 | ["jobs", "log_chars", "INTEGER NOT NULL DEFAULT 0"], |
| 202 | // 1 while a queued job waits for one of the repository's hosted runner slots (an account's share, lib/quotas.ts fleetLimits). |
| 203 | ["jobs", "fleet_held", "INTEGER NOT NULL DEFAULT 0"], |
| 204 | // Times the platform stopped the job's container before its runner started (repo/actions.ts QUICK_RETRY_SECONDS). |
| 205 | ["jobs", "early_stops", "INTEGER NOT NULL DEFAULT 0"], |
| 206 | // Runners on the previous image turned away during a rollout, and when the job starts again (repo/actions.ts STALE_IMAGE_TRIES). |
| 207 | ["jobs", "stale_tries", "INTEGER NOT NULL DEFAULT 0"], |
| 208 | ["jobs", "image_retry_at", "INTEGER"], |
| 209 | // The registered self-hosted runner that claimed the job (null for hosted jobs and personal-token claims). |
| 210 | ["jobs", "runner_id", "INTEGER"], |
| 211 | // Times a hosted job's container couldn't get a machine and the job went back in line (repo/actions.ts MAX_PLACEMENT_TRIES). |
| 212 | ["jobs", "placement_tries", "INTEGER NOT NULL DEFAULT 0"], |
| 213 | // What the runner reported when it fetched the job (JSON RunnerInfo: image, location, instance), for investigations. |
| 214 | ["jobs", "runner_info", "TEXT"], |
| 215 | // Times the platform replaced a hosted job's runner before its steps ran and the job started again (repo/actions.ts MAX_RUNNER_RESTARTS). |
| 216 | ["jobs", "restarts", "INTEGER NOT NULL DEFAULT 0"], |
| 217 | // Hosted runner size (runner/sizes.ts): the one runs-on asks for (null: chosen from the job's earlier runs), |
| 218 | // the log line saying which size the job got and why, and what the job used of its runner (JSON JobUsage). |
| 219 | ["jobs", "runner_size", "TEXT"], |
| 220 | ["jobs", "size_note", "TEXT"], |
| 221 | ["jobs", "usage", "TEXT"], |
| 222 | // When a running hosted job was cancelled and why, while its runner runs the steps that run after a cancel (repo/actions.ts CANCEL_STOP_MS). |
| 223 | ["jobs", "cancel_at", "INTEGER"], |
| 224 | ["jobs", "cancel_reason", "TEXT"], |
| 225 | // The GitHub import that wrote this row (null for everything else); a retry may only overwrite its own rows. |
| 226 | ["issues", "imported", "TEXT"], |
| 227 | ["pulls", "imported", "TEXT"], |
| 228 | ["comments", "imported", "TEXT"], |
| 229 | ["reviews", "imported", "TEXT"], |
| 230 | ["labels", "imported", "TEXT"], |
| 231 | ["milestones", "imported", "TEXT"], |
| 232 | ["protections", "imported", "TEXT"], |
| 233 | // Auto-merge while it is on (repo/auto-merge.ts AutoMergeState as JSON); null when off. |
| 234 | ["pulls", "auto_merge", "TEXT"], |
| 235 | // The base branch's commit when the pull request was merged (GitHub's base.sha for an imported one): |
| 236 | // a merged pull request's commits and files are counted from it (repo/collab.ts diffBase). |
| 237 | ["pulls", "merged_base", "TEXT"], |
| 238 | ["releases", "imported", "TEXT"], |
| 239 | // "task" for an agent task's sandbox (agentic/): no public sandbox call reaches it, and its git |
| 240 | // credential also needs the person who started the task (task_person) to still be allowed. |
| 241 | ["sandboxes", "kind", "TEXT NOT NULL DEFAULT 'api'"], |
| 242 | ["sandboxes", "task_person", "TEXT"], |
| 243 | // Environment protection rules (repo/deployments.ts): required reviewers as JSON [{type: "User", id, login}], |
| 244 | // a wait timer in minutes, and whether whoever started a run may approve it. |
| 245 | ["environments", "reviewers", "TEXT"], |
| 246 | ["environments", "wait_timer", "INTEGER NOT NULL DEFAULT 0"], |
| 247 | ["environments", "prevent_self_review", "INTEGER NOT NULL DEFAULT 0"], |
| 248 | // The job's `environment:` name, evaluated when it is launched; runnerSpec uses this, never a second evaluation. |
| 249 | ["jobs", "environment", "TEXT"], |
| 250 | // While a job waits on its environment's protection rules: when it started waiting, and "released" once they passed. |
| 251 | ["jobs", "gate_since", "TEXT"], |
| 252 | ["jobs", "gate_state", "TEXT"], |
| 253 | // Logins that started earlier attempts of a re-run (JSON list): prevent_self_review covers them too. |
| 254 | ["runs", "prior_actors", "TEXT"], |
| 255 | // What a partial re-run (failed jobs, or one job) keeps from the attempt it re-runs (JSON RerunCarry in |
| 256 | // repo/actions.ts): per job key, the earlier attempt's job rows copied into this one instead of running. |
| 257 | ["runs", "rerun_carry", "TEXT"], |
| 258 | // Commits a run's remote reusable workflow calls resolved to at plan time (JSON, "repoId@ref" → sha). |
| 259 | ["runs", "call_refs", "TEXT"], |
| 260 | // A job copied unchanged from an earlier attempt by a partial re-run: the job that actually ran, whose log it shows. |
| 261 | ["jobs", "carried_from", "TEXT"], |
| 262 | // Bytes an object takes as stored (compressed, in SQLite or R2); null in objects stored before it was kept. |
| 263 | ["objects", "stored", "INTEGER"], |
| 264 | // A comment hidden (minimized) by someone with write access: GitHub's reason (spam, abuse, off_topic, outdated, |
| 265 | // duplicate, resolved), who hid it and when (lib/moderation.ts). Null while it is shown. |
| 266 | ["comments", "minimized_reason", "TEXT"], |
| 267 | ["comments", "minimized_by", "TEXT"], |
| 268 | ["comments", "minimized_at", "TEXT"], |
| 269 | // When the job's current token was issued (repo/actions.ts jobTokenExpiry); a job not yet started counts from it. |
| 270 | ["jobs", "token_at", "TEXT"], |
| 271 | ]; |
| 272 | |
| 273 | export function migrateRepoSchema(sql: SqlStorage): void { |
| 274 | for (const [table, column, definition] of ADDED_COLUMNS) { |
| 275 | const has = sql.exec<{ n: number }>(`SELECT count(*) AS n FROM pragma_table_info('${table}') WHERE name = ?`, column).one().n > 0; |
| 276 | if (has) continue; |
| 277 | sql.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`); |
| 278 | // Job tokens issued before token_at was recorded count from this upgrade, so they still expire. |
| 279 | if (table === "jobs" && column === "token_at") sql.exec("UPDATE jobs SET token_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE token_hash IS NOT NULL"); |
| 280 | } |
| 281 | // Older runners' artifacts were first recorded in `artifacts`, which the REST API didn't list: they |
| 282 | // move to rt_artifacts, kept for 90 days from their upload. One shadowed by an artifact service |
| 283 | // upload of the same name was never shown and is dropped (its object goes with the bucket's age rule). |
| 284 | sql.exec( |
| 285 | `INSERT INTO rt_artifacts (run_id, job_id, name, size, mime_type, status, created_at, updated_at, expires_at, legacy) |
| 286 | SELECT a.run_id, '', a.name, a.size, 'application/gzip', 'ready', a.created_at, a.created_at, strftime('%Y-%m-%dT%H:%M:%fZ', a.created_at, '+90 days'), 1 FROM artifacts a |
| 287 | WHERE NOT EXISTS (SELECT 1 FROM rt_artifacts r WHERE r.run_id = a.run_id AND r.name = a.name)`, |
| 288 | ); |
| 289 | sql.exec("DELETE FROM artifacts"); |
| 290 | } |