use std::{
fs,
path::{Path, PathBuf},
};
use anyhow::{Context, Result, ensure};
const APPROVED_PIN: &str = "a5765019912a8ab6882b12db049c7cde635f3a85";
const UPLOADER_MARKER: &str = "leynos/shared-actions/.github/actions/upload-codescene-coverage@";
const DEPRECATED_INPUT: &str = "installer-checksum";
const DEPRECATED_VARIABLE: &str = "CODESCENE_CLI_SHA256";
const REFRESH_WORKFLOW_STEM: &str = "get-codescene-sha";
const WORKFLOW_EXTENSIONS: [&str; 2] = ["yml", "yaml"];
fn workflow_directory() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join(".github")
.join("workflows")
}
fn workflow_sources() -> Result<Vec<(String, String)>> {
let directory = workflow_directory();
let entries = fs::read_dir(&directory)
.with_context(|| format!("{} could not be read", directory.display()))?;
let mut sources = Vec::new();
for entry in entries {
let path = entry
.with_context(|| format!("could not read an entry in {}", directory.display()))?
.path();
let is_workflow = path.extension().is_some_and(|extension| {
WORKFLOW_EXTENSIONS
.iter()
.any(|accepted| extension.eq_ignore_ascii_case(accepted))
});
if !is_workflow {
continue;
}
let name = path
.file_name()
.with_context(|| format!("{} has no file name", path.display()))?
.to_string_lossy()
.into_owned();
let source = fs::read_to_string(&path)
.with_context(|| format!("{} could not be read", path.display()))?;
sources.push((name, source));
}
sources.sort_by(|left, right| left.0.cmp(&right.0));
ensure!(
!sources.is_empty(),
"no workflow was found under {}, so every contract in this file would pass having read \
nothing",
directory.display()
);
Ok(sources)
}
fn uploader_references() -> Result<Vec<(String, String)>> {
let sources = workflow_sources()?;
Ok(sources
.into_iter()
.flat_map(|(name, source)| {
source
.split(UPLOADER_MARKER)
.skip(1)
.map(|tail| {
let revision: String = tail
.chars()
.take_while(|character| !character.is_whitespace())
.collect();
(name.clone(), revision)
})
.collect::<Vec<_>>()
})
.collect())
}
fn workflows_containing(needle: &str) -> Result<Vec<String>> {
let sources = workflow_sources()?;
Ok(sources
.into_iter()
.filter(|(_, source)| source.contains(needle))
.map(|(name, _)| name)
.collect())
}
#[test]
fn no_workflow_passes_the_deprecated_installer_checksum() -> Result<()> {
let offenders = workflows_containing(DEPRECATED_INPUT)?;
ensure!(
offenders.is_empty(),
"{DEPRECATED_INPUT} is deprecated and rejected by the uploader at {APPROVED_PIN}; remove \
it from {offenders:?}"
);
Ok(())
}
#[test]
fn no_workflow_references_the_deprecated_checksum_variable() -> Result<()> {
let offenders = workflows_containing(DEPRECATED_VARIABLE)?;
ensure!(
offenders.is_empty(),
"{DEPRECATED_VARIABLE} fed {DEPRECATED_INPUT} and has no remaining consumer; remove it \
from {offenders:?}"
);
Ok(())
}
#[test]
fn every_uploader_reference_is_pinned_to_the_approved_revision() -> Result<()> {
let references = uploader_references()?;
ensure!(
!references.is_empty(),
"no upload-codescene-coverage reference was found, so the pin assertion would pass \
vacuously; this repository is expected to call the uploader"
);
let wrong: Vec<&(String, String)> = references
.iter()
.filter(|(_, revision)| revision != APPROVED_PIN)
.collect();
ensure!(
wrong.is_empty(),
"every upload-codescene-coverage reference must be pinned to {APPROVED_PIN}; found \
{wrong:?}"
);
Ok(())
}
#[test]
fn the_checksum_refresh_workflow_is_absent() {
let dir = workflow_directory();
let present: Vec<String> = WORKFLOW_EXTENSIONS
.iter()
.map(|extension| format!("{REFRESH_WORKFLOW_STEM}.{extension}"))
.filter(|name| dir.join(name).exists())
.collect();
assert!(
present.is_empty(),
"{present:?} maintains {DEPRECATED_VARIABLE}, which no workflow reads; delete it rather \
than keeping a dispatch that writes an unread repository variable"
);
}