action-config
Source: crates/lvcs-action/src/config.rs.
/// One `[[actions]]` entry of `lvcs.toml` as written (decision 245).
///
/// `name` is `[a-z0-9._-]+`, unique in the file, and names the channel
/// the action moves (decision 297). An action whose `on` is not `change` is
/// gated: it takes only frontiers every one of its `after` channels already
/// holds. `on` is its trigger: `change` (the default: it runs on every
/// merge into main and when main goes quiet, and moves its channel at each
/// frontier it passes with quorum); `{ channel = "<name>" }` (a gated
/// action: it runs when that channel, any action's or a `[channels]`
/// combination, moves to a new frontier, against that frontier); or
/// `manual` (a gated action that runs only on a run request of its own
/// channel, `lvcs run`). `after` is one channel or a list of them, each an
/// action's channel or a combination: for a channel trigger, that channel
/// (the default) or ones upstream of it along `after` links and
/// combination members; for a manual action, the change set (every change
/// action's channel but the generator's, the default, written out). A
/// trigger or `after` naming no known channel, and triggers, `after` links,
/// and combination members that form a cycle, are refused.
/// `paths` and `uses` are gitignore-syntax patterns (a check's were);
/// `paths` may be left out only by the generator. `run` is the argument
/// list of the action's one step (no shell); `steps` lists its steps
/// instead (decision 248), and exactly one of the two is given. `env` sets
/// variables for every step, each value a template (`${{ secrets.<NAME>
/// }}` in a gated action naming one of its `secrets`). `toolchain`
/// names values of the bounded `toolchain` vocabulary the action needs,
/// and a `node` step adds `node`. `writes` names the derived
/// files a change action generates: at most one action has it, and with no
/// `paths` its inputs are every synced file that isn't derived. `secrets`
/// names the credential variables a run copies from its runner's
/// environment, never their values; only a gated action has them, since
/// any merged code runs in a change action's sandbox (`secret_in_change`,
/// refused at parse and at approve). `quorum` raises the
/// attestations or outcomes the action needs above `QUORUM_DEFAULT`, and a
/// gated action's above `QUORUM_PROTECTED`. `timeout_secs` can only
/// lower the class bound (`CHECK_TIMEOUT_SECS` for a change action,
/// `GATED_TIMEOUT_SECS` for a gated action). Any other key is refused
/// at parse, and so is a list [`map`] refuses.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ActionEntry {
/// The action's name.
pub name: String,
/// What runs it; `change` by default.
#[serde(default)]
pub on: On,
/// The channels a gated action's frontier must already be held by;
/// the trigger channel, or the change set for a manual action, by
/// default.
#[serde(default)]
pub after: Option<After>,
/// The paths it covers, in gitignore syntax.
#[serde(default)]
pub paths: Option<Vec<String>>,
/// The further paths it reads, in gitignore syntax; empty by default.
#[serde(default)]
pub uses: Vec<String>,
/// Its one step's argument list, when `steps` is not given.
#[serde(default)]
pub run: Option<Vec<String>>,
/// Its steps, when `run` is not given (decision 248).
#[serde(default)]
pub steps: Vec<StepEntry>,
/// The variables every step runs with, each value a template.
#[serde(default)]
pub env: BTreeMap<String, String>,
/// The toolchains it needs, values of the `toolchain` vocabulary.
#[serde(default)]
pub toolchain: Vec<String>,
/// The derived paths it generates, in gitignore syntax; empty by
/// default.
#[serde(default)]
pub writes: Vec<String>,
/// The credential variable names a gated action's run receives;
/// empty by default.
#[serde(default)]
pub secrets: Vec<String>,
/// The attestations or outcomes it needs, when above its floor.
#[serde(default)]
pub quorum: Option<u32>,
/// Its wall clock and CPU limit, when below its class bound.
#[serde(default)]
pub timeout_secs: Option<u32>,
}
/// One `[[actions.steps]]` entry (decision 248): exactly one of `run` (an
/// argument list, no shell), `script` (text run by `shell`: `bash`, `sh`,
/// `python`, or a custom command line holding `{0}`; `bash -e {0}`, or `sh
/// -e {0}` without bash, when unset), and `node` (a directory of the
/// action's inputs holding `main`, and optionally `pre` and `post`, each
/// run with `node`, its inputs given by `with`).
///
/// `id` names it for `${{ steps.<id>.outputs.<name> }}`; `if` is
/// `success()` (the default), `always()`, `failure()`, `cancelled()`, or a
/// step output; `env` is set after the action's and earlier steps'
/// `GITHUB_ENV`; `working_directory` is relative to `/work`;
/// `continue_on_error` lets it fail without failing the run; and
/// `timeout_secs` can only lower what is left of the action's limit.
/// `script`, `env`, and `with` values are templates. Any other key is
/// refused at parse.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StepEntry {
/// Its id.
#[serde(default)]
pub id: Option<String>,
/// Its `if`.
#[serde(default, rename = "if")]
pub cond: Option<String>,
/// Its environment.
#[serde(default)]
pub env: BTreeMap<String, String>,
/// Its working directory, relative to `/work`.
#[serde(default)]
pub working_directory: Option<String>,
/// Whether its failure leaves the run passing.
#[serde(default)]
pub continue_on_error: bool,
/// Its own limit, in seconds.
#[serde(default)]
pub timeout_secs: Option<u32>,
/// An argument list.
#[serde(default)]
pub run: Option<Vec<String>>,
/// A script's text.
#[serde(default)]
pub script: Option<String>,
/// The script's shell.
#[serde(default)]
pub shell: Option<String>,
/// A node program's directory.
#[serde(default)]
pub node: Option<String>,
/// The node program, in `node`.
#[serde(default)]
pub main: Option<String>,
/// The node program run first, in `node`.
#[serde(default)]
pub pre: Option<String>,
/// The node program run last, in `node`.
#[serde(default)]
pub post: Option<String>,
/// The node program's inputs.
#[serde(default)]
pub with: BTreeMap<String, String>,
}
/// An `[[actions]]` entry's `on`: `"change"`, `"manual"`, or `{ channel =
/// "<name>" }`.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum On {
/// It runs on every merge into main and when main goes quiet.
#[default]
Change,
/// It runs when the named channel moves to a new frontier.
Channel(String),
/// It runs on a run request of its own channel.
Manual,
}
/// An `[[actions]]` entry's `after` as written: one channel name, or a
/// list of them. `after = "tests"` and `after = ["tests"]` map to one
/// value (decision 297).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum After {
/// One channel.
One(String),
/// A list of channels.
List(Vec<String>),
}
/// `[runner]` (decisions 161 and 245): how long main's visible frontier
/// must be unchanged before a runner serving change channels prepares a
/// job for it.
///
/// `quiet_ms` can only raise `QUIET_MS`, up to `QUIET_MAX_MS`: a smaller
/// value counts as `QUIET_MS` and a larger one as `QUIET_MAX_MS`. It
/// defaults to `QUIET_MS`. Any other key is refused at parse.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct RunnerTable {
/// The quiet period asked for, in milliseconds.
pub quiet_ms: u64,
}