First release to ship prebuilt binaries, and the first to carry a
working passwd in them: 0.2.1 and earlier had no assets at all, and the
archive the new pipeline first produced was built without PAM.
Closes#221.
The README and the dist config both claimed musl is unavailable because
crypt(3) is not part of the static musl target. That is wrong: musl
implements crypt() inside libc —
$ nm .../x86_64-unknown-linux-musl/.../libc.a | grep ' T crypt'
0000000000000000 T crypt
The link failure comes from `#[link(name = "crypt")]` emitting -lcrypt,
which finds no libcrypt.a in Rust's musl sysroot and falls through to the
host's glibc libxcrypt. Gating that attribute on the target env is enough
to produce a working static binary.
The genuine blocker is PAM: it dlopens its modules, which a fully static
binary cannot do, so a musl artifact would ship without interactive
password changes. State that instead, and point at #224.
release.yml is produced by dist from dist-workspace.toml, and the 'plan'
job checks the two agree. Renovate has been bumping action and runner
versions directly in that file, which makes every such PR fail:
release.yml has out of date contents and needs to be regenerated
help: run 'dist init' to update the file
Both open Renovate PRs (#217, #218) touch only that file and cannot be
merged as-is. Versions in it move by upgrading cargo-dist-version and
regenerating, so exclude the path from Renovate.
`pam` is not a default cargo feature, so every build path except the e2e
image compiled the interactive change path out of `passwd`. The tool
could report and lock accounts but not change a password:
$ passwd alice
shadow-rs: PAM support is not compiled in — cannot change password
interactively
That covered the release archive, `make install` (the default install of
14 standalone binaries) and `make install-multicall`. Only the e2e image
passed --features pam, and its assertions stop at `passwd -S`, which
works without PAM — hence nobody noticed.
Enable the feature in the release build and in both Makefile targets, and
install libpam0g-dev on the release runner. The PAM headers are already a
documented build requirement.
Add an e2e assertion that the installed passwd is linked against libpam,
so this cannot regress silently again.
Closes#220.
* ci: reject AI tooling artifacts in pull requests
The project's public face carries no AI tooling attribution, but nothing
enforced it: a session trailer reached a merged commit message and a pull
request body before anyone noticed.
Add a check that runs on pull requests and inspects only what the PR
changes — commit messages, PR title and body, added lines, and any
attempt to track the local-only guidance and review-export files that
are meant to stay gitignored. History is untouched and unrelated files
are not scanned. The clean-room audit records are exempt: they must
describe their own methodology to serve as compliance evidence.
Also drop the tooling credits from README and CHANGELOG.
* ci: fix base-ref fetch in the artifact check
`--depth=0` is rejected by git, and the extra fetch was redundant with
the full-history checkout. Fetch the base branch plainly and diff against
FETCH_HEAD.
Releases carried no binaries (v0.2.1 shipped zero assets). Add dist
(formerly cargo-dist) 0.32.0 to build and upload them on tag push.
Scope: only the workspace root package is distributed, producing the
single shadow-rs multicall binary that provides all 14 tools via argv[0]
— the same layout as `make install-multicall`. The 14 per-tool crates
carry `dist = false` so a release is one archive per target rather than
fifteen. `[dist.binaries]` pins the archive to `shadow-rs`, keeping the
feature-gated `shadow-rs-completions` helper out of it.
Targets are glibc-only for now: shadow-core::crypt links crypt(3) via
`#[link(name = "crypt")]`, and the self-contained musl target provides
no libcrypt, so an x86_64-unknown-linux-musl cross-build fails at link
time. (Alpine CI passes only because it installs a system libcrypt.)
`[dist.dependencies.apt]` installs libcrypt-dev on the runner for the
same reason.
`[profile.dist]` inherits the hardened release profile, so published
binaries keep LTO, stripped symbols and panic = "abort".
README documents deploying the archive (symlinks + setuid) and why musl
is not published yet.
Verified locally: `dist plan` and `dist build --artifacts=local` produce
uu_shadow-x86_64-unknown-linux-gnu.tar.gz containing shadow-rs plus
README/CHANGELOG/LICENSE and a .sha256; the extracted binary reports
`shadow-rs (uutils shadow-rs) 0.2.1`.
Closes#207.
Claude-Session: https://claude.ai/code/session_011f42R6SMAfQXufTgdjSuhU
Without a version field, `cargo publish` fails for all crates that
depend on shadow-core:
all dependencies must have a version requirement specified when publishing.
dependency `shadow-core` does not specify a version
Add version = "0.2.0" to all three shadow-core dependency declarations
in Cargo.toml (workspace.dependencies, [dependencies], and dev-dependencies)
to allow `cargo publish` to work correctly.
Renovate's single-package update (#198) could not land this: clap_derive
4.6.4 requires syn >=3.0.2 while the lockfile pinned syn 3.0.0 (via
thiserror-impl), so the conservative 'cargo update -p clap --precise'
had no solvable resolution. Update both together (clap 4.6.3 -> 4.6.4,
syn 3.0.0 -> 3.0.3). cargo deny check passes with the syn skip added in
#194.