1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Mutates {
8 Nothing,
10 Forge,
12}
13
14#[derive(Debug)]
16pub struct StepSpec {
17 pub name: &'static str,
20 pub chapter: &'static str,
22 pub mutates: Mutates,
24 pub proves: &'static str,
26 pub destructive: bool,
29 pub optional: bool,
32 pub prereqs: &'static [&'static str],
34}
35
36pub const STEPS: [StepSpec; 10] = [
40 StepSpec {
41 name: "package-check",
42 chapter: "§0",
43 mutates: Mutates::Nothing,
44 proves: "the package is publishable with no credentials",
45 destructive: false,
46 optional: false,
47 prereqs: &[],
48 },
49 StepSpec {
50 name: "default-branch",
51 chapter: "§1",
52 mutates: Mutates::Forge,
53 proves: "the trunk is the default branch",
54 destructive: false,
55 optional: false,
56 prereqs: &[],
57 },
58 StepSpec {
59 name: "single-trunk",
60 chapter: "§1",
61 mutates: Mutates::Forge,
62 proves: "no long-lived branch besides the trunk remains",
63 destructive: true,
64 optional: false,
65 prereqs: &["default-branch"],
66 },
67 StepSpec {
68 name: "ci-permissions",
69 chapter: "§2",
70 mutates: Mutates::Forge,
71 proves: "CI may write and open requests",
72 destructive: false,
73 optional: false,
74 prereqs: &[],
75 },
76 StepSpec {
77 name: "install-bot",
78 chapter: "§2",
79 mutates: Mutates::Forge,
80 proves: "the bot identity can act on this project",
81 destructive: false,
82 optional: false,
83 prereqs: &[],
84 },
85 StepSpec {
86 name: "bot-secrets",
87 chapter: "§2",
88 mutates: Mutates::Forge,
89 proves: "the bot credentials are stored on the project",
90 destructive: false,
91 optional: false,
92 prereqs: &[],
93 },
94 StepSpec {
95 name: "protect-trunk",
96 chapter: "§3",
97 mutates: Mutates::Forge,
98 proves: "the trunk takes no direct push, merges only by squash, and requires the named check",
99 destructive: false,
100 optional: false,
101 prereqs: &["default-branch"],
102 },
103 StepSpec {
104 name: "protect-tags",
105 chapter: "§3",
106 mutates: Mutates::Forge,
107 proves: "v* is protected as far as the forge allows",
108 destructive: false,
109 optional: false,
110 prereqs: &[],
111 },
112 StepSpec {
113 name: "protect-release-lines",
114 chapter: "§3",
115 mutates: Mutates::Forge,
116 proves: "release/* cannot be force-pushed or deleted",
117 destructive: false,
118 optional: true,
119 prereqs: &[],
120 },
121 StepSpec {
122 name: "protections-check",
123 chapter: "§3",
124 mutates: Mutates::Nothing,
125 proves: "exactly the owned protections, with those rules",
126 destructive: false,
127 optional: false,
128 prereqs: &[],
129 },
130];
131
132#[must_use]
134pub fn spec(name: &str) -> Option<&'static StepSpec> {
135 STEPS.iter().find(|step| step.name == name)
136}
137
138#[cfg(test)]
139mod tests {
140 use super::{STEPS, spec};
141
142 #[test]
145 fn every_prereq_is_an_earlier_step() {
146 for (idx, step) in STEPS.iter().enumerate() {
147 for prereq in step.prereqs {
148 let position = STEPS
149 .iter()
150 .position(|other| other.name == *prereq)
151 .unwrap_or(usize::MAX);
152 assert!(
153 position < idx,
154 "{}: prereq {prereq} is not an earlier step",
155 step.name
156 );
157 }
158 }
159 assert!(spec("package-check").is_some());
160 assert!(spec("no-such-step").is_none());
161 }
162}