Skip to content

Audition 0.3.0

Latest

Choose a tag to compare

@github-actions github-actions released this 05 Sep 18:16
  • Native extensions. audition . in a gem checkout, audition <gem> on an installed gem, app targets, and bundle sweeps now
    report every compiled extension (.bundle/.so) that never
    declares Ractor safety, as a warning: every method such an
    extension defines raises Ractor::UnsafeError on the first
    call from a non-main Ractor, C, Rust, and Zig alike (verified
    on Ruby 4.0.6). The static check (native-extension) is a
    byte scan for the rb_ext_ractor_safe import, so it covers
    precompiled platform gems that ship no sources; an unbuilt
    checkout is scanned at the source level instead (ext/** in
    C, Rust, or Zig), anchored at the declaration or at Init_*,
    where it belongs, and harness trees (fuzz, benches, tests)
    are ignored. The require and Rails probes extend it to
    dependencies (runtime-native-extension): the harness records
    every compiled file the load pulled in and byte-scans those
    too, attributing them to the dependency; Ruby's own archdir
    extensions are left to Ruby, and files the static check
    already reported are not repeated. Declared extensions get an
    info note, since the declaration is the maintainer's
    assertion, not a proof. Cargo and Zig build output and
    .dSYM copies are skipped.
  • The require probe finds squashed entry files. audition activesupport used to fail its dynamic probe with "cannot load
    such file": the gem's entry is active_support, and no rule
    inverts that spelling. When the target ships exactly one
    top-level file under lib/, the probe now requires it after
    the name and its slashed form fail, and reports the last error
    seen rather than the first.
  • Mutable constants learn three shapes, each verified on Ruby
    4.0.6: a bare Object.new sentinel (safe .freeze autofix,
    withheld when the file gives the object singleton methods;
    BasicObject.new, which has no #freeze, gets an
    Object.new.freeze replacement in the unsafe tier),
    Set.new([...]), Set[...], and [...].to_set as containers,
    and Concurrent::Map, which cannot be frozen at all, beside
    the sync primitives. A constant frozen by a bare NAME.freeze
    statement later in the same class body now counts as
    build-then-freeze, so only provably mutable elements are
    reported. The container autofix writes a plain .freeze when
    every element is provably shareable and keeps the deep
    Ractor.make_shareable wrap otherwise; bracket-less
    X = :a, :b gains its brackets. On the 43 Rails files studied
    below the new rules flag all fourteen sites the PRs fixed and
    none after; on current rails/rails they find sixteen more that
    no PR has touched.
  • Advice text stands on Ruby semantics: the class-variable macro
    advice names the singleton-ivar-plus-delegate conversion next
    to class_attribute, the runtime-require advice names the
    class-level macro as the boot-time scope for an optional
    dependency, and no advice cites Rails as the reason for a
    recipe or carries a commit id.
  • Fix knowledge base: fourth pass, the 28 merged "Ractor
    Support" PRs that the commit-message and pickaxe selection of
    the first two passes never saw (freezes, memo deletions, and
    eager requires mention neither Ractor nor ractor). All 45
    commits and their review threads read in full and distilled
    into nine new patterns in docs/rails_core_best_practices.md:
    sentinel and Set constants, benchmark-gated memo deletion,
    constants versus module ivars for registries, settings as
    singleton ivars plus delegate, capture-free boot procs and the
    boot-in-raise-mode gate, share-a-copy and freeze-in-the-setter,
    main-or-local singletons with per-Ractor rebuilds, subsystem
    make_shareable! hooks, and boot-time loading hygiene. audition
    was run on both sides of the 43 files the PRs touch.
  • Fix knowledge base: third pass, the gem dialect. i18n PR 741
    (the first full gem conversion out of the Rails ractorization
    effort) read in full and distilled into three new patterns in
    docs/rails_core_best_practices.md: config class variables
    moving to singleton-class ivars behind delegators, the opt-in
    <gem>/ractorize entry point, and frozen caches degrading to
    recompute-per-call. audition was run on both sides of the PR
    to verify its checks against the conversion; it confirmed the
    fixes, caught a .freeze lost in a rebase and a class
    variable read that still raises from workers (reproduced on
    Ruby 4.0.6), and the class-variable semantics claim in the
    checks' advice was re-verified: reads raise even for
    shareable values.
  • CI-ready. --exit-zero (alias for the new --fail-on never,
    also accepted in .audition.yml) reports every finding but
    always exits 0, the adoption mode other linters ship under the
    same name. --format github now also appends a verdict and
    counts markdown table to the job summary page when GitHub
    Actions provides one, strips ./ prefixes so annotations
    anchor to the PR diff, and works for bundle sweeps: one
    annotation per failing gem plus the readiness line, instead of
    a terminal table.
  • Git-hook-ready. Several .rb file arguments now audit as one
    static target, the shape hook managers pass staged files in
    (lefthook's {staged_files}, pre-commit's filename
    arguments); config, pragmas, and the baseline resolve against
    the working directory. Previously everything after the first
    argument was silently ignored. README gained a CI and git
    hooks section with copy-paste lefthook, pre-commit, and
    GitHub Actions configs.
  • Dogfooding: this repository now runs audition on itself, on
    every commit through lefthook (staged files, static) and on
    every push through a non-blocking CI self-audit with PR
    annotations and a job summary.
  • Dynamic dependency attribution now matches the static
    scanner's exclusion rule: constants whose source lives in a
    vendored or dot directory under the target root (Bundler's
    deployment mode and Actions' bundler-cache put every gem in
    vendor/bundle) count as dependencies, not as the target's
    own code. Found by the very first CI self-audit, which
    attributed the vendored gems to audition itself and flipped
    the verdict from blocked to not_ready.
  • Report rendering split into one class per format
    (Report::Text, Report::Json, Report::Github); the
    Report class keeps only the data, verdict, and counts. The
    Report#to_text/to_json/to_github methods are gone, an API
    change for anyone driving audition programmatically.