github_actions_models/action.rs
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
//! Data models for GitHub Actions action definitions.
//!
//! Resources:
//! * [Metadata syntax for GitHub Actions]
//! * [JSON Schema definition for GitHub Actions]
//!
//! [Metadata syntax for GitHub Actions]: https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions
//! [JSON Schema definition for GitHub Actions]: https://json.schemastore.org/github-action.json
use indexmap::IndexMap;
use serde::Deserialize;
use crate::common::{
expr::{BoE, LoE},
Env, If,
};
/// A GitHub Actions action definition.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Action {
pub name: String,
pub author: Option<String>,
pub description: Option<String>,
#[serde(default)]
pub inputs: IndexMap<String, Input>,
#[serde(default)]
pub outputs: IndexMap<String, Output>,
pub runs: Runs,
}
/// An action input.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Input {
pub description: String,
pub required: Option<bool>,
pub default: Option<String>,
}
/// An action output.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Output {
pub description: String,
// NOTE: not optional for composite actions, but this is not worth modeling.
pub value: Option<String>,
}
/// An action `runs` definition.
///
/// A `runs` definition can be either a JavaScript action, a "composite" action
/// (made up of several constituent actions), or a Docker action.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case", untagged)]
pub enum Runs {
JavaScript(JavaScript),
Composite(Composite),
Docker(Docker),
}
/// A `runs` definition for a JavaScript action.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct JavaScript {
/// The Node runtime to use for this action. This is one of:
///
/// `"node12" | "node16" | "node20"`
pub using: String,
/// The action's entrypoint, as a JavaScript file.
pub main: String,
/// An optional script to run, before [`JavaScript::main`].
pub pre: Option<String>,
/// An optional expression that triggers [`JavaScript::pre`] if it evaluates to `true`.
///
/// If not present, defaults to `always()`
pub pre_if: Option<If>,
/// An optional script to run, after [`JavaScript::main`].
pub post: Option<String>,
/// An optional expression that triggers [`JavaScript::post`] if it evaluates to `true`.
///
/// If not present, defaults to `always()`
pub post_if: Option<If>,
}
/// A `runs` definition for a composite action.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Composite {
/// Invariant: `"composite"`
pub using: String,
/// The individual steps that make up this composite action.
pub steps: Vec<Step>,
}
/// An individual composite action step.
///
/// This is similar, but not identical to [`crate::workflow::job::Step`].
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Step {
/// An optional ID for this composite step.
pub id: Option<String>,
/// An optional expression that prevents this composite step from running unless it evaluates to `true`.
pub r#if: Option<If>,
/// An optional name for this composite step.
pub name: Option<String>,
/// An optional boolean or expression that, if `true`, prevents the job from failing when
/// this composite step fails.
#[serde(default)]
pub continue_on_error: BoE,
/// The `run:` or `uses:` body for this composite step.
#[serde(flatten)]
pub body: StepBody,
}
/// The body of a composite action step.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case", untagged)]
pub enum StepBody {
/// A step that uses another GitHub Action.
Uses {
/// The GitHub Action being used.
uses: String,
/// Any inputs to the action being used.
#[serde(default)]
with: Env,
},
/// A step that runs a command in a shell.
Run {
/// The command to run.
run: String,
/// The shell to run in.
shell: String,
/// An optional environment mapping for this step.
#[serde(default)]
env: LoE<Env>,
/// An optional working directory to run [`RunShell::run`] from.
working_directory: Option<String>,
},
}
/// A `runs` definition for a Docker action.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Docker {
/// Invariant: `"docker"`
pub using: String,
/// The Docker image to use.
pub image: String,
/// An optional environment mapping for this step.
#[serde(default)]
pub env: Env,
/// An optional Docker entrypoint, potentially overriding the image's
/// default entrypoint.
pub entrypoint: Option<String>,
/// An optional "pre" entrypoint to run, before [`Docker::entrypoint`].
pub pre_entrypoint: Option<String>,
/// An optional expression that triggers [`Docker::pre_entrypoint`] if it evaluates to `true`.
///
/// If not present, defaults to `always()`
pub pre_if: Option<If>,
/// An optional "post" entrypoint to run, after [`Docker::entrypoint`] or the default
/// entrypoint.
pub post_entrypoint: Option<String>,
/// An optional expression that triggers [`Docker::post_entrypoint`] if it evaluates to `true`.
///
/// If not present, defaults to `always()`
pub post_if: Option<If>,
}