github_actions_models/dependabot/
v2.rs

1//! "v2" Dependabot models.
2//!
3//! Resources:
4//! * [Configuration options for the `dependabot.yml` file](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file)
5//! * [JSON Schema for Dependabot v2](https://json.schemastore.org/dependabot-2.0.json)
6
7use indexmap::{IndexMap, IndexSet};
8use serde::Deserialize;
9
10/// A `dependabot.yml` configuration file.
11#[derive(Deserialize, Debug)]
12#[serde(rename_all = "kebab-case")]
13pub struct Dependabot {
14    /// Invariant: `2`
15    pub version: u64,
16    #[serde(default)]
17    pub enable_beta_ecosystems: bool,
18    #[serde(default)]
19    pub registries: IndexMap<String, Registry>,
20    pub updates: Vec<Update>,
21}
22
23/// Different registries known to Dependabot.
24#[derive(Deserialize, Debug)]
25#[serde(rename_all = "kebab-case", tag = "type")]
26pub enum Registry {
27    ComposerRepository {
28        url: String,
29        username: Option<String>,
30        password: Option<String>,
31    },
32    DockerRegistry {
33        url: String,
34        username: Option<String>,
35        password: Option<String>,
36        #[serde(default)]
37        replaces_base: bool,
38    },
39    Git {
40        url: String,
41        username: Option<String>,
42        password: Option<String>,
43    },
44    HexOrganization {
45        organization: String,
46        key: Option<String>,
47    },
48    HexRepository {
49        repo: Option<String>,
50        url: String,
51        auth_key: Option<String>,
52        public_key_fingerprint: Option<String>,
53    },
54    MavenRepository {
55        url: String,
56        username: Option<String>,
57        password: Option<String>,
58    },
59    NpmRegistry {
60        url: String,
61        username: Option<String>,
62        password: Option<String>,
63        #[serde(default)]
64        replaces_base: bool,
65    },
66    NugetFeed {
67        url: String,
68        username: Option<String>,
69        password: Option<String>,
70    },
71    PythonIndex {
72        url: String,
73        username: Option<String>,
74        password: Option<String>,
75        #[serde(default)]
76        replaces_base: bool,
77    },
78    RubygemsServer {
79        url: String,
80        username: Option<String>,
81        password: Option<String>,
82        #[serde(default)]
83        replaces_base: bool,
84    },
85    TerraformRegistry {
86        url: String,
87        token: Option<String>,
88    },
89}
90
91/// Cooldown settings for Dependabot updates.
92#[derive(Deserialize, Debug)]
93#[serde(rename_all = "kebab-case")]
94pub struct Cooldown {
95    pub default_days: Option<u64>,
96    pub semver_major_days: Option<u64>,
97    pub semver_minor_days: Option<u64>,
98    pub semver_patch_days: Option<u64>,
99    #[serde(default)]
100    pub include: Vec<String>,
101    #[serde(default)]
102    pub exclude: Vec<String>,
103}
104
105/// A `directory` or `directories` field in a Dependabot `update` directive.
106#[derive(Deserialize, Debug, PartialEq)]
107#[serde(rename_all = "kebab-case")]
108pub enum Directories {
109    Directory(String),
110    Directories(Vec<String>),
111}
112
113/// A single `update` directive.
114#[derive(Deserialize, Debug)]
115#[serde(rename_all = "kebab-case")]
116pub struct Update {
117    #[serde(default)]
118    pub allow: Vec<Allow>,
119    #[serde(default)]
120    pub assignees: IndexSet<String>,
121    pub commit_message: Option<CommitMessage>,
122    pub cooldown: Option<Cooldown>,
123    #[serde(flatten)]
124    pub directories: Directories,
125    #[serde(default)]
126    pub groups: IndexMap<String, Group>,
127    #[serde(default)]
128    pub ignore: Vec<Ignore>,
129    #[serde(default)]
130    pub insecure_external_code_execution: AllowDeny,
131    /// Labels to apply to this update group's pull requests.
132    ///
133    /// The default label is `dependencies`.
134    #[serde(default = "default_labels")]
135    pub labels: IndexSet<String>,
136    pub milestone: Option<u64>,
137    /// The maximum number of pull requests to open at a time from this
138    /// update group.
139    ///
140    /// The default maximum is 5.
141    #[serde(default = "default_open_pull_requests_limit")]
142    pub open_pull_requests_limit: u64,
143    pub package_ecosystem: PackageEcosystem,
144    // TODO: pull-request-branch-name
145    #[serde(default)]
146    pub rebase_strategy: RebaseStrategy,
147    #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
148    pub registries: Vec<String>,
149    #[serde(default)]
150    pub reviewers: IndexSet<String>,
151    pub schedule: Schedule,
152    pub target_branch: Option<String>,
153    #[serde(default)]
154    pub vendor: bool,
155    pub versioning_strategy: Option<VersioningStrategy>,
156}
157
158#[inline]
159fn default_labels() -> IndexSet<String> {
160    IndexSet::from(["dependencies".to_string()])
161}
162
163#[inline]
164fn default_open_pull_requests_limit() -> u64 {
165    // https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit
166    5
167}
168
169/// Allow rules for Dependabot updates.
170#[derive(Deserialize, Debug)]
171#[serde(rename_all = "kebab-case")]
172pub struct Allow {
173    pub dependency_name: Option<String>,
174    pub dependency_type: Option<DependencyType>,
175}
176
177/// Dependency types in `allow` rules.
178#[derive(Deserialize, Debug)]
179#[serde(rename_all = "kebab-case")]
180pub enum DependencyType {
181    Direct,
182    Indirect,
183    All,
184    Production,
185    Development,
186}
187
188/// Commit message settings for Dependabot updates.
189#[derive(Deserialize, Debug)]
190#[serde(rename_all = "kebab-case")]
191pub struct CommitMessage {
192    pub prefix: Option<String>,
193    pub prefix_development: Option<String>,
194    /// Invariant: `"scope"`
195    pub include: Option<String>,
196}
197
198/// Group settings for batched updates.
199#[derive(Deserialize, Debug)]
200#[serde(rename_all = "kebab-case")]
201pub struct Group {
202    /// This can only be [`DependencyType::Development`] or
203    /// [`DependencyType::Production`].
204    pub dependency_type: Option<DependencyType>,
205    #[serde(default)]
206    pub patterns: IndexSet<String>,
207    #[serde(default)]
208    pub exclude_patterns: IndexSet<String>,
209    #[serde(default)]
210    pub update_types: IndexSet<UpdateType>,
211}
212
213/// Update types for grouping.
214#[derive(Deserialize, Debug, Hash, Eq, PartialEq)]
215#[serde(rename_all = "kebab-case")]
216pub enum UpdateType {
217    Major,
218    Minor,
219    Patch,
220}
221
222/// Dependency ignore settings for updates.
223#[derive(Deserialize, Debug)]
224#[serde(rename_all = "kebab-case")]
225pub struct Ignore {
226    pub dependency_name: Option<String>,
227    /// These are, inexplicably, not [`UpdateType`] variants.
228    /// Instead, they're strings like `"version-update:semver-{major,minor,patch}"`.
229    #[serde(default)]
230    pub update_types: IndexSet<String>,
231    #[serde(default)]
232    pub versions: IndexSet<String>,
233}
234
235/// An "allow"/"deny" toggle.
236#[derive(Deserialize, Debug, Default)]
237#[serde(rename_all = "kebab-case")]
238pub enum AllowDeny {
239    Allow,
240    #[default]
241    Deny,
242}
243
244/// Supported packaging ecosystems.
245#[derive(Deserialize, Debug, PartialEq)]
246#[serde(rename_all = "kebab-case")]
247pub enum PackageEcosystem {
248    /// `bun`
249    Bun,
250    /// `bundler`
251    Bundler,
252    /// `cargo`
253    Cargo,
254    /// `composer`
255    Composer,
256    /// `conda`
257    Conda,
258    /// `devcontainers`
259    Devcontainers,
260    /// `docker`
261    Docker,
262    /// `docker-compose`
263    DockerCompose,
264    /// `dotnet-sdk`
265    DotnetSdk,
266    /// `helm`
267    Helm,
268    /// `elm`
269    Elm,
270    /// `gitsubmodule`
271    Gitsubmodule,
272    /// `github-actions`
273    GithubActions,
274    /// `gomod`
275    Gomod,
276    /// `gradle`
277    Gradle,
278    /// `maven`
279    Maven,
280    /// `mix`
281    Mix,
282    /// `npm`
283    Npm,
284    /// `nuget`
285    Nuget,
286    /// `pip`
287    Pip,
288    /// `pub`
289    Pub,
290    /// `rust-toolchain`
291    RustToolchain,
292    /// `swift`
293    Swift,
294    /// `terraform`
295    Terraform,
296    /// `uv`
297    Uv,
298    /// `vcpkg`
299    Vcpkg,
300}
301
302/// Rebase strategies for Dependabot updates.
303#[derive(Deserialize, Debug, Default, PartialEq)]
304#[serde(rename_all = "kebab-case")]
305pub enum RebaseStrategy {
306    #[default]
307    Auto,
308    Disabled,
309}
310
311/// Scheduling settings for Dependabot updates.
312#[derive(Deserialize, Debug)]
313#[serde(rename_all = "kebab-case")]
314pub struct Schedule {
315    pub interval: Interval,
316    pub day: Option<Day>,
317    pub time: Option<String>,
318    pub timezone: Option<String>,
319}
320
321/// Schedule intervals.
322#[derive(Deserialize, Debug, PartialEq)]
323#[serde(rename_all = "kebab-case")]
324pub enum Interval {
325    Daily,
326    Weekly,
327    Monthly,
328}
329
330/// Days of the week.
331#[derive(Deserialize, Debug, PartialEq)]
332#[serde(rename_all = "kebab-case")]
333pub enum Day {
334    Monday,
335    Tuesday,
336    Wednesday,
337    Thursday,
338    Friday,
339    Saturday,
340    Sunday,
341}
342
343/// Versioning strategies.
344#[derive(Deserialize, Debug, PartialEq)]
345#[serde(rename_all = "kebab-case")]
346pub enum VersioningStrategy {
347    Auto,
348    Increase,
349    IncreaseIfNecessary,
350    LockfileOnly,
351    Widen,
352}