diff --git a/Cargo.toml b/Cargo.toml index 8fb825e..ec8a6c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ version = "1.0.0" edition = "2021" description = "A modern, fast, and concurrent disk usage analyzer" license = "GPL-3.0-only" -repository = "https://gitlab.speelman.ca/gamertan/sized" +repository = "https://gitea.speelman.ca/gamertan/sized" +homepage = "https://gamertan.com/projects/sized/" readme = "README.md" categories = ["command-line-utilities", "filesystem"] diff --git a/MANUAL.md b/MANUAL.md index 8143987..92e0170 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -16,16 +16,28 @@ ## Installation -### From Binaries (Recommended) -Download the latest pre-compiled binaries from the [Releases](https://gitlab.speelman.ca/gamertan/sized/releases) page. +### From source + +The following installs the current review branch, including scanner hardening +not present in the published v1.0.0 release: -### From Source ```bash -git clone https://gitlab.speelman.ca/gamertan/sized.git +git clone --branch sizequeen-scan-hardening https://gitea.speelman.ca/gamertan/sized.git cd sized -make install # Installs binary and man page +cargo install --locked --path . ``` +Cargo installs to `~/.cargo/bin`. For the published source instead, check out +`v1.0.0` before installing. `make install PREFIX="$HOME/.local"` additionally +installs the man page without requiring administrator privileges. + +### Existing release files + +The [v1.0.0 release](https://gitea.speelman.ca/gamertan/sized/releases/tag/v1.0.0) +has source archives, a macOS arm64 executable, a man page and completions. +It has no Linux binary, architecture-labelled binary archive or `.deb` attachment. +See the [README](README.md#installation) for current installation guidance. + ## Basic Usage By default, `sized` analyzes the current directory recursively and displays a table of the immediate children. diff --git a/Makefile b/Makefile index a5c8057..5f7d326 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ MANDIR = $(PREFIX)/share/man/man1 all: build build: - cargo build --release + cargo build --locked --release install: build install -d $(BINDIR) diff --git a/README.md b/README.md index fd00c94..59cad2d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@ # Sized -A fast, concurrent, and feature-rich command-line tool for visualizing disk usage, written in Rust. +**Know what’s taking up space. Without leaving your terminal.** + +Sized is a Rust command-line disk usage tool for macOS and Linux. Inspect large +folders, filter the noise, and export reports for your own scripts. +[Project page](https://gamertan.com/projects/sized/) · +[Prefer a visual map? Meet SizeQueen](https://gamertan.com/projects/sizequeen/). + +This branch contains scanner hardening under review. The published **v1.0.0** +release predates the hard-link, partial-scan and filesystem-boundary changes +below. No new release is implied by a branch build. ## Features - **Allocation by Default**: Prioritizes filesystem-reported allocated blocks; sparse files retain their separate apparent size, and hard-link allocation is counted once per scan. Reported allocation is not a promise of bytes freed by deletion on filesystems with shared extents or snapshots. -- **Fast & Concurrent**: Uses `rayon` to process directories in parallel, making it extremely fast on modern multi-core systems. +- **Fast & Concurrent**: Processes directories in parallel with `rayon`; scan time depends on the filesystem and workload. - **Rich Output**: Beautifully formatted tables with colors, distinguishing files and directories. - **Apparent Size**: Toggle logical file length with `-a` or `--apparent`. - **Unit Selection**: Switch between Binary (IEC) and Decimal (SI) unit systems via `--units`. @@ -18,56 +27,40 @@ A fast, concurrent, and feature-rich command-line tool for visualizing disk usag ## Installation -### 🚀 Direct Download (macOS & Linux) -Download the latest archive for your architecture from the [Releases](https://gitlab.speelman.ca/gamertan/sized/releases) page. +### Build the current review branch -1. **Extract the archive**: - ```bash - tar -xzf sized-v1.0.0-Darwin-arm64.tar.gz - ``` +With a Rust toolchain and Git installed: -2. **Install Binary & Man Page**: - ```bash - # Move binary to path - sudo mv sized /usr/local/bin/ - - # Install man page - sudo mkdir -p /usr/local/share/man/man1 - sudo cp sized.1 /usr/local/share/man/man1/ - ``` - -3. **macOS Security (Gatekeeper)**: - Since the binary isn't code-signed for the App Store, macOS may block it. To allow it: - ```bash - sudo xattr -d com.apple.quarantine /usr/local/bin/sized - ``` - *Alternatively, run `sized` once, let it fail, then go to **System Settings > Privacy & Security** and click **"Allow Anyway"**.* - -### 🍺 Homebrew (macOS & Linux) -If you have a homebrew tap: ```bash -brew install gamertan/tap/sized -``` - -### 📦 Debian / Ubuntu (.deb) -1. Download the `.deb` package from the [Releases](https://gitlab.speelman.ca/gamertan/sized/releases) page. -2. Install using `dpkg`: - ```bash - sudo dpkg -i sized_1.0.0_amd64.deb - ``` - -### 🦀 From Source (Rust toolchain required) -```bash -git clone https://gitlab.speelman.ca/gamertan/sized.git +git clone --branch sizequeen-scan-hardening https://gitea.speelman.ca/gamertan/sized.git cd sized -make install # Installs binary and man page +cargo install --locked --path . +sized --help ``` -Alternatively, via Cargo: +Cargo installs into `~/.cargo/bin`; include it in your `PATH`. For the published +source instead, check out `v1.0.0` before the install command. Source builds run +locally; they are separate from SizeQueen's signed and notarized Mac app. + +To install the binary and man page together without administrator privileges: + ```bash -cargo install --path . +make install PREFIX="$HOME/.local" ``` +This uses `~/.local/bin` and `~/.local/share/man/man1`; configure `PATH` and your +manual-page search path as needed. + +### Existing release downloads + +The [v1.0.0 release](https://gitea.speelman.ca/gamertan/sized/releases/tag/v1.0.0) +contains source, one legacy executable named `sized`, a man page and shell +completions. The executable was inspected as macOS arm64 (Apple silicon); it is not a Linux download. +There are currently no attached architecture-labelled archives or `.deb` +packages, and no verified Homebrew tap. Build from source for the current +review changes. Platform-labelled archives will be advertised after a new +release is reviewed and published. + ## Documentation - **[Manual](MANUAL.md)**: Detailed explanations of all flags and features. - **[Man Page](sized.1)**: Standard unix man pages (installed via `make install`). @@ -177,19 +170,25 @@ Docker access inside the job. Fork contributions can run the local script; maintainers can bring reviewed changes onto a repository branch for CI. See SHIPMENT for runner-image setup. Documentation-only pushes skip builds. +Run `./scripts/check-release.sh` to verify the actual archive, checksum and +extracted executable on the host. Linux CI also runs this packaging check. + See [source review and release process](SHIPMENT.md) and the [live queue](TODO.md). +## Sized, SizeQueen and the shared scanner + +Sized began as terminal tooling. Its filesystem scanner was extracted into +`sized-core`, which SizeQueen now uses directly beneath its native Mac interface. +SizeQueen adds the treemap and desktop interactions; it does not run the CLI. +Sized currently retains its own scanner copy while shared-core adoption is +reviewed. A public checkout of Sized does not need access to the private core +repository. SizeQueen's matching source download includes its pinned core. + ## License -This project is licensed under the **GNU General Public License v3.0 (GPL-3.0)**. - -### Why GPL-3.0? (The "Insulin" Philosophy) -We believe that core diagnostic tools like `sized` should remain a public good. Inspired by the philosophy behind open-access medicine like insulin, this license ensures that: -- The tool remains **free and open** for everyone to use. -- Any improvements or forks made by others **must also be shared** with the community. -- It prevents proprietary "vampire" versions from taking private credit for public efforts. - -For more details, see the [LICENSE](LICENSE) file. +**GPL-3.0-only.** See [LICENSE](LICENSE) for the complete terms. All Sized features +are free; optional [support for Gamertan](https://gamertan.com/store/) does not +unlock features. ## Contributing diff --git a/SHIPMENT.md b/SHIPMENT.md index dd37a1e..e24ea0e 100644 --- a/SHIPMENT.md +++ b/SHIPMENT.md @@ -6,8 +6,9 @@ This document defines the process for versioning and distributing the `sized` pr Use a named branch from `main` and keep a pull request focused on one outcome. The `sizequeen-scan-hardening` branch corrects accounting/error handling and -adds the scan controls needed by SizeQueen; shared-crate extraction follows -in a separate PR so reviewers can distinguish behaviour changes from packaging. +adds the scan controls needed by SizeQueen; the scanner has also been +extracted into private `sized-core` for SizeQueen. Sized retains its own copy +until adoption preserves public source access. 1. Run local locked tests and strict Clippy as an unprivileged user. Run `./scripts/check-linux.sh linux/arm64` and @@ -20,8 +21,8 @@ in a separate PR so reviewers can distinguish behaviour changes from packaging. 3. Review and merge independently of distribution. A branch push or PR merge does not publish a package, change repository visibility or create a tag. 4. Choose the release version after reviewing library/API compatibility, then - use the release steps below when explicitly authorized. Reconcile legacy - GitLab download/package links before announcing a Gitea release. + use the release steps below when explicitly authorized. Verify the exact archive + contents, target and installation instructions before announcing a release. ### Gitea Linux CI @@ -68,40 +69,54 @@ The project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html git tag -a v1.0.0 -m "Release v1.0.0" ``` -## 2. Asset Generation - -The `scripts/release.sh` script is the authoritative way to generate release assets locally or in any CI environment. +## 2. Asset generation and verification ```bash +./scripts/check-release.sh ./scripts/release.sh ``` -This script generates: -- **Optimized Binary**: The production-ready `sized` executable. -- **Documentation**: The `sized.1` man page. -- **Shell Completions**: Scripts for Bash, Zsh, and Fish. -- **System Installers**: A `.deb` package for Debian-based Linux distributions. +`release.sh` uses the locked dependencies and the Rust toolchain's host target. +It packages the executable **before** creating the archive, together with the +man page, Bash/Zsh/Fish completions, licence, README, manual and build metadata. +Outputs are `dist/sized-v--/`, a sibling `.tar.gz` +and `.tar.gz.sha256`. Existing outputs are never replaced. Use +`--output-dir /absolute/new/path` for an explicitly named local candidate. +`CARGO_TARGET_DIR` is respected. The script does not sign, upload, tag or publish. -All assets are gathered in the `dist/v/` directory. +`check-release.sh` creates temporary outputs, extracts the archive, checks all +required files and the checksum, then runs the packaged executable against an +owned fixture. It also checks the refusal to overwrite. Gitea's Linux check +runs this same test; a successful compile alone is insufficient. -## 3. Distribution Channels +Only build from a clean, reviewed commit for distribution. `BUILD-INFO.txt` +records the commit, target, toolchain and whether tracked inputs were modified. +Exported source can provide `SIZED_SOURCE_REVISION`; verify that provenance +against the original checkout. Retain matching source and complete dependency +licences with any public binary distribution. A host build is unsigned; do not +advertise it as notarized or as a Mac App Store application. -### crates.io -To update the project on the central Rust registry: -```bash -cargo publish -``` +## 3. Distribution channels -### System Package Managers -For Homebrew, GitLab, or Gitea-based distribution: -1. Push the git tag to your SCM. -2. Run the release script to generate assets. -3. Attach the contents of the `dist/` folder to your SCM's "Release" or "Tag" entry. -4. Update downstream formulas (like `homebrew-tap`) with the new source URL and SHA256 of the generated tarball. +The existing public Gitea v1.0.0 release contains a macOS arm64 executable, +man page and completions, plus generated source archives. It predates the +scanner hardening branch. No architecture-labelled binary archive, `.deb` or +verified Homebrew tap is currently offered. -## 4. Automation & Hooks +Future public releases need their own reviewed version, source, target-specific +archives, checksums and installation acceptance. Do not replace historical +v1.0.0 assets with newly built bytes. Add a new version only after review. -To automate asset generation locally, you can use a git `post-checkout` or a wrapper script. Since gitea and gitlab use different CI formats, it is recommended to simply call `./scripts/release.sh` within your preferred CI runner (e.g., `gitlab-ci.yml` or `gitea-actions`). +Debian/Alpine packaging is separate from the portable host archive. Do not +implicitly invoke `cargo deb` or `abuild` just because they are installed: a Mac +binary is not a Debian package. Validate each installer on its target OS before +publishing it. Registry and Homebrew publication are separate decisions too. + +## 4. Automation + +CI tests and packages temporary candidates without publishing credentials. +Do not run release generation automatically from Git hooks. Publication remains +an intentional operation after the checks above. ## 5. Manual Installation For system-wide installation from source, use the `Makefile`: diff --git a/TODO.md b/TODO.md index bd60091..c09085c 100644 --- a/TODO.md +++ b/TODO.md @@ -23,23 +23,41 @@ Keep the existing CLI useful and preserve the GPL-3.0-only license. cliff-mads runner. Keep its container isolation and other jobs unchanged; verify a real workflow result before treating CI as proven. -## Proposed next work +## Approved project page and release cleanup -SizeQueen now includes a byte-identical scanner snapshot in a small core crate, -removing terminal dependencies and allowing independent builds. Extract that -shared core upstream in a separate PR after the behaviour changes are reviewed; -terminal formatting is already separate from scan logic. -Windows allocation support and very-large/deep-tree tuning need separate evidence. -README/MANUAL/Cargo metadata still contain legacy GitLab addresses; reconcile -them against available Gitea releases before changing distribution instructions. -Before producing release assets, fix and verify `scripts/release.sh`: it currently -creates the tarball before copying the executable into its input directory. -Do not release/tag/publish automatically. The requested review-branch push is -authorized; see `SHIPMENT.md` for source review and the separate release process. +The owner approved a Sized project page in the shared Gamertan CMS, truthful +installation/release guidance, packaging repairs, and a Built on Sized section +on SizeQueen. Website delivery is tracked in that repository's existing queue. + +- [x] Inspect the public v1.0.0 assets; the unlabelled executable is macOS arm64, + ad hoc signed, SHA-256 `50fde4d8215babbb64f4a4f55e030dd5c941a97ed1bedfa80392e4301c52d2bf`. + There are no attached Linux binaries, labelled archives or Debian packages. +- [x] Replace stale GitLab/unsupported package-manager installation claims with + explicit review-branch source builds; explain the published release boundary. +- [x] Package the executable before the archive; include licence/docs/build + provenance, target-labelled names and checksums. Reject output replacement. + Remove incidental installer generation; host binaries must not become `.deb`s. +- [x] Verify archive contents/checksum, run the extracted CLI and test overwrite + refusal on macOS arm64. Add the same verification to Linux CI. +- [ ] Verify the updated cliff-mads workflow, push the review checkpoint and + record the public CMS delivery. No new version, release assets or merge yet. + +## Deferred / next release + +SizeQueen now pins the extracted private `sized-core`; Sized retains its own +scanner copy. Adoption must keep public builds independently fetchable. +Review scanner/API/status changes before merging the existing branch. Select a +new release version, verify target-specific installation and package complete +corresponding source/dependency notices before publishing fresh binary assets. +Do not replace historical v1.0.0 files. Windows allocation and very-large/deep +scan tuning remain separate work. See `SHIPMENT.md` for the release process. ## Resume note -Current CI checkpoint: repository Actions is enabled and a real native AMD64 +Current work: the approved project page and packaging cleanup above. Local Mac +archive verification passes; updated Linux CI and CMS publication are pending. + +Previous CI checkpoint: repository Actions is enabled and a real native AMD64 push run passed on cliff-mads. [Gitea run 1048 (number 2)](https://gitea.speelman.ca/gamertan/sized/actions/runs/1048) tested `74b30bbf750417121f3b0c26017d2f011a2a8286` on `cliff-himesan-linux-amd64` (`gitea-runner v3.1.0`): all 23 tests, formatting, diff --git a/scripts/check-linux-container.sh b/scripts/check-linux-container.sh index a8b3544..13de280 100755 --- a/scripts/check-linux-container.sh +++ b/scripts/check-linux-container.sh @@ -13,6 +13,10 @@ check_root=$(mktemp -d /tmp/sized-check.XXXXXX) source_root=${SIZED_TEST_SOURCE:-/source} cp "$source_root/Cargo.toml" "$source_root/Cargo.lock" "$check_root/" cp -R "$source_root/src" "$source_root/tests" "$source_root/benches" "$check_root/" +cp "$source_root/LICENSE" "$source_root/README.md" "$source_root/MANUAL.md" "$check_root/" +mkdir "$check_root/scripts" +cp "$source_root/scripts/release.sh" "$source_root/scripts/check-release.sh" "$check_root/scripts/" +export SIZED_SOURCE_REVISION="$(git -C "$source_root" rev-parse HEAD 2>/dev/null || printf unknown)" cd "$check_root" cargo fmt --all -- --check cargo test --locked @@ -24,4 +28,5 @@ fixture_root=$(mktemp -d /tmp/sized-release.XXXXXX) printf 'Linux release smoke test\n' > "$fixture_root/payload" ./target/release/sized --version ./target/release/sized "$fixture_root" --threads 2 --one-file-system --apparent --format json +./scripts/check-release.sh printf 'Linux checks passed.\n' diff --git a/scripts/check-release.sh b/scripts/check-release.sh new file mode 100755 index 0000000..4fea86d --- /dev/null +++ b/scripts/check-release.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root=$(cd "$(dirname "$0")/.." && pwd) +check_root=$(mktemp -d "${TMPDIR:-/tmp}/sized-package.XXXXXX") +trap 'rm -rf "$check_root"' EXIT +"$repo_root/scripts/release.sh" --output-dir "$check_root/bundle" +mkdir "$check_root/extracted" "$check_root/fixture" +tar -xzf "$check_root/bundle.tar.gz" -C "$check_root/extracted" +for required in sized sized.1 sized.bash _sized sized.fish LICENSE README.md MANUAL.md BUILD-INFO.txt; do + test -s "$check_root/extracted/$required" +done +test -x "$check_root/extracted/sized" +( + cd "$check_root" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum -c bundle.tar.gz.sha256 + else + shasum -a 256 -c bundle.tar.gz.sha256 + fi +) +printf 'owned package fixture\n' > "$check_root/fixture/payload" +"$check_root/extracted/sized" --version +"$check_root/extracted/sized" "$check_root/fixture" --apparent --format json > "$check_root/result.json" +grep -q 'payload' "$check_root/result.json" +grep -q '"complete":true' "$check_root/result.json" +if "$repo_root/scripts/release.sh" --output-dir "$check_root/bundle" > "$check_root/repeat.log" 2>&1; then + printf 'Packaging unexpectedly replaced an existing output.\n' >&2 + exit 1 +fi +grep -q 'Refusing to replace' "$check_root/repeat.log" +printf 'Archive contents, checksum, extracted CLI and overwrite guard passed.\n' diff --git a/scripts/release.sh b/scripts/release.sh index 8e2fe0d..d2c7b21 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,53 +1,56 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -euo pipefail -VERSION=$(grep '^version =' Cargo.toml | cut -d '"' -f 2) -DIST_DIR="dist/v$VERSION" - -echo "Building release assets for sized v$VERSION..." - -# 1. Clean and Prepare -rm -rf "$DIST_DIR" -mkdir -p "$DIST_DIR" - -# 2. Build Release Binary -cargo build --release - -# 3. Generate Man Page -cargo run --release -- --generate-man-page "$DIST_DIR" - -# 4. Generate Completions -cargo run --release -- --completions bash > "$DIST_DIR/sized.bash" -cargo run --release -- --completions zsh > "$DIST_DIR/_sized" -cargo run --release -- --completions fish > "$DIST_DIR/sized.fish" - -# 5. Build Debian Package (if cargo-deb is installed) -if command -v cargo-deb &> /dev/null; then - echo "Building Debian package..." - # On macOS, we use --no-strip to avoid 'unrecognized option: --strip-unneeded' - # and --no-build because we already built the release binary. - cargo deb --no-build --no-strip - cp target/debian/*.deb "$DIST_DIR/" - echo "Note: .deb package contains the binary for $(uname -s)-$(uname -m)" -else - echo "Warning: cargo-deb not found. Skipping .deb packaging." +# Build a host-target archive only. No tags, uploads, installers or signing. +repo_root=$(cd "$(dirname "$0")/.." && pwd) +cd "$repo_root" +version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml) +target=$(rustc -vV | sed -n 's/^host: //p') +revision=${SIZED_SOURCE_REVISION:-$(git rev-parse HEAD 2>/dev/null || printf unknown)} +destination="$repo_root/dist/sized-v$version-$target-${revision:0:12}" +if [[ $# == 2 && $1 == --output-dir ]]; then + destination=$2 +elif [[ $# != 0 ]]; then + printf 'Usage: %s [--output-dir ABSOLUTE_PATH]\n' "$0" >&2 + exit 2 fi +[[ $destination == /* ]] || { printf 'Output directory must be absolute.\n' >&2; exit 2; } +for output in "$destination" "$destination.tar.gz" "$destination.tar.gz.sha256"; do + [[ ! -e $output ]] || { printf 'Refusing to replace %s\n' "$output" >&2; exit 1; } +done -# 6. Build Alpine Package (Placeholder/Hook) -# Note: For real .apk building, one usually uses a docker container or abuild. -# This serves as a reminder for Alpine users. -if [ -f "APKBUILD" ] && command -v abuild &> /dev/null; then - echo "Building Alpine package..." - abuild -r -fi +target_dir=${CARGO_TARGET_DIR:-"$repo_root/target"} +[[ $target_dir == /* ]] || target_dir="$repo_root/$target_dir" +cargo build --locked --release --target "$target" +binary="$target_dir/$target/release/sized" +mkdir -p "$destination" +install -m 755 "$binary" "$destination/sized" +"$binary" --generate-man-page "$destination" +"$binary" --completions bash > "$destination/sized.bash" +"$binary" --completions zsh > "$destination/_sized" +"$binary" --completions fish > "$destination/sized.fish" +cp LICENSE README.md MANUAL.md "$destination/" +{ + printf 'Version: %s\nTarget: %s\nSource revision: %s\n' "$version" "$target" "$revision" + printf 'Source worktree: ' + if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + if git diff --quiet HEAD --; then printf 'clean tracked files\n'; else printf 'modified tracked files\n'; fi + else + printf 'exported source; verify against supplied revision\n' + fi + rustc --version + cargo --version + printf 'Unsigned host build; not a notarized Mac application.\n' +} > "$destination/BUILD-INFO.txt" -# 7. Create General Release Tarball -echo "Creating release tarball..." -tar -czf "dist/sized-v$VERSION-$(uname -s)-$(uname -m).tar.gz" -C "$DIST_DIR" . - -# 8. Copy Binary -cp target/release/sized "$DIST_DIR/" - -echo "Success! Assets are ready in $DIST_DIR" -ls -F "$DIST_DIR" -echo "Tarball: dist/sized-v$VERSION-$(uname -s)-$(uname -m).tar.gz" +tar -czf "$destination.tar.gz" -C "$destination" . +( + cd "$(dirname "$destination")" + archive="$(basename "$destination").tar.gz" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$archive" > "$archive.sha256" + else + shasum -a 256 "$archive" > "$archive.sha256" + fi +) +printf 'Archive: %s.tar.gz\nChecksum: %s.tar.gz.sha256\n' "$destination" "$destination"