Group release sources into a source module with re-exports
Move the ReleaseSource trait, GithubEndpoints and ForgejoEndpoints under src/source/, each in its own file (release_source.rs, github.rs, forgejo.rs). The submodules are private; mod.rs re-exports their types and holds for_package, so the rest of the crate imports from crate::source and never names a host's file. checker.rs keeps only version_from_tag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ba2c5c2d02
commit
6459856aab
10 changed files with 52 additions and 43 deletions
|
|
@ -35,8 +35,10 @@ practice rather than asserted from habit — see Further reading.
|
||||||
`helpers/`) tends toward the opposite, and a single feature change ends
|
`helpers/`) tends toward the opposite, and a single feature change ends
|
||||||
up touching files scattered across every layer.
|
up touching files scattered across every layer.
|
||||||
**Rule**: modules are named after what they do in the pipeline
|
**Rule**: modules are named after what they do in the pipeline
|
||||||
(`checker`, `fetcher`, `verifier`, `builder`, `sanity`, `publisher`,
|
(`source`, `fetcher`, `verifier`, `builder`, `sanity`, `publisher`,
|
||||||
`state`), not generic buckets. A new pipeline stage gets a new module
|
`state`), not generic buckets. (`source` is a directory module: the
|
||||||
|
`ReleaseSource` trait and one file per host, re-exported from its
|
||||||
|
`mod.rs` so the rest of the crate never names a host's file.) A new pipeline stage gets a new module
|
||||||
named after the stage, not a method bolted onto an existing one.
|
named after the stage, not a method bolted onto an existing one.
|
||||||
**Anti-example to keep watching for**: a `utils.rs` grab-bag. `hash.rs`
|
**Anti-example to keep watching for**: a `utils.rs` grab-bag. `hash.rs`
|
||||||
could look like one but isn't — it exists for exactly one piece of
|
could look like one but isn't — it exists for exactly one piece of
|
||||||
|
|
@ -107,7 +109,7 @@ practice rather than asserted from habit — see Further reading.
|
||||||
directory, but the same information — why this way and not the obvious
|
directory, but the same information — why this way and not the obvious
|
||||||
alternative — needs to live somewhere a future reader will actually see
|
alternative — needs to live somewhere a future reader will actually see
|
||||||
it: the doc comment on the thing itself.
|
it: the doc comment on the thing itself.
|
||||||
**Example already here**: `github.rs`'s doc comment on
|
**Example already here**: `source/github.rs`'s doc comment on
|
||||||
`GithubEndpoints::latest_release` explains why the newest Atom-feed entry isn't
|
`GithubEndpoints::latest_release` explains why the newest Atom-feed entry isn't
|
||||||
trusted outright (scaleway-cli's `-dbg1` tag has no real Release behind
|
trusted outright (scaleway-cli's `-dbg1` tag has no real Release behind
|
||||||
it) — the reasoning lives right next to the code it justifies, not in a
|
it) — the reasoning lives right next to the code it justifies, not in a
|
||||||
|
|
|
||||||
|
|
@ -373,10 +373,12 @@ Open questions on the schema:
|
||||||
likely reuses `nvchecker`'s logic/sources conceptually for non-GitHub
|
likely reuses `nvchecker`'s logic/sources conceptually for non-GitHub
|
||||||
sources eventually. For GitHub sources, prefers the `github-atom` feed
|
sources eventually. For GitHub sources, prefers the `github-atom` feed
|
||||||
(see Scaling > Check method) over unconditional REST polling.
|
(see Scaling > Check method) over unconditional REST polling.
|
||||||
*(Implemented for GitHub and Forgejo. `src/checker.rs` defines the
|
*(Implemented for GitHub and Forgejo. `src/source/` defines the
|
||||||
`ReleaseSource` trait (latest release + API root); each host implements
|
`ReleaseSource` trait (latest release + API root); each host implements
|
||||||
it in its own module (`github.rs`, `forgejo.rs`), and `source.rs` picks
|
it in its own file (`github.rs`, `forgejo.rs`), and the module's
|
||||||
one per package, so adding a host doesn't touch existing ones. A trait
|
`for_package` picks one per package, so adding a host doesn't touch
|
||||||
|
existing ones. The rest of the crate imports the trait and hosts from
|
||||||
|
`crate::source`, which re-exports them. A trait
|
||||||
rather than an enum match because there are now two real hosts with
|
rather than an enum match because there are now two real hosts with
|
||||||
genuinely different logic. GitHub regex-matches the first
|
genuinely different logic. GitHub regex-matches the first
|
||||||
`releases/tag/<tag>` link in the feed rather than doing a full XML
|
`releases/tag/<tag>` link in the feed rather than doing a full XML
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,6 @@
|
||||||
//! The check stage's contract: a `ReleaseSource` says what a package's
|
//! Turns a release tag into a version string. What the latest tag *is*
|
||||||
//! latest release is and where its releases API lives, and
|
//! comes from a `ReleaseSource` (see `source`); this is the one piece of
|
||||||
//! `version_from_tag` turns the resulting tag into a version string. Each
|
//! the check stage that isn't host-specific.
|
||||||
//! hosting service implements the trait in its own module (`github`,
|
|
||||||
//! `forgejo`) so per-host logic doesn't accumulate here;
|
|
||||||
//! `source::for_package` picks the implementation for a package.
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
/// A hosting service a package's releases are published on. `Debug` so a
|
|
||||||
/// `Box<dyn ReleaseSource>` can sit in a `Result` that tests unwrap.
|
|
||||||
pub trait ReleaseSource: std::fmt::Debug {
|
|
||||||
/// The latest release tag for `repo`.
|
|
||||||
fn latest_release(&self, client: &reqwest::blocking::Client, repo: &str) -> Result<String>;
|
|
||||||
|
|
||||||
/// The releases API root, which `fetcher` and `verifier` build their
|
|
||||||
/// own paths under. GitHub and Forgejo both serve
|
|
||||||
/// `/repos/{owner}/{repo}/releases/tags/{tag}` there with the same
|
|
||||||
/// `assets[].{name, browser_download_url}` shape, which is why those
|
|
||||||
/// stages need only this and not the source itself.
|
|
||||||
fn api(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Strips a leading `v` from a release tag, e.g. `v2.62.0` -> `2.62.0`.
|
/// Strips a leading `v` from a release tag, e.g. `v2.62.0` -> `2.62.0`.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Downloads a named release asset (from GitHub or Forgejo) to a local
|
//! Downloads a named release asset (from GitHub or Forgejo) to a local
|
||||||
//! path. The only module that talks to the releases API for asset bytes —
|
//! path. The only module that talks to the releases API for asset bytes —
|
||||||
//! `checker` only resolves version tags, never downloads.
|
//! `source` only resolves version tags, never downloads.
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ mod builder;
|
||||||
mod checker;
|
mod checker;
|
||||||
mod config;
|
mod config;
|
||||||
mod fetcher;
|
mod fetcher;
|
||||||
mod forgejo;
|
|
||||||
mod github;
|
|
||||||
mod hash;
|
mod hash;
|
||||||
mod notifier;
|
mod notifier;
|
||||||
mod paths;
|
mod paths;
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
//! `main.rs`.
|
//! `main.rs`.
|
||||||
|
|
||||||
use crate::builder;
|
use crate::builder;
|
||||||
use crate::checker::{self, ReleaseSource};
|
use crate::checker;
|
||||||
use crate::config::{self, Package};
|
use crate::config::{self, Package};
|
||||||
use crate::fetcher::{self, DownloadedAsset};
|
use crate::fetcher::{self, DownloadedAsset};
|
||||||
use crate::notifier::{self, Event};
|
use crate::notifier::{self, Event};
|
||||||
use crate::paths::Paths;
|
use crate::paths::Paths;
|
||||||
use crate::publisher;
|
use crate::publisher;
|
||||||
use crate::sanity;
|
use crate::sanity;
|
||||||
use crate::source;
|
use crate::source::{self, ReleaseSource};
|
||||||
use crate::state;
|
use crate::state;
|
||||||
use crate::verifier::{self, VerificationResult};
|
use crate::verifier::{self, VerificationResult};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
|
|
@ -353,8 +353,7 @@ fn approve(state_dir: &Path, work_dir: &Path, name: &str, pkg: &Package, tag: &s
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::forgejo::ForgejoEndpoints;
|
use crate::source::{ForgejoEndpoints, GithubEndpoints};
|
||||||
use crate::github::GithubEndpoints;
|
|
||||||
use crate::test_support::same_origin_package;
|
use crate::test_support::same_origin_package;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! A Forgejo (or Gitea) instance as a release source: its API root, and
|
//! A Forgejo (or Gitea) instance as a release source: its API root, and
|
||||||
//! how to find a repo's latest release there.
|
//! how to find a repo's latest release there.
|
||||||
|
|
||||||
use crate::checker::ReleaseSource;
|
use super::ReleaseSource;
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! GitHub as a release source: its endpoints, and how to find a repo's
|
//! GitHub as a release source: its endpoints, and how to find a repo's
|
||||||
//! latest release there.
|
//! latest release there.
|
||||||
|
|
||||||
use crate::checker::ReleaseSource;
|
use super::ReleaseSource;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
//! Picks the `ReleaseSource` implementation for a package from its
|
//! Where a package's releases are published: the `ReleaseSource` trait,
|
||||||
//! configured `source`. The one place that knows which hosts exist; the
|
//! one file per host implementing it, and `for_package`, which picks the
|
||||||
//! pipeline and the stages it calls only ever see the trait.
|
//! implementation for a package from its configured `source`. The
|
||||||
|
//! submodules are private and re-exported here, so the rest of the crate
|
||||||
|
//! imports everything from `crate::source` and never a host's file.
|
||||||
|
|
||||||
|
mod forgejo;
|
||||||
|
mod github;
|
||||||
|
mod release_source;
|
||||||
|
|
||||||
|
pub use forgejo::ForgejoEndpoints;
|
||||||
|
pub use github::GithubEndpoints;
|
||||||
|
pub use release_source::ReleaseSource;
|
||||||
|
|
||||||
use crate::checker::ReleaseSource;
|
|
||||||
use crate::config::{Package, Source};
|
use crate::config::{Package, Source};
|
||||||
use crate::forgejo::ForgejoEndpoints;
|
|
||||||
use crate::github::GithubEndpoints;
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
/// Defensive: errors only if a `forgejo-release` package has no
|
/// Defensive: errors only if a `forgejo-release` package has no
|
||||||
20
src/source/release_source.rs
Normal file
20
src/source/release_source.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
//! The `ReleaseSource` trait: what the pipeline needs from a hosting
|
||||||
|
//! service a package's releases are published on. Each host implements it
|
||||||
|
//! in its own file next to this one, so per-host logic never accumulates
|
||||||
|
//! here.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
/// `Debug` so a `Box<dyn ReleaseSource>` can sit in a `Result` that tests
|
||||||
|
/// unwrap.
|
||||||
|
pub trait ReleaseSource: std::fmt::Debug {
|
||||||
|
/// The latest release tag for `repo`.
|
||||||
|
fn latest_release(&self, client: &reqwest::blocking::Client, repo: &str) -> Result<String>;
|
||||||
|
|
||||||
|
/// The releases API root, which `fetcher` and `verifier` build their
|
||||||
|
/// own paths under. GitHub and Forgejo both serve
|
||||||
|
/// `/repos/{owner}/{repo}/releases/tags/{tag}` there with the same
|
||||||
|
/// `assets[].{name, browser_download_url}` shape, which is why those
|
||||||
|
/// stages need only this and not the source itself.
|
||||||
|
fn api(&self) -> &str;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue