Commit
Message
Changed Files (12)
-
modified CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md index 213394e..db6b095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,12 @@ ### 🚀 Features - Add multi-format output support (HTML + Gemtext) +- Detect non-interactive terminals and fall back to logging +- Add builder categories for grouped artifact display on release pages ### 🚜 Refactor - Split builder running & output rendering into own modules -- *(archive_builder)* Change output message format. ## [0.7.1] - 2026-06-16 ### 🚀 Features -
modified abbaye.schema.json
diff --git a/abbaye.schema.json b/abbaye.schema.json index b3e6f9a..7037342 100644 --- a/abbaye.schema.json +++ b/abbaye.schema.json @@ -111,7 +111,7 @@ } }, "output": { - "description": "Output path for the generated `.tar.gz` archive.\nDefaults to `source.tar.gz` in the current working directory.", + "description": "Output path for the generated `.tar.gz` archive.\nDefaults to `{prefix}-{version}.tar.gz` in the current working directory.", "type": [ "string", "null" @@ -137,6 +137,13 @@ "description": "A single `[[builders]]` entry: the builder itself plus optional dependency\nmetadata.\n\n# Dependency ordering\n\nBy default every builder runs concurrently with all others. When a builder\nmust only start *after* another one has finished successfully, give the\nprerequisite an `id` and list that id in the dependent's `depends_on`:\n\n```toml\n[[builders]]\ntype = \"cargo\"\nid = \"compile\" # ← give this builder a name\n\n[[builders]]\ntype = \"script\"\nscript = [\"strip target/release/my_bin\"]\noutputs = [\"target/release/my_bin\"]\ndepends_on = [\"compile\"] # ← wait for the builder above\n```\n\nCircular dependencies are detected before any builder starts and reported\nas an error.", "type": "object", "properties": { + "category": { + "description": "Optional category for grouping artifacts on the release page.\nArtifacts produced by builders with the same `category` are listed\ntogether under a shared heading.", + "type": [ + "string", + "null" + ] + }, "depends_on": { "description": "IDs of builders that must complete successfully before this one starts.", "type": "array", -
modified abbaye.toml
diff --git a/abbaye.toml b/abbaye.toml index e197760..b61793a 100644 --- a/abbaye.toml +++ b/abbaye.toml @@ -1,54 +1,256 @@ +# ── Abbaye Configuration ────────────────────────────────────────────────────── +# This file configures the Abbaye release-page generator. +# See https://opencode.ai for details. +# +# All paths are relative to the directory containing this file. +# All fields are optional unless marked "REQUIRED". + +# JSON Schema reference for editor autocompletion (optional). "$schema" = "./abbaye.schema.json" +# ── Site Metadata ──────────────────────────────────────────────────────────── + [site] +# REQUIRED. Display name of the project, used in page titles and headings. name = "Abbaye" + +# Canonical base URL of the published site (optional). +# When set, the Atom feed uses absolute links and proper entry IDs. +# Trailing slashes are stripped automatically. base_url = "https://vit.am/~ololduck/abbaye" + +# URL of the project's repository (optional). +# When set, a link is shown on every release page. repo_url = "https://git.sr.ht/~ololduck/abbaye" + +# Output directory for generated files (optional, default: "public"). +# output_dir = "public" + +# Path to the README file rendered on each version page (optional, default: "README.md"). +# readme = "README.md" + +# Language code for the generated HTML <html lang="..."> attribute (optional, default: "en"). +# lang = "en" + +# Fediverse creator handle for social meta tags (optional). +# Don't forget the leading '@'. fediverse_creator = "@ololduck@vit.am" + +# Output formats to generate (optional, default: ["html"]). +# Set to ["html", "gemtext"] to also produce Gemini text pages. formats = ["html", "gemtext"] +# ── OpenGraph (optional section) ──────────────────────────────────────────── + [site.opengraph] +# REQUIRED. URL of the website's image used in social-media preview cards. image = "latest/logo-wordmark.svg" + +# Alt text for the image (optional). image_alt = "Abbaye logo" + +# Author of the website, shown on release-notes pages (optional). author = "Ololduck <ololduck@vit.am>" +# OpenGraph type (optional, default: "website" for index, "article" for release pages). +# type = "website" + +# Canonical URL for og:url (optional, falls back to site.base_url). +# url = "https://vit.am/~ololduck/abbaye" + +# ── Git Repository Web UI (optional section) ──────────────────────────────── +# When present, abbaye generates browsable HTML at <output>/repository/ +# and a clonable bare repository at <output>/repository.git/. + [git_ui] -default_branch = "main" +# Default branch displayed when opening the repository page (optional, default: "main"). +# default_branch = "main" + +# Maximum number of commits shown on the log page (optional, default: 200). +# max_commits = 200 + +# Path to the git repository to read (optional, default: "."). +# repo_path = "." + +# Explicit clone URL displayed in the UI (optional). +# When absent, derived from site.base_url + "/repository.git". +# clone_url = "https://vit.am/~ololduck/abbaye/repository.git" + +# Glob patterns for refs to EXCLUDE from the UI (optional, default: []). +# Any branch or tag matching one of these patterns is hidden. +# exclude = ["gh-pages", "tmp/*"] + +# Glob patterns for refs to INCLUDE (optional, default: [] = allow all). +# When non-empty, acts as an allowlist: only refs matching at least one +# include pattern are shown (and are then still subject to exclude). include = ["main", "develop", "v*"] +# ── Version Extractor ─────────────────────────────────────────────────────── +# Determines how the current release version is obtained. +# Two backends are available: "git" (tags) or "cargo" (Cargo.toml). + [version_extractor] type = "git" + +# ── Git Version Extractor (type = "git") ───────────────────────────────────── +# Derives the version from `git describe --tags --always`. + +# Strip this prefix from the tag name (optional, default: none). +# "v1.2.3" with tag_prefix = "v" → "1.2.3". tag_prefix = "v" +# Suffix appended when the working tree has uncommitted changes (optional, default: "-dirty"). +# dirty_suffix = "-dirty" + +# ── Cargo Version Extractor (alternative: type = "cargo") ──────────────────── +# Reads version from [package].version in a Cargo.toml. +# +# [version_extractor] +# type = "cargo" +# +# Path to Cargo.toml (optional, default: "Cargo.toml"). +# manifest_path = "Cargo.toml" + +# ── Changelog ─────────────────────────────────────────────────────────────── + [changelog] +# Directory containing CHANGELOG.md (optional, default: "."). +# directory = "." + +# ── Builders ───────────────────────────────────────────────────────────────── +# Each [[builders]] entry defines a build step. Builders run in parallel +# unless linked via depends_on / id. Artifacts (files) land in <version>/dist/ +# and directories land in <version>/docs/. +# +# Common fields shared by all builder types: +# +# id — optional stable name for dependency resolution +# depends_on — optional list of builder IDs to wait for +# category — optional grouping label for the release-page download list +# +# The per-type fields (targets, script, …) are flattened into the same table. + +# ── Cargo Builder (type = "cargo") ────────────────────────────────────────── -[[builders]] # builds the project using cargo build --release +[[builders]] +# Builds the Rust project with `cargo build --release`. + +category = "Binaries" type = "cargo" + +# Target triples for cross-compilation (optional, default: [] = host target). targets = ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] -[[builders]] # generates documentation using cargo doc +# Path to Cargo.toml (optional, default: "./Cargo.toml"). +# manifest_path = "Cargo.toml" + +# Restrict collected artifacts to these binary/cdylib names (optional, default: [] = all). +# Useful in workspaces to avoid picking up dev-tools or examples. +# bins = ["my_app", "my_lib"] + +# Run cross-compilation targets in parallel (optional, default: true). +# Set to false when disk space is tight or the build host is resource-constrained. +# parallel = true + +# ── Cargo Doc Builder (type = "cargo_doc") ────────────────────────────────── + +[[builders]] +# Generates API documentation with `cargo doc`. +# The resulting doc directory is placed under <version>/docs/. type = "cargo_doc" + +# Skip building documentation for dependencies (optional, default: false). no_deps = true +# Path to Cargo.toml (optional, default: "./Cargo.toml"). +# manifest_path = "Cargo.toml" + +# ── Script Builder (type = "script") ──────────────────────────────────────── + [[builders]] id = "generate schema" +category = "Other" type = "script" + +# REQUIRED. Shell commands to execute in order. +# Each line is passed to `sh -c`. The build fails on any non-zero exit. +# Environment variable ABBAYE_BUILDING_VERSION is set automatically. script = [ "mkdir -p target", "cargo run --bin abbaye -- dump-schema > target/abbaye.schema.json", ] + +# REQUIRED. Paths (or glob patterns) of files/directories produced by the +# script that should be treated as release artifacts. +# Each entry can be: +# - A plain string path: "target/release/mybin" +# - A table with a custom display name: +# { path = "target/release/mybin", name = "mybin-v1.0.0-linux" } outputs = ["target/abbaye.schema.json"] +# ── Archive Builder (type = "archive") ────────────────────────────────────── + [[builders]] id = "archive source" -type = "archive" # creates a compressed tarball of the source code (can be of anything, really) +category = "Source" +type = "archive" +# Creates a .tar.gz archive of the source tree, honouring .gitignore rules. + +# Root directory to archive (optional, default: "."). +# source_dir = "." + +# Output path for the .tar.gz archive (optional). +# Default: "{prefix}-{version}.tar.gz" in the current working directory. output = "target/abbaye-source.tar.gz" +# Prefix applied to every entry inside the archive (optional, default: source dir name). +# Example: "myproject-1.0.0" → entries like "myproject-1.0.0/src/main.rs". +# prefix = "abbaye" + +# Glob patterns to exclude (optional, default: [".git", "*.local"]). +# Each pattern is matched against every path component. +# ignore_patterns = [".git", "*.local"] + +# ── AUR Package Script ────────────────────────────────────────────────────── + [[builders]] id = "AUR package" +category = "Packages" type = "script" script = [ "cargo aur", "sed -i \"s%source=.*$%source=(\"https://vit.am/~ololduck/abbaye/abbaye-$pkgver-x86-64.tar.gz\")%\" target/cargo-aur/PKGBUILD", ] outputs = ["target/cargo-aur/abbaye-*-x86_64.tar.gz"] + +# ── Markdown Builder (type = "markdown", example — not active here) ───────── +# +# [[builders]] +# type = "markdown" +# +# # Directory containing .md files to render (optional, default: "."). +# input = "docs/" +# +# # Output directory (optional, default: "<input-dir-name>-html"). +# output = "docs-html/" +# +# # Descend into subdirectories (optional, default: true). +# recursive = true +# +# # Output formats (optional, default: ["html"]). +# formats = ["html", "gemtext"] + +# ── Dependency Ordering Examples ───────────────────────────────────────────── +# Builders run concurrently by default. To enforce ordering, assign an `id` +# and reference it in `depends_on`: +# +# [[builders]] +# id = "compile" +# type = "cargo" +# targets = ["x86_64-unknown-linux-gnu"] +# +# [[builders]] +# type = "script" +# script = ["strip target/release/mybin"] +# outputs = ["target/release/mybin"] +# depends_on = ["compile"] # waits for "compile" to finish -
modified src/builders/archive.rs
diff --git a/src/builders/archive.rs b/src/builders/archive.rs index 4abd6d6..9269432 100644 --- a/src/builders/archive.rs +++ b/src/builders/archive.rs @@ -104,6 +104,7 @@ impl Builder for ArchiveBuilder { path: archive_path, name, hash: None, + category: None, }]) } } -
modified src/builders/cargo.rs
diff --git a/src/builders/cargo.rs b/src/builders/cargo.rs index c412bd6..36cc34d 100644 --- a/src/builders/cargo.rs +++ b/src/builders/cargo.rs @@ -360,6 +360,7 @@ async fn run_cargo_build( path, name, hash: None, + category: None, }); } } @@ -401,6 +402,7 @@ async fn relocate_artifacts( path: stable, name: artifact.name, hash: artifact.hash, + category: artifact.category, }); } Ok(relocated) @@ -525,6 +527,7 @@ impl Builder for CargoDocBuilder { path: doc_dir, name: "doc".to_owned(), hash: None, + category: None, }]) } } -
modified src/builders/markdown.rs
diff --git a/src/builders/markdown.rs b/src/builders/markdown.rs index f5d0b88..f32a256 100644 --- a/src/builders/markdown.rs +++ b/src/builders/markdown.rs @@ -191,6 +191,7 @@ async fn build_directory( path: output_dir.clone(), name: dir_name_string(&output_dir), hash: None, + category: None, }]); } @@ -261,6 +262,7 @@ async fn build_directory( path: output_dir.clone(), name: dir_name_string(&output_dir), hash: None, + category: None, }]) } -
modified src/builders/mod.rs
diff --git a/src/builders/mod.rs b/src/builders/mod.rs index 62ce38f..091233b 100644 --- a/src/builders/mod.rs +++ b/src/builders/mod.rs @@ -264,6 +264,12 @@ pub struct BuilderEntry { /// IDs of builders that must complete successfully before this one starts. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub depends_on: Vec<String>, + + /// Optional category for grouping artifacts on the release page. + /// Artifacts produced by builders with the same `category` are listed + /// together under a shared heading. + #[serde(skip_serializing_if = "Option::is_none")] + pub category: Option<String>, } impl BuilderEntry { @@ -273,8 +279,13 @@ impl BuilderEntry { } /// Run the inner builder, forwarding the version string and log sender. + /// Each returned [`ArtifactPath`] inherits this entry's `category`. pub async fn build(&self, version: &str, log: LogSender) -> Result<Vec<ArtifactPath>> { - self.builder.build(version, log).await + let mut artifacts = self.builder.build(version, log).await?; + for artifact in &mut artifacts { + artifact.category = self.category.clone(); + } + Ok(artifacts) } } @@ -379,6 +390,9 @@ pub struct ArtifactPath { pub name: String, /// Lowercase hexadecimal SHA1 digest of the artifact's contents, if computed. pub hash: Option<String>, + /// Optional category for grouping on the release page, carried over from + /// the builder entry that produced this artifact. + pub category: Option<String>, } /// # Implementing a New Builder -
modified src/builders/script.rs
diff --git a/src/builders/script.rs b/src/builders/script.rs index a71ecab..a75f18b 100644 --- a/src/builders/script.rs +++ b/src/builders/script.rs @@ -146,6 +146,7 @@ impl Builder for ScriptBuilder { path: path.to_path_buf(), name, hash: None, + category: None, }); } } @@ -172,6 +173,7 @@ impl Builder for ScriptBuilder { path: pattern.clone(), name, hash: None, + category: None, }); } } -
modified src/main.rs
diff --git a/src/main.rs b/src/main.rs index a93bd8d..bbfd836 100644 --- a/src/main.rs +++ b/src/main.rs @@ -350,6 +350,7 @@ async fn main() -> Result<()> { }), id: None, depends_on: vec![], + category: None, }], changelog: ChangelogConfig { ..Default::default() -
modified src/site.rs
diff --git a/src/site.rs b/src/site.rs index 563b4a0..13e4d06 100644 --- a/src/site.rs +++ b/src/site.rs @@ -72,6 +72,18 @@ struct DistFileInfo { size_human: String, /// Lowercase hex-encoded SHA-256 digest of the file contents. sha256: String, + /// Optional category inherited from the builder that produced this file. + #[serde(skip_serializing_if = "Option::is_none")] + category: Option<String>, +} + +/// A group of dist artifacts sharing the same builder category. +#[derive(serde::Serialize)] +struct DistCategory { + /// The shared category, or `None` for builders without one. + category: Option<String>, + /// Files in this category. + files: Vec<DistFileInfo>, } /// Build the full website into `config.output_dir` (defaults to `public/`). @@ -170,9 +182,24 @@ pub async fn build_site(config: AbbayeConfig) -> Result<()> { size_bytes, size_human: human_size(size_bytes), sha256, + category: artifact.category.clone(), }); } - let has_dist = !dist_file_infos.is_empty(); + + // Group dist artifacts by category. + let mut category_map: std::collections::BTreeMap<Option<String>, Vec<DistFileInfo>> = + std::collections::BTreeMap::new(); + for info in dist_file_infos { + category_map + .entry(info.category.clone()) + .or_default() + .push(info); + } + let dist_categories: Vec<DistCategory> = category_map + .into_iter() + .map(|(category, files)| DistCategory { category, files }) + .collect(); + let has_dist = !dist_categories.is_empty(); // ── 5. Lay out docs/ ────────────────────────────────────────────────────── info!("Laying out {} doc artifact(s) …", doc_artifacts.len()); @@ -300,7 +327,7 @@ pub async fn build_site(config: AbbayeConfig) -> Result<()> { ctx.insert("has_docs", &has_docs); ctx.insert("has_docs_tarball", &has_docs_tarball); ctx.insert("has_dist", &has_dist); - ctx.insert("dist_files", &dist_file_infos); + ctx.insert("dist_categories", &dist_categories); ctx.insert("git_ui_enabled", &config.git_ui.is_some()); ctx.insert("git_ui_clone_url", &git_ui_clone_url); ctx.insert("version_tag", &version_tag); -
modified src/templates/version_index.gmi.j2
diff --git a/src/templates/version_index.gmi.j2 b/src/templates/version_index.gmi.j2 index 7104f80..8a5d390 100644 --- a/src/templates/version_index.gmi.j2 +++ b/src/templates/version_index.gmi.j2 @@ -14,9 +14,14 @@ {% endif %} {% endif %}{% if has_dist %} ## Downloads -{% for file in dist_files %} +{% for group in dist_categories %} +{% if group.category %} +### {{ group.category }} +{% endif %} +{% for file in group.files %} => dist/{{ file.name }} {{ file.name }} ({{ file.size_human }}) {% endfor %} +{% endfor %} {% endif %} {{ readme_content | safe }} {% if changelog_content %} -
modified src/templates/version_index.html.j2
diff --git a/src/templates/version_index.html.j2 b/src/templates/version_index.html.j2 index 349de4d..04ca8f0 100644 --- a/src/templates/version_index.html.j2 +++ b/src/templates/version_index.html.j2 @@ -44,20 +44,26 @@ <li><a href="docs.tar.gz">⬇ docs.tar.gz</a></li> {% endif %} </ul> - {% endif %} {% if has_dist %} + {% endif %} + {% if has_dist %} <h3>Downloads</h3> - <ul> - {% for file in dist_files %} - <li> - <a href="dist/{{ file.name }}">⬇ {{ file.name }}</a> - <div class="dist-meta"> - {{ file.size_human }}<br /><code title="SHA-256" - >sha256: {{ file.sha256 }}</code - > - </div> - </li> + {% for group in dist_categories %} + {% if group.category %} + <h4>{{ group.category }}</h4> + {% endif %} + <ul> + {% for file in group.files %} + <li> + <a href="dist/{{ file.name }}">⬇ {{ file.name }}</a> + <div class="dist-meta"> + {{ file.size_human }}<br /><code title="SHA-256" + >sha256: {{ file.sha256 }}</code + > + </div> + </li> {% endfor %} - </ul> + </ul> + {% endfor %} {% endif %} </aside> <main class="release-content">