rtb-forge 0.7.1

Release-provider abstractions and backends (GitHub, GitLab, Bitbucket, Gitea, Codeberg, Direct) plus an async git-operations Repo built on gix. Part of the phpboyscout Rust toolkit.
Documentation
[package]
name = "rtb-forge"
description = "Release-provider abstractions and backends (GitHub, GitLab, Bitbucket, Gitea, Codeberg, Direct) plus an async git-operations Repo built on gix. Part of the phpboyscout Rust toolkit."
version = "0.7.1"
edition = "2021"
rust-version = "1.82"
license = "MIT"
authors = ["Matt Cockayne <matt@phpboyscout.com>"]
repository = "https://gitlab.com/phpboyscout/rust/forge"
homepage = "https://forge.rust.phpboyscout.uk"
documentation = "https://docs.rs/rtb-forge"
readme = "README.md"
categories = ["development-tools", "api-bindings"]
keywords = ["git", "release", "github", "gitlab", "gitea"]

[lints.rust]
# `deny` (not forbid) so the linkme registration sites can carry a
# targeted `#[allow(unsafe_code)]`; see the comment in `src/lib.rs` —
# that file's `#![deny(unsafe_code)]` is the production guarantee.
unsafe_code = "deny"
missing_docs = "warn"

[lints.clippy]
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
module_name_repetitions = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
multiple_crate_versions = "allow"

[features]
# Built-in backends are feature-gated so a downstream tool that only
# targets one provider doesn't pay the compile-cost of the others.
# `git` is default-on per v0.5 scope A7 — the framework spec promises
# `Repo`; tools with hard binary-size budgets disable it via
# `default-features = false`.
default = ["github", "gitlab", "gitea", "codeberg", "direct", "bitbucket", "git"]
# Each backend is feature-gated so a downstream tool that only targets
# one provider can trim the compile-cost of the others.
github = ["_http"]
gitlab = ["_http"]
gitea = ["_http"]
# Codeberg piggybacks on the gitea backend but exposes its own
# distinct `source_type: codeberg` registration with the host hard-
# coded to `codeberg.org`.
codeberg = ["gitea"]
direct = ["_http"]
bitbucket = ["_http"]
# v0.5 git-operations slice. Pulls in `gix` for the `Repo` type and
# `rtb-credentials` for auth (Repo::clone / fetch / push call
# `Resolver::resolve` per A2). See the monorepo spec
# `docs/development/specs/2026-05-11-v0.5-scope.md`.
git = ["dep:gix", "dep:rtb-credentials", "dep:futures-core"]
# Spec A4 originally reserved a `git2-fallback` opt-in for ops gix
# can't yet do (push). v0.5's consistent shell-out architecture for
# all write paths (commit / fetch / checkout / push) made the
# fallback unnecessary — push uses the same shell-out pattern as
# fetch. The feature flag is intentionally not declared; if a
# future op needs libgit2 a `git2` dep declaration can
# re-introduce it.
# Enables the testcontainers-backed integration-tests gated with
# `#[cfg(feature = "integration")]`. Not default-on: requires Docker.
integration = []
# Internal feature — pulls in the HTTP-client deps any backend needs.
# Not for direct use by downstream tools.
_http = ["dep:reqwest", "dep:serde_json", "dep:bytes", "dep:futures-util", "dep:tokio-util"]

[dependencies]
# Async + trait plumbing
tokio = { version = "1.53.0", features = ["full"] }
async-trait = "0.1.91"

# Error type
miette = { version = "7.6.0", features = ["fancy"] }
thiserror = "2.0.19"

# Config types
serde = { version = "1.0.229", features = ["derive"] }

# Value types
semver = { version = "1.0.28", features = ["serde"] }
time = { version = "0.3.53", features = ["serde", "formatting", "parsing", "macros"] }

# Secrets — providers accept an optional bearer token
secrecy = "0.10.3"

# Registry (linkme-backed distributed slice of provider factories)
linkme = "0.3.37"

# Backend HTTP — optional, feature-gated per backend.
reqwest = { version = "0.13.4", default-features = false, features = ["rustls", "json", "stream", "gzip"], optional = true }
serde_json = { version = "1.0.150", optional = true }
bytes = { version = "1.12.1", optional = true }
futures-util = { version = "0.3.33", optional = true }
# `io` feature adds `StreamReader`, which adapts `Stream<Item = Result<Bytes>>`
# into `AsyncRead` for streaming asset downloads.
tokio-util = { version = "0.7.18", features = ["io"], optional = true }

# v0.5 git-operations slice — gated on the `git` feature. gix is the
# primary backend; rtb-credentials handles auth resolution;
# futures-core supplies the `Stream` trait that CommitWalk implements.
gix = { version = "0.85.0", default-features = false, features = [
    "blocking-network-client",
    "blocking-http-transport-reqwest-rust-tls",
    # v0.5 git-ops slice: status + dirwalk + parallel (for Send on
    # ThreadSafeRepository). `extras` pulls in status + dirwalk +
    # excludes + attributes + interrupt + credentials, which together
    # are what `Repository::status()` actually needs to compile.
    "parallel",
    "extras",
    # gix 0.83 moved hash backends behind feature flags — gix-hash
    # emits `compile_error!` if neither sha1 nor sha256 is enabled.
    # We use git's standard SHA-1 hashes; SHA-256 (newer hash format
    # repos) can be added when a downstream tool needs it.
    "sha1",
    # v0.5 commit 2b: enables the `gix::blame` re-export of gix-blame
    # for `Repo::blame`.
    "blame",
], optional = true }
rtb-credentials = { version = "0.6.0", optional = true }
futures-core = { version = "0.3.33", optional = true }

[dev-dependencies]
cucumber = { version = "0.23.0", features = ["macros"] }
serde_yaml = "0.9.34"
serde_json = "1.0.150"
# `multipart` is only used by the testcontainers Gitea integration test
# to upload an asset via the real Gitea API.
reqwest = { version = "0.13.4", default-features = false, features = ["rustls", "json", "stream", "gzip", "multipart"] }
wiremock = "0.6.5"
testcontainers = "0.27.3"
tempfile = "3.27.0"
# Read-paths tests poll the CommitWalk async stream via StreamExt.
futures = "0.3.33"