Skip to content

Commit 7c80b74

Browse files
authored
Merge pull request #22 from GuillaumeGomez/rename-from
Add new rename-from field to ensure checks when source URL file differs from `name`
2 parents 5e2683e + b08da67 commit 7c80b74

File tree

4 files changed

+130
-24
lines changed

4 files changed

+130
-24
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ the TOML files in the `files/` directory. Each entry has the following schema:
3131
artifacts built from open source code you should put the license identifier,
3232
for everything else you should put a link to the licensing terms.
3333

34+
* **`rename-from`**: in case the `source` file has a different name than `name`,
35+
you need to add this field to explicitly mark that this is expected with the
36+
file name from `source`.
37+
3438
You can add a new entry either by manually modifying a TOML file in the `files` directory,
3539
or by using the following command:
3640

files/gha-self-hosted.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ name = "gha-self-hosted/qemu-efi-aarch64_2022.11-6.deb"
33
source = "http://ftp.debian.org/debian/pool/main/e/edk2/qemu-efi-aarch64_2022.11-6+deb12u2_all.deb"
44
sha256 = "9a55c7b94fdf13a28928359a77d42e5364aa3ae2e558bd1fd5361955bf479d81"
55
license = "BSD"
6+
rename-from = "qemu-efi-aarch64_2022.11-6+deb12u2_all.deb"

src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,21 @@ async fn add_file(args: AddFileArgs) -> anyhow::Result<()> {
167167
.append(true)
168168
.open(&args.toml_file)?;
169169

170+
let rename_from = if let Some(file_name) = args.url.path().split('/').last()
171+
&& let Some(path_name) = args.path.split('/').last()
172+
&& file_name != path_name
173+
{
174+
Some(file_name.to_string())
175+
} else {
176+
None
177+
};
178+
170179
let entry = ManifestFileManaged::new(
171180
args.path,
172181
to_hex(&hash),
173182
args.url,
174183
args.license.unwrap_or(String::new()),
184+
rename_from,
175185
);
176186
let entry = toml::to_string(&entry)?;
177187

src/manifest.rs

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,44 +44,118 @@ struct LocationCache {
4444
pub(crate) fn load_manifests(load_from: &Path) -> Result<(Vec<MirrorFile>, Vec<String>), Error> {
4545
let mut result = Vec::new();
4646
let mut cache = LocationCache::default();
47+
let mut errors = Vec::new();
48+
49+
fn emit_error(
50+
error: String,
51+
mirror_file: &MirrorFile,
52+
file_source: &str,
53+
cache: &LocationCache,
54+
errors: &mut Vec<String>,
55+
) {
56+
let location = cache
57+
.seen_paths
58+
.get(&mirror_file.name)
59+
.unwrap()
60+
.first()
61+
.unwrap();
62+
let (src_line, snippet) = span_info(&file_source, location);
63+
errors.push(format!(
64+
"{error}:\n\
65+
# {} (line {src_line})\n{snippet}\n",
66+
location.file.display()
67+
));
68+
}
4769

4870
fn load_inner(
4971
load_from: &Path,
5072
result: &mut Vec<MirrorFile>,
5173
cache: &mut LocationCache,
74+
errors: &mut Vec<String>,
5275
) -> anyhow::Result<()> {
5376
for entry in load_from.read_dir()? {
5477
let path = entry?.path();
5578
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("toml") {
56-
let manifest = std::fs::read_to_string(&path)
79+
let file_source = std::fs::read_to_string(&path)
80+
.map_err(Error::from)
81+
.with_context(|| format!("failed to read {}", path.display()))?;
82+
let manifest = toml::from_str::<Manifest>(&file_source)
5783
.map_err(Error::from)
58-
.and_then(|raw| toml::from_str::<Manifest>(&raw).map_err(Error::from))
5984
.with_context(|| format!("failed to read {}", path.display()))?;
6085
record_locations(&path, &manifest, cache);
6186

6287
for file in manifest.files {
63-
result.push(match file.into_inner() {
88+
let mirror_file = match file.into_inner() {
6489
ManifestFile::Legacy(legacy) => MirrorFile {
6590
name: legacy.name,
6691
sha256: legacy.sha256,
6792
source: Source::Legacy,
93+
rename_from: None,
6894
},
6995
ManifestFile::Managed(managed) => MirrorFile {
7096
name: managed.name,
7197
sha256: managed.sha256,
7298
source: Source::Url(managed.source),
99+
rename_from: managed.rename_from,
73100
},
74-
});
101+
};
102+
if let Source::Url(ref source) = mirror_file.source {
103+
if let Some(file_name) = source.path().split('/').last()
104+
&& let Some(path_name) = mirror_file.name.split('/').last()
105+
{
106+
match mirror_file.rename_from {
107+
Some(ref rename_from) => {
108+
if path_name == file_name {
109+
emit_error(
110+
format!(
111+
"`rename-from` field isn't needed since `source` and `name` field have the same file name (`{file_name}`)"
112+
),
113+
&mirror_file,
114+
&file_source,
115+
cache,
116+
errors,
117+
);
118+
} else if rename_from != file_name {
119+
emit_error(
120+
format!(
121+
"`rename-from` field value doesn't match name from the URL `{source}` (`{file_name}` != `{rename_from}`)"
122+
),
123+
&mirror_file,
124+
&file_source,
125+
cache,
126+
errors,
127+
);
128+
}
129+
}
130+
None => {
131+
if path_name != file_name {
132+
emit_error(
133+
format!(
134+
"The name from the URL `{source}` doesn't match the `name` field (`{file_name}` != `{path_name}`). \
135+
Add `rename-from = {file_name:?}` to fix this error"
136+
),
137+
&mirror_file,
138+
&file_source,
139+
cache,
140+
errors,
141+
);
142+
}
143+
}
144+
}
145+
}
146+
}
147+
result.push(mirror_file);
75148
}
76149
} else if path.is_dir() {
77-
load_inner(&path, result, cache)?;
150+
load_inner(&path, result, cache, errors)?;
78151
}
79152
}
80153
Ok(())
81154
}
82155

83-
load_inner(load_from, &mut result, &mut cache)?;
84-
Ok((result, find_errors(cache)))
156+
load_inner(load_from, &mut result, &mut cache, &mut errors)?;
157+
find_errors(cache, &mut errors);
158+
Ok((result, errors))
85159
}
86160

87161
fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationCache) {
@@ -114,12 +188,32 @@ fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationC
114188
.or_default()
115189
.insert(location.clone());
116190
if let Some(url) = url {
117-
cache.seen_urls.entry(url).or_default().insert(location);
191+
cache
192+
.seen_urls
193+
.entry(url)
194+
.or_default()
195+
.insert(location.clone());
118196
}
119197
}
120198
}
121199

122-
fn find_errors(cache: LocationCache) -> Vec<String> {
200+
fn span_info<'a>(content: &'a str, location: &Location) -> (usize, &'a str) {
201+
// Find the corresponding line number
202+
let mut accumulated_chars = 0;
203+
let mut src_line = 0;
204+
for (index, line) in content.lines().enumerate() {
205+
accumulated_chars += line.len() + 1; // +1 for newline
206+
if accumulated_chars > location.span.0.start {
207+
src_line = index + 1;
208+
break;
209+
}
210+
}
211+
212+
let snippet = &content[location.span.0.start..location.span.0.end];
213+
(src_line, snippet)
214+
}
215+
216+
fn find_errors(cache: LocationCache, errors: &mut Vec<String>) {
123217
let mut file_cache: HashMap<PathBuf, String> = HashMap::new();
124218

125219
fn format_locations(
@@ -136,18 +230,7 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
136230
})
137231
});
138232

139-
// Find the corresponding line number
140-
let mut accumulated_chars = 0;
141-
let mut src_line = 0;
142-
for (index, line) in content.lines().enumerate() {
143-
accumulated_chars += line.len() + 1; // +1 for newline
144-
if accumulated_chars > location.span.0.start {
145-
src_line = index + 1;
146-
break;
147-
}
148-
}
149-
150-
let snippet = &content[location.span.0.start..location.span.0.end];
233+
let (src_line, snippet) = span_info(&content, location);
151234
writeln!(
152235
output,
153236
"# {} (line {src_line})\n{snippet}\n",
@@ -159,7 +242,6 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
159242
output
160243
}
161244

162-
let mut errors = Vec::new();
163245
for (path, locations) in cache.seen_paths {
164246
if locations.len() > 1 {
165247
errors.push(format!(
@@ -184,13 +266,13 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
184266
));
185267
}
186268
}
187-
errors
188269
}
189270

190271
pub(crate) struct MirrorFile {
191272
pub(crate) name: String,
192273
pub(crate) sha256: String,
193274
pub(crate) source: Source,
275+
pub(crate) rename_from: Option<String>,
194276
}
195277

196278
pub(crate) enum Source {
@@ -233,15 +315,24 @@ pub struct ManifestFileManaged {
233315
// This field is not considered at all by the automation, we just enforce its presence so that
234316
// people adding new entries think about the licensing implications.
235317
license: String,
318+
#[serde(default, rename = "rename-from")]
319+
rename_from: Option<String>,
236320
}
237321

238322
impl ManifestFileManaged {
239-
pub fn new(name: String, sha256: String, source: Url, license: String) -> Self {
323+
pub fn new(
324+
name: String,
325+
sha256: String,
326+
source: Url,
327+
license: String,
328+
rename_from: Option<String>,
329+
) -> Self {
240330
Self {
241331
name,
242332
sha256,
243333
source,
244334
license,
335+
rename_from,
245336
}
246337
}
247338
}

0 commit comments

Comments
 (0)