1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Mutates {
8 Nothing,
10 Forge,
12 Local,
14}
15
16#[derive(Debug)]
18pub struct StepSpec {
19 pub name: &'static str,
22 pub chapter: &'static str,
24 pub mutates: Mutates,
26 pub proves: &'static str,
28 pub destructive: bool,
31 pub optional: bool,
34 pub prereqs: &'static [&'static str],
36}
37
38pub const STEPS: [StepSpec; 14] = [
46 StepSpec {
47 name: "package-check",
48 chapter: "§0",
49 mutates: Mutates::Nothing,
50 proves: "the package is publishable with no credentials",
51 destructive: false,
52 optional: false,
53 prereqs: &[],
54 },
55 StepSpec {
56 name: "default-branch",
57 chapter: "§1",
58 mutates: Mutates::Forge,
59 proves: "the trunk is the default branch",
60 destructive: false,
61 optional: false,
62 prereqs: &[],
63 },
64 StepSpec {
65 name: "single-trunk",
66 chapter: "§1",
67 mutates: Mutates::Forge,
68 proves: "no long-lived branch besides the trunk remains",
69 destructive: true,
70 optional: false,
71 prereqs: &["default-branch"],
72 },
73 StepSpec {
74 name: "merge-cleanup",
75 chapter: "§1",
76 mutates: Mutates::Forge,
77 proves: "the forge deletes a branch when its merge lands",
78 destructive: false,
79 optional: false,
80 prereqs: &["default-branch"],
81 },
82 StepSpec {
83 name: "branch-reminder",
84 chapter: "§1",
85 mutates: Mutates::Local,
86 proves: "a pull reminds the operator when a merged branch lingers locally",
87 destructive: false,
88 optional: false,
89 prereqs: &[],
90 },
91 StepSpec {
92 name: "ci-permissions",
93 chapter: "§2",
94 mutates: Mutates::Forge,
95 proves: "CI may write and open requests",
96 destructive: false,
97 optional: false,
98 prereqs: &[],
99 },
100 StepSpec {
101 name: "install-bot",
102 chapter: "§2",
103 mutates: Mutates::Forge,
104 proves: "the bot identity can act on this project",
105 destructive: false,
106 optional: false,
107 prereqs: &[],
108 },
109 StepSpec {
110 name: "bot-secrets",
111 chapter: "§2",
112 mutates: Mutates::Forge,
113 proves: "the bot credentials are stored on the project",
114 destructive: false,
115 optional: false,
116 prereqs: &[],
117 },
118 StepSpec {
119 name: "forge-version",
120 chapter: "§3",
121 mutates: Mutates::Nothing,
122 proves: "the forge meets the convention's minimum version",
123 destructive: false,
124 optional: false,
125 prereqs: &[],
126 },
127 StepSpec {
128 name: "protect-trunk",
129 chapter: "§3",
130 mutates: Mutates::Forge,
131 proves: "the trunk takes no direct push, merges only by squash with the request's title and body as the message, and requires the named check",
132 destructive: false,
133 optional: false,
134 prereqs: &["default-branch", "forge-version"],
135 },
136 StepSpec {
137 name: "protect-tags",
138 chapter: "§3",
139 mutates: Mutates::Forge,
140 proves: "v* is protected as far as the forge allows",
141 destructive: false,
142 optional: false,
143 prereqs: &[],
144 },
145 StepSpec {
146 name: "protect-release-lines",
147 chapter: "§3",
148 mutates: Mutates::Forge,
149 proves: "release/* cannot be force-pushed or deleted",
150 destructive: false,
151 optional: true,
152 prereqs: &[],
153 },
154 StepSpec {
155 name: "auto-merge",
156 chapter: "§3",
157 mutates: Mutates::Forge,
158 proves: "a request may merge itself once its required checks pass",
159 destructive: false,
160 optional: false,
161 prereqs: &["default-branch"],
162 },
163 StepSpec {
164 name: "protections-check",
165 chapter: "§3",
166 mutates: Mutates::Nothing,
167 proves: "exactly the owned protections, with those rules",
168 destructive: false,
169 optional: false,
170 prereqs: &[],
171 },
172];
173
174#[must_use]
176pub fn spec(name: &str) -> Option<&'static StepSpec> {
177 STEPS.iter().find(|step| step.name == name)
178}
179
180#[cfg(test)]
181mod tests {
182 use super::{STEPS, spec};
183
184 #[test]
187 fn every_prereq_is_an_earlier_step() {
188 for (idx, step) in STEPS.iter().enumerate() {
189 for prereq in step.prereqs {
190 let position = STEPS
191 .iter()
192 .position(|other| other.name == *prereq)
193 .unwrap_or(usize::MAX);
194 assert!(
195 position < idx,
196 "{}: prereq {prereq} is not an earlier step",
197 step.name
198 );
199 }
200 }
201 assert!(spec("package-check").is_some());
202 assert!(spec("no-such-step").is_none());
203 }
204}