MySQL · 3821 bytes Raw Blame History
1 -- SPDX-License-Identifier: AGPL-3.0-or-later
2 --
3 -- S41a workflow steps — one row per `steps[]` entry in a job.
4 --
5 -- Steps execute serially within a job. status moves queued → running →
6 -- completed | cancelled | skipped (skipped fires when an earlier
7 -- step fails and continue-on-error wasn't set).
8 --
9 -- The `run` text is the shell command. The parser (S41a) tags every
10 -- expression value carried into this string with a Tainted flag; the
11 -- runner's exec layer (S41d) refuses to interpolate Tainted values
12 -- into shell strings — they compile to ${SHITHUB_INPUT_*} envvar refs
13 -- set safely by the runner. We don't need a separate "compiled"
14 -- column; the render step happens in-runner each execution.
15 --
16 -- log_object_key holds the Spaces blob key after the finalize worker
17 -- (S41d) concatenates and uploads chunks. NULL while running; chunks
18 -- live in workflow_step_log_chunks (0047) until finalize.
19 --
20 -- step_index gives the dispatch order (0-based). step_id (text) is
21 -- the optional GHA `id:` for cross-step references via
22 -- ${{ steps.<id>.outputs.X }} (outputs are v2; the column exists now
23 -- so the parser can carry the id without a schema change).
24
25 -- +goose Up
26
27 CREATE TYPE workflow_step_status AS ENUM (
28 'queued', 'running', 'completed', 'cancelled', 'skipped'
29 );
30
31 CREATE TABLE workflow_steps (
32 id bigserial PRIMARY KEY,
33 job_id bigint NOT NULL REFERENCES workflow_jobs(id) ON DELETE CASCADE,
34 step_index integer NOT NULL,
35 step_id text NOT NULL DEFAULT '',
36 step_name text NOT NULL DEFAULT '',
37 if_expr text NOT NULL DEFAULT '',
38 run_command text NOT NULL DEFAULT '',
39 uses_alias text NOT NULL DEFAULT '',
40 working_directory text NOT NULL DEFAULT '',
41 step_env jsonb NOT NULL DEFAULT '{}'::jsonb,
42 continue_on_error boolean NOT NULL DEFAULT false,
43 status workflow_step_status NOT NULL DEFAULT 'queued',
44 conclusion check_conclusion,
45 log_object_key text,
46 log_byte_count bigint NOT NULL DEFAULT 0,
47 started_at timestamptz,
48 completed_at timestamptz,
49 version integer NOT NULL DEFAULT 0,
50 created_at timestamptz NOT NULL DEFAULT now(),
51 updated_at timestamptz NOT NULL DEFAULT now(),
52
53 UNIQUE (job_id, step_index),
54
55 CONSTRAINT workflow_steps_step_id_format CHECK (
56 step_id = '' OR step_id ~ '^[A-Za-z_][A-Za-z0-9_-]*$'
57 ),
58 CONSTRAINT workflow_steps_name_length CHECK (char_length(step_name) <= 256),
59 CONSTRAINT workflow_steps_run_or_uses CHECK (
60 (run_command <> '' AND uses_alias = '') OR
61 (run_command = '' AND uses_alias <> '')
62 ),
63 CONSTRAINT workflow_steps_uses_alias_known CHECK (
64 uses_alias IN ('', 'actions/checkout@v4',
65 'shithub/upload-artifact@v1',
66 'shithub/download-artifact@v1')
67 ),
68 CONSTRAINT workflow_steps_working_directory_length CHECK (
69 char_length(working_directory) <= 1024
70 ),
71 CONSTRAINT workflow_steps_completed_has_conclusion CHECK (
72 status NOT IN ('completed', 'skipped') OR conclusion IS NOT NULL
73 ),
74 CONSTRAINT workflow_steps_log_byte_nonnegative CHECK (log_byte_count >= 0)
75 );
76
77 CREATE INDEX workflow_steps_job_idx ON workflow_steps (job_id);
78
79 CREATE TRIGGER set_updated_at BEFORE UPDATE ON workflow_steps
80 FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at();
81
82
83 -- +goose Down
84 DROP TABLE IF EXISTS workflow_steps;
85 DROP TYPE IF EXISTS workflow_step_status;
86