diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd0ee87 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.rs] +indent_size = 4 +max_line_length = 100 + +[*.toml] +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 + +[*.md] +indent_size = 2 +# Two trailing spaces are a hard line break in Markdown. +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.tangled/workflows/ci.yml b/.tangled/workflows/ci.yml new file mode 100644 index 0000000..936e72f --- /dev/null +++ b/.tangled/workflows/ci.yml @@ -0,0 +1,50 @@ +# Format, lint, build and test on every push to main and every pull request. +# +# Every step is a command a person can run locally, in the same order, so a +# red pipeline can be reproduced with one line from CONTRIBUTING.md. +# +# UNVERIFIED against a live spindle - no CI runner is attached to this repo +# yet, so this file's schema has not been executed once. The shape follows a +# known-good workflow, but check it against tangled's current workflow +# reference before trusting a green run. See TODO.md. +when: + - event: ["push"] + branch: ["main"] + - event: ["pull_request"] + +engine: "nixery" + +# cargo and rustc are separate nixpkgs attrs, as are the two components; there +# is no single "install the toolchain" attr being used here. Note this is NOT +# rustup, so rust-toolchain.toml is ignored and the version can drift from the +# local pin - the first thing to check if fmt or clippy disagree with a local +# run. gcc is for ring, which builds C. +dependencies: + nixpkgs: + - cargo + - rustc + - clippy + - rustfmt + - gcc + - git + +steps: + # Cheapest check first, and the one most likely to be red: the tree did not + # pass this when the file was added. Pre-existing, and clearing it needs a + # commit that touches src/. + - name: "fmt" + command: "cargo fmt --check" + + # Lint config lives in Cargo.toml's [lints] tables. Six warnings predate + # this file, so -D warnings only holds once those are cleared. + - name: "clippy" + command: "cargo clippy --all-targets --locked -- -D warnings" + + # --locked everywhere: Cargo.lock is committed, so CI should fail on a stale + # lockfile rather than quietly resolving a different dependency tree. + - name: "build" + command: "cargo build --locked" + + # No tests exist yet. Kept so the step is wired up when they land. + - name: "test" + command: "cargo test --locked" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ae88a38 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +# Contributing + +Read the note in the README first: this is a single-user codebase, largely AI +generated, with no stability guarantees. Contact @permadeath.com before +sending an issue or a pull request. + +## Build + +The toolchain is pinned in `rust-toolchain.toml`. With rustup installed, +nothing else is needed — it will fetch the right version on first build. + +``` +cargo build +cargo install --path . +``` + +## Checks + +These are the same four the CI pipeline runs, in the same order: + +``` +cargo fmt --check +cargo clippy --all-targets -- -D warnings +cargo build --locked +cargo test --locked +``` + +If `fmt` or `clippy` fails on a tree you have not modified, that is +pre-existing drift rather than something your change caused. Fix it in its +own commit instead of folding it into an unrelated one. + +`cargo deny check` covers licenses and advisories. It needs `cargo install +cargo-deny`, is configured in `deny.toml`, and is not part of the pipeline. + +`vendor/jacquard-oauth` is a third-party crate with its own manifest. It is +not linted or formatted by this project's config, and it should only be +touched to re-apply the patch described in the README. + +## Pull requests + +This project lives on [Tangled](https://tangled.org), not GitHub, and PRs go +through atgc itself: + +``` +atgc login +atgc pr create +``` + +`atgc pr create` diffs the current branch against the remote's default branch +and uploads the patch to your own PDS, so the branch does not need to be +pushed anywhere. Use `--dry-run` to see what would be sent. + +## Commit messages + +Short imperative subject, capitalized, no trailing period, under about 60 +characters — "Add repo create", "Prune stale sessions on login". If the +change needs justification, put it in the body as prose. Look at `git log` +for the shape of it. + +Commits written with an AI assistant carry a `Co-Authored-By:` trailer. diff --git a/Cargo.toml b/Cargo.toml index 899c96f..b0badc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,23 @@ name = "atgc" version = "0.1.0" edition = "2024" +description = "ATproto Git Client. A CLI for Tangled repos and pull requests." license = "MIT OR Apache-2.0" +repository = "https://tangled.org/permadeath.com/atgc" +readme = "README.md" +keywords = ["atproto", "tangled", "git", "cli", "pull-request"] +categories = ["command-line-utilities", "development-tools"] + +# These apply to this package only. vendor/jacquard-oauth is a separate +# package with its own manifest, so it is not linted by them. +[lints.rust] +unsafe_code = "forbid" +missing_debug_implementations = "warn" +unused_qualifications = "warn" +rust_2018_idioms = { level = "warn", priority = -1 } + +[lints.clippy] +all = { level = "warn", priority = -1 } [dependencies] anyhow = "1.0.104" diff --git a/README.md b/README.md index 8c20598..c978b72 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ Please contact [@permadeath.com](https://tangled.org/permadeath.com) before send Commits are checked by [prek](https://prek.j178.dev/), configured in `prek.toml`. Run `prek install` once in your checkout to enable the hooks. They cover whitespace and file hygiene plus `cargo fmt --check` and `cargo clippy -D warnings`, and they leave `vendor/` alone. `prek run --all-files` checks everything without committing. +[CONTRIBUTING.md](CONTRIBUTING.md) covers how to build it, the checks CI +runs, and why pull requests go through `atgc pr create` instead of GitHub. + ## Install ``` diff --git a/TODO.md b/TODO.md index e7b1bd5..5decc21 100644 --- a/TODO.md +++ b/TODO.md @@ -67,6 +67,13 @@ - [ ] `repo collaborator add/remove/list` — knot XRPC (v1.15+) ## pipelines (CI via spindles) +- [ ] Attach a spindle to this repo. `.tangled/workflows/ci.yml` exists but is + unverified — no runner has ever executed it, so its schema is unproven. + It also assumes the nixpkgs cargo/rustc attrs resolve, and that the + nixpkgs toolchain is close enough to rust-toolchain.toml's pin that fmt + and clippy agree with a local run +- [ ] Clear the pre-existing `cargo fmt --check` and clippy findings so the + pipeline's fmt and `-D warnings` gates can go green - [ ] `run list` / `run view` — pipeline runs and workflow logs over spindle XRPC (v1.16+) - [ ] `run watch` — poll a run until it completes diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..60d2076 --- /dev/null +++ b/deny.toml @@ -0,0 +1,67 @@ +# cargo-deny configuration. +# +# Targets cargo-deny 0.20.x. It avoids the keys that were removed in 0.16 +# (licenses.unlicensed / copyleft / default, advisories.vulnerability / +# notice / severity-threshold) and the [bans.std-replacements] table that +# only exists from 0.20.0, so it should also load on 0.19.x. +# +# NOT VERIFIED: cargo-deny is not installed here, so this file has never been +# parsed by the tool. Run `cargo deny check` before trusting it. + +[graph] +# No `targets` list, so every platform's dependencies are checked. The allow +# list below was derived from `cargo metadata --all-features`, which resolves +# the same superset. +all-features = true + +[output] +feature-depth = 1 + +[advisories] +db-urls = ["https://github.com/RustSec/advisory-db"] +yanked = "warn" +# Only flag unmaintained crates this project actually depends on directly; +# "all" turns the deep transitive tree into noise. +unmaintained = "workspace" +unsound = "workspace" +ignore = [] + +[licenses] +# Exactly the licenses the current tree needs, and no more: an entry that +# nothing uses trips `unused-allowed-license`, which defaults to "warn". +# +# Everything else in the tree is dual-licensed with MIT or Apache-2.0 as an +# option, so it resolves against those two. +allow = [ + "Apache-2.0", + "MIT", + "MIT-0", + "BSD-3-Clause", # *-dalek, subtle, alloc-no-stdlib, brotli, encoding_rs + "ISC", # rustls-webpki, untrusted, and ring (Apache-2.0 AND ISC) + "Zlib", # foldhash + "Unicode-3.0", # the icu_* tree and unicode-ident + "CDLA-Permissive-2.0", # webpki-roots + "MPL-2.0", # the jacquard crates, including vendor/jacquard-oauth +] +exceptions = [] +confidence-threshold = 0.8 + +[bans] +# This tree has duplicate versions of a few transitive crates. Worth seeing, +# not worth failing over. +multiple-versions = "warn" +wildcards = "deny" +# vendor/jacquard-oauth is wired in through [patch.crates-io] as a path +# dependency with no version requirement, which is a wildcard by the above. +allow-wildcard-paths = true +highlight = "all" +allow = [] +deny = [] +skip = [] +skip-tree = [] + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..517183f --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +# Pinned rather than "stable" on purpose: clippy and rustfmt change their +# output between releases, so a floating channel turns CI red on a tree +# nobody touched. Bumping this is a deliberate, reviewable commit. +channel = "1.97.1" +components = ["clippy", "rustfmt"] +profile = "minimal" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..6bb1d39 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,13 @@ +# Deliberately close to stock rustfmt. Every option here is stable on the +# stable channel — nightly-only options are silently ignored, so they are +# just noise in a config file. +# +# Nothing in here changes how the existing tree is formatted; these settings +# write down the defaults so they survive a future style_edition bump. +style_edition = "2024" +max_width = 100 +tab_spaces = 4 +hard_tabs = false +newline_style = "Unix" +reorder_imports = true +reorder_modules = true