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
// Copyright 2020-2022 Peter Williams <peter@newton.cx> and collaborators
// Licensed under the MIT License.
//! The Cranko configuration file.
//!
//! Given the same input repository, Cranko should give reproducible results no
//! matter who’s running it. So we really want all configuration to be at the
//! per-repository level.
use anyhow::Context;
use std::{collections::HashMap, fs::File, io::Read, path::Path};
use crate::{
atry,
errors::{Error, Result},
};
/// The configuration file structures as explicitly serialized into the TOML
/// format.
mod syntax {
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// The toplevel (per-repo) configuration structure.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SerializedConfiguration {
/// General per-repository configuration.
pub repo: RepoConfiguration,
/// NPM integration configuration.
#[serde(default)]
pub npm: NpmConfiguration,
/// Centralized per-project configuration.
#[serde(default)]
pub projects: HashMap<String, ProjectConfiguration>,
}
/// Configuration relating to the backing repository. This is applied
/// directly to the runtime Repository instance.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct RepoConfiguration {
/// Git URLs that the upstream remote might be using.
pub upstream_urls: Vec<String>,
/// The name of the `rc`-like branch.
pub rc_name: Option<String>,
/// The name of the `release`-like branch.
pub release_name: Option<String>,
/// The format for release tag names.
pub release_tag_name_format: Option<String>,
}
/// Configuration related to the NPM integration.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct NpmConfiguration {
/// A custom "resolution protocol" to use for internal dependencies; if
/// using Yarn workspaces, `"workspace"` may be useful here.
pub internal_dep_protocol: Option<String>,
}
/// Configuration relating to individual projects.
///
/// Whenever possible, this configuration should be specified in per-project
/// metadata files to preserve locality. But some pieces of configuration
/// need to be centralized.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ProjectConfiguration {
/// Ignore this project if/when it is automatically detected.
pub ignore: bool,
}
}
// The rest of this module normalizes the on-disk format into forms more useful
// at runtime.
pub use syntax::{NpmConfiguration, ProjectConfiguration, RepoConfiguration};
#[derive(Clone, Debug)]
pub struct ConfigurationFile {
pub repo: RepoConfiguration,
pub npm: NpmConfiguration,
pub projects: HashMap<String, ProjectConfiguration>,
}
impl Default for ConfigurationFile {
fn default() -> Self {
let repo = RepoConfiguration::default();
let npm = Default::default();
let projects = Default::default();
ConfigurationFile {
repo,
npm,
projects,
}
}
}
impl ConfigurationFile {
pub fn get<P: AsRef<Path>>(path: P) -> Result<Self> {
let mut f = match File::open(&path) {
Ok(f) => f,
Err(e) => {
return if e.kind() == std::io::ErrorKind::NotFound {
Ok(Self::default())
} else {
Err(Error::new(e).context(format!(
"failed to open config file `{}`",
path.as_ref().display()
)))
}
}
};
let mut text = String::new();
f.read_to_string(&mut text)
.with_context(|| format!("failed to read config file `{}`", path.as_ref().display()))?;
let sercfg: syntax::SerializedConfiguration = toml::from_str(&text).with_context(|| {
format!(
"could not parse config file `{}` as TOML",
path.as_ref().display()
)
})?;
Ok(ConfigurationFile {
repo: sercfg.repo,
npm: sercfg.npm,
projects: sercfg.projects,
})
}
pub fn into_toml(self) -> Result<String> {
let syn_cfg = syntax::SerializedConfiguration {
repo: self.repo,
npm: self.npm,
projects: self.projects,
};
Ok(atry!(
toml::to_string_pretty(&syn_cfg);
["could not serialize configuration into TOML format"]
))
}
}