Skip to content

Adopt Spacecraft Software Standard posture: GPL-3.0-or-later + REUSE#9

Merged
UnbreakableMJ merged 1 commit into
mainfrom
gpl-reuse-migration
Jun 22, 2026
Merged

Adopt Spacecraft Software Standard posture: GPL-3.0-or-later + REUSE#9
UnbreakableMJ merged 1 commit into
mainfrom
gpl-reuse-migration

Conversation

@UnbreakableMJ

@UnbreakableMJ UnbreakableMJ commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Commits the in-progress working-tree migration that moves copy from MIT-only to the Spacecraft Software Standard licensing/compliance posture, while preserving upstream cpx MIT attribution. 19 files modified, 6 added (+1253/−243).

Licensing

  • Fork modifications relicensed to GPL-3.0-or-later (Cargo.toml license, root LICENSE); upstream MIT preserved in LICENSES/MIT.txt.
  • REUSE 3.3 compliance: LICENSES/{GPL-3.0-or-later,MIT}.txt, REUSE.toml, NOTICE.md, CREDITS.md, and per-file SPDX headers.

Agent context

  • New AGENTS.md (generic-agent source) + CLAUDE.md rewritten as its strict superset — build/test/lint, architectural invariants, packaging, release workflow.
  • Added a daily-drive install note to both: cargo install --path . --locked~/.cargo/bin/copy, runnable from any directory.

Source / docs

  • Accompanying updates across config (schema.rs +192, loader.rs), core/copy.rs, preprocess.rs, preserve.rs, both READMEs, docs/, and packaging metadata. Integration tests grew (intergration.rs +83).

Verification (local, all green)

  • cargo build, cargo fmt --check, cargo clippy --all-targets -- -D warnings
  • cargo test → 69 unit + 68 integration pass
  • reuse lint → REUSE 3.3 compliant (54/54 files)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Updated license badges to GPL-3.0-or-later
    • Added documentation for optional SELinux support
  • License & Compliance

    • Project relicensed to GPL-3.0-or-later with SPDX compliance headers and proper attribution
  • New Features

    • Configuration files now merge from multiple sources (system, user, project level) instead of using the first found
  • Bug Fixes

    • Invalid configuration files now report errors instead of being silently ignored
    • Backup and extended attribute operation failures now properly propagate errors
  • Tests

    • Added integration tests for configuration merging and error handling scenarios

Migrate `copy` from MIT-only to the Spacecraft Software Standard
licensing/compliance posture, preserving upstream cpx MIT attribution.

- Licensing: fork modifications are now GPL-3.0-or-later (Cargo.toml
  `license`, root LICENSE); upstream MIT is preserved in LICENSES/MIT.txt.
- REUSE 3.3 compliance: add LICENSES/{GPL-3.0-or-later,MIT}.txt, REUSE.toml,
  NOTICE.md, CREDITS.md, and per-file SPDX headers. `reuse lint` passes
  (54/54 files with copyright + license info).
- Agent context: add AGENTS.md (generic-agent source) and rewrite CLAUDE.md
  as its strict superset; both document build/test/lint, architectural
  invariants, packaging, and the release workflow. Add a daily-drive install
  note (`cargo install --path . --locked` -> ~/.cargo/bin/copy, runnable from
  any directory) to the shared Build/Test/Lint section of both.
- Accompanying source and doc updates across config (schema/loader), core
  copy, preprocess, preserve, both READMEs, docs, and packaging metadata.

Verified green locally: cargo build, fmt --check, clippy --all-targets
-D warnings, cargo test (69 unit + 68 integration), and reuse lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR migrates the project license from MIT to GPL-3.0-or-later with REUSE compliance across all source, packaging, and documentation files. Independently, it rewrites config loading from first-match to ordered multi-layer merging via new PartialConfig structs, propagates config errors to callers, introduces a shared AtomicBool abort flag in the parallel copy path, tightens error handling for backup/force-remove/xattr operations, and removes the inode-group HashMap from preprocessing.

Changes

License Migration and Project Documentation

Layer / File(s) Summary
Core license files and REUSE compliance
LICENSE, LICENSES/GPL-3.0-or-later.txt, LICENSES/MIT.txt, Cargo.toml, REUSE.toml
LICENSE is replaced with full GPLv3 text, LICENSES/GPL-3.0-or-later.txt is introduced, LICENSES/MIT.txt is populated for upstream attribution, Cargo.toml license field is updated, and REUSE.toml declares SPDX coverage for all repository file globs.
Packaging metadata license updates
guix.scm, nix/package.nix, packaging/aur/PKGBUILD
Declared license changed from MIT/expat to GPL-3.0-or-later in all three packaging files; SPDX headers added; LICENSES/MIT.txt installed alongside other license artifacts; SELinux feature flag comments updated.
Project posture, credits, and README updates
CREDITS.md, NOTICE.md, CONTRIBUTING.md, README.md, README_CRATES.md
CREDITS.md and NOTICE.md created with fork attribution and warranty disclaimer; READMEs update the license badge, add an Optional SELinux Support section, and rewrite Project Posture/License/Credits sections; CONTRIBUTING.md adds a GPL-3.0-or-later license bullet.
Agent and contributor guidance documents
AGENTS.md, CLAUDE.md
AGENTS.md introduced with project identity, build/lint commands, architectural invariants, forbidden patterns, and standards compliance. CLAUDE.md rewritten as a strict superset of AGENTS.md adding Claude-specific tool preferences, MCP server expectations, and sync notes.

Config Layer Merging and Copy Error Hardening

Layer / File(s) Summary
PartialConfig structs and merge_partial method
src/config/schema.rs
New public Partial*Config structs mirror the full config hierarchy with Option fields; Config::merge_partial conditionally updates sub-configs and appends exclude patterns; two unit tests verify non-overwrite of defaults and pattern appending.
Config loader rewrite: ordered multi-file merge
src/config/loader.rs, src/cli/args.rs, src/config/config_command.rs
load_config_file parses into PartialConfig then merges into Config::default(); load_config is rewritten to iterate system→user→project paths and return ConfigResult<Config>; load_config_if_needed and show_config now propagate errors via ?.
Parallel copy: AtomicBool abort flag and error propagation
src/core/copy.rs, src/core/fast_copy.rs
Rayon parallel path switches to par_iter().try_for_each with a shared failure_abort AtomicBool; copy_core gains an optional abort parameter checked on entry and in the write loop; backup/force-remove failures now return CopyError::CopyFailed; fast_copy propagates remove_file errors.
Inode-group HashMap removal and xattr error propagation
src/utility/preprocess.rs, src/utility/preserve.rs
inode_groups HashMap and its mutation are removed from process_entry and all call sites; preserve_xattr changed to propagate xattr::set failures via ? instead of discarding them.
Integration tests and configuration docs
tests/intergration.rs, docs/configuration.md
Three new integration tests cover backup failure reporting, multi-layer config merging, and invalid config error output; docs/configuration.md documents that unreadable/unparseable config files now cause a configuration error and stop.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant load_config
  participant merge_ordered_config_files
  participant load_config_file
  participant Config

  rect rgba(70, 130, 180, 0.5)
    Note over load_config,Config: Config layer merge (new behavior)
    Caller->>load_config: load_config()
    load_config->>merge_ordered_config_files: get ordered config paths
    merge_ordered_config_files-->>load_config: [system, user, project] (existing paths only)
    loop each path
      load_config->>load_config_file: load_config_file(path)
      load_config_file-->>load_config: PartialConfig
      load_config->>Config: merge_partial(partial)
    end
    load_config-->>Caller: ConfigResult<Config>
  end
Loading
sequenceDiagram
  participant copy_parallel
  participant failure_abort as failure_abort: AtomicBool
  participant TaskN as copy_core (task N)
  participant TaskM as copy_core (task M)

  rect rgba(180, 70, 70, 0.5)
    Note over copy_parallel,TaskM: Parallel copy with cross-task abort (new behavior)
    copy_parallel->>failure_abort: initialize(false)
    copy_parallel->>TaskN: spawn, pass &failure_abort
    copy_parallel->>TaskM: spawn, pass &failure_abort
    TaskN->>failure_abort: check on entry → false, proceed
    TaskN-->>copy_parallel: Err(CopyFailed)
    copy_parallel->>failure_abort: store(true)
    TaskM->>failure_abort: check in write loop → true
    TaskM-->>copy_parallel: Err(Interrupted "Operation aborted")
    copy_parallel-->>copy_parallel: return CopyError::CopyFailed
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hopping from MIT to GNU's embrace,
The license swapped with copyright grace.
Configs now merge, layer by layer they blend,
Parallel tasks abort when errors descend.
HashMaps removed, xattrs now propagate right—
A forked cpx soaring to GPL light! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Adopt Spacecraft Software Standard posture: GPL-3.0-or-later + REUSE' directly and clearly summarizes the main changes: migration to GPL-3.0-or-later licensing with REUSE compliance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gpl-reuse-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 1-120: The AGENTS.md file at the repository root is not covered by
any REUSE annotation, even though it is documented in CLAUDE.md as a file to
keep in sync. Find the REUSE.toml file and locate the annotation block that
covers root documentation files like CLAUDE.md and README.md, then add
"AGENTS.md" to the path array in that annotation block so that AGENTS.md
receives the same SPDX headers and licensing coverage as the other root
documentation files.

In `@src/config/loader.rs`:
- Around line 49-72: The fix requires updating both
`merge_ordered_config_files()` and `find_config_files()` to propagate permission
errors instead of silently collapsing them. Replace all instances of `.exists()`
with `.try_exists().map_err(ConfigError::Io)?` in both functions. Change
`merge_ordered_config_files()` to return `ConfigResult<Vec<PathBuf>>` instead of
`Vec<PathBuf>`, and update its caller `load_config()` to handle the new return
type using the `?` operator. Apply the same `.try_exists()` change to
`find_config_files()` which is public and used by `show_config()`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e22c099c-78e5-42e0-a29e-8cb4efa5f354

📥 Commits

Reviewing files that changed from the base of the PR and between 53ec82c and 7891be0.

📒 Files selected for processing (25)
  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • CREDITS.md
  • Cargo.toml
  • LICENSE
  • LICENSES/GPL-3.0-or-later.txt
  • LICENSES/MIT.txt
  • NOTICE.md
  • README.md
  • README_CRATES.md
  • REUSE.toml
  • docs/configuration.md
  • guix.scm
  • nix/package.nix
  • packaging/aur/PKGBUILD
  • src/cli/args.rs
  • src/config/config_command.rs
  • src/config/loader.rs
  • src/config/schema.rs
  • src/core/copy.rs
  • src/core/fast_copy.rs
  • src/utility/preprocess.rs
  • src/utility/preserve.rs
  • tests/intergration.rs

Comment thread CLAUDE.md
Comment on lines +1 to +120
<!--
SPDX-FileCopyrightText: 2026 Mohamed Hammad <Mohamed.Hammad@SpacecraftSoftware.org>
SPDX-License-Identifier: GPL-3.0-or-later
-->

# CLAUDE.md — copy

This file is a strict superset of `AGENTS.md`. Keep the shared sections in sync
when project invariants, commands, packaging, or release workflow change.

## Project Identity

`copy` is a Linux-first Rust 2024 CLI that replaces `cp` with parallel copying,
resume support, reflinks, symlink/hardlink modes, and configurable preservation
behavior. It is a Spacecraft Software-maintained fork of upstream `cpx`; upstream
MIT attribution is preserved in `LICENSES/MIT.txt`, and fork modifications are
distributed under GPL-3.0-or-later.

## Build, Test, Lint

- Build: `cargo build`
- Release build: `cargo build --release`
- Test: `cargo test`
- Integration tests only: `cargo test --test intergration`
- One test by name: `cargo test test_copy_single_file`
- Lint: `cargo clippy --all-targets -- -D warnings`
- Format check: `cargo fmt --check`
- REUSE compliance: `nix-shell -p reuse --run "reuse lint"`
- Optional SELinux feature build: `cargo build --features selinux-support` after installing libselinux development headers that provide `selinux/selinux.h`
- Daily-drive install: always build and install the latest binary locally so the user can run `copy` from any directory — `cargo install --path . --locked` puts it on `PATH` at `~/.cargo/bin/copy`. Re-run after changes so the daily driver stays current.

CI runs build, test, clippy with warnings denied, and format check. Keep all four
green before handing work back.

## Architectural Invariants

- The binary in `src/main.rs` is intentionally thin: parse args, validate, set the signal abort flag, then dispatch to `copy()` or `multiple_copy()`.
- Copying is a two-phase pipeline: build a complete `CopyPlan` in `utility/preprocess.rs`, then execute it in `core/copy.rs`.
- `copy` is the root action. `config` is the only subcommand and is special-cased in `CLIArgs::parse()` because greedy source positionals would otherwise swallow `copy config show`.
- Config precedence is defaults → system → user → project → CLI. Invalid discovered config files are errors, not ignored fallbacks. `--config PATH` means defaults → that file → CLI.
- `CopyOptions::none()` and `CopyOptions::from_config()` must stay in sync when adding fields.
- `preprocess.rs` decides whether each item becomes a file, symlink, hardlink, resume skip, or directory task. Do not duplicate that routing in execution.
- Directory creation is sequential. Non-interactive file copies use a Rayon thread pool sized by `options.parallel`; `--interactive` is sequential.
- `copy_core()` tries hardlink preservation, reflink, Linux `copy_file_range`, then buffered fallback.
- Worker failures request cooperative cancellation for remaining parallel work. User SIGINT/SIGTERM uses the separate `options.abort` flag and maps to exit code 130 in `main.rs`.
- `README.md` and `README_CRATES.md` must stay in sync for user-facing behavior, install instructions, licensing, and release references.

## Forbidden Patterns

- Do not ignore filesystem errors in production copy paths. Backup creation, destination removal, xattr preservation, and destination creation failures must propagate.
- Do not print progress, logs, or diagnostics to stdout when stdout is data. Current code is not fully SFRS-compliant yet; do not make this worse.
- Do not add `unwrap()` or `expect()` in production paths for user input, filesystem state, config parsing, or CLI parsing.
- Do not make `selinux-support` a default feature; it requires system headers.
- Do not rename `tests/intergration.rs`; the misspelling is part of existing command/docs references.
- Do not remove upstream MIT attribution or `LICENSES/MIT.txt`.
- Do not touch unrelated dirty worktree files. In particular, inspect existing changes before editing `CLAUDE.md` or other root docs.

## Environment Expectations

- Rust stable with edition 2024 support; README documents Rust 1.85 or later.
- `cargo`, `rustc`, `clippy`, and `rustfmt` are expected.
- Nix may be available for ephemeral tooling such as `reuse`; prefer `nix-shell -p reuse --run "reuse lint"` over permanent installs.
- Default builds need no SELinux system library. All-features builds require libselinux development headers.
- GNU compatibility scripts in `tests/gnu/*.sh` are standalone and require a built `copy` binary on `PATH`; they are not part of `cargo test`.

## Where To Look

- CLI args and validation: `src/cli/args.rs`
- Config schema and merge behavior: `src/config/schema.rs`, `src/config/loader.rs`
- Copy planning: `src/utility/preprocess.rs`
- Copy execution and cancellation: `src/core/copy.rs`
- Linux fast path: `src/core/fast_copy.rs`
- Preservation logic: `src/utility/preserve.rs`
- Error types: `src/error.rs`
- Integration tests: `tests/intergration.rs`
- Standalone GNU compatibility scripts: `tests/gnu/`
- Packaging: `nix/package.nix`, `guix.scm`, `packaging/aur/PKGBUILD`

## Release Notes For Agents

Version bumps are manual. Update `Cargo.toml`, `Cargo.lock`, `nix/package.nix`,
`guix.scm`, `packaging/aur/PKGBUILD`, `packaging/aur/.SRCINFO`, and release
references in both READMEs. The AUR source tarball hash exists only after the
GitHub tag is created, so use `SKIP` during the bump and fill the real hash in
a follow-up commit.

## Standards Compliance

This project is moving toward the Spacecraft Software CLI Standard but still has
legacy `cp`-compatible behavior. The `spacecraft-cli-standard` and
`spacecraft-agentic-cli` skills are authoritative for new CLI surface design;
this file records project-specific invariants and current exceptions only.

## Skills Referenced

- `spacecraft-standard` — Spacecraft Software project standard, licensing, posture, REUSE, time/unit policy.
- `microsoft-rust-guidelines` — required before writing or modifying Rust code.
- `spacecraft-cli-standard` — structural SFRS rules for future CLI surface work.
- `spacecraft-agentic-cli` — agent-facing UX and context-file guidance.
- `spacecraft-cli-preference` — preferred command-line tools, such as `rg`, `fd`, `bat`, `jaq`.
- `spacecraft-cli-shell` — shell syntax and portability checks for command snippets and scripts.
- `spacecraft-missing-pkg` — use when a needed tool is missing; prefer ephemeral Nix/Guix runners.

## MCP Servers Expected

No project-specific MCP server is implemented yet. Do not assume `copy mcp`,
`copy schema`, or `copy describe` exists unless implementing SFRS work explicitly.

## Tool Preferences For Claude

- Use `rg` for search and `fd` for file discovery.
- Use `cargo test` as the baseline; `cargo nextest` is not currently required by the repo.
- Use `nix-shell -p reuse --run "reuse lint"` for REUSE checks when `reuse` is not on `PATH`.
- Avoid broad auto-formatting outside Rustfmt-managed Rust files unless the task explicitly calls for it.

## Notes For Claude Specifically

- Treat `AGENTS.md` as the generic-agent source. If project facts change, update both files in the same patch.
- Claude Code may load skills, but this repo should not rely on Claude-only tooling for ordinary development.
- Do not add Claude-specific TODO markers unless the user asks for a Claude workflow.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify AGENTS.md is covered by REUSE.toml annotations

# Check if AGENTS.md is covered by any REUSE annotation
rg -A 15 '\[\[annotations\]\]' REUSE.toml | grep -E '(AGENTS\.md|docs/\*\.md)'

# Run reuse lint to confirm full compliance (expects 54/54 files)
# Note: reuse command may not be available; use nix-shell if needed
if command -v reuse &> /dev/null; then
  reuse lint
else
  echo "reuse not on PATH; try: nix-shell -p reuse --run 'reuse lint'"
fi

Repository: UnbreakableMJ/copy

Length of output: 139


🏁 Script executed:

#!/bin/bash
# Find AGENTS.md in the repository
fd -t f 'AGENTS\.md' .

# Display the relevant section of REUSE.toml with context
echo "=== Full REUSE.toml annotations section ==="
rg -A 20 '\[\[annotations\]\]' REUSE.toml

# Check the structure of docs directory if it exists
echo "=== docs directory structure ==="
if [ -d "docs" ]; then
  ls -la docs/ | head -20
else
  echo "No docs/ directory found"
fi

Repository: UnbreakableMJ/copy

Length of output: 2006


Add AGENTS.md to REUSE.toml annotations.

AGENTS.md exists at the repository root but is not covered by any REUSE annotation. The docs/*.md glob in the third annotation block covers only files within the docs/ directory, not root-level files. Since AGENTS.md is documented in CLAUDE.md as a file to keep in sync (line 10), it should be explicitly added to the annotation covering root documentation files.

Add "AGENTS.md" to the path array in the annotation block that covers CLAUDE.md, README.md, and other root documentation.

🧰 Tools
🪛 LanguageTool

[style] ~51-~51: Consider using a shorter alternative to avoid wordiness.
Context: ...is not fully SFRS-compliant yet; do not make this worse. - Do not add unwrap() or expect() ...

(MADE_IT_JJR)


[style] ~54-~54: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... feature; it requires system headers. - Do not rename tests/intergration.rs; the...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~55-~55: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... of existing command/docs references. - Do not remove upstream MIT attribution or ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~56-~56: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...IT attribution or LICENSES/MIT.txt. - Do not touch unrelated dirty worktree file...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~100-~100: Ensure spelling is correct
Context: ...-line tools, such as rg, fd, bat, jaq. - spacecraft-cli-shell — shell synta...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~113-~113: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...s not currently required by the repo. - Use nix-shell -p reuse --run "reuse lint"...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` around lines 1 - 120, The AGENTS.md file at the repository root is
not covered by any REUSE annotation, even though it is documented in CLAUDE.md
as a file to keep in sync. Find the REUSE.toml file and locate the annotation
block that covers root documentation files like CLAUDE.md and README.md, then
add "AGENTS.md" to the path array in that annotation block so that AGENTS.md
receives the same SPDX headers and licensing coverage as the other root
documentation files.

Source: Learnings

Comment thread src/config/loader.rs
Comment on lines +49 to +72
fn merge_ordered_config_files() -> Vec<PathBuf> {
let mut paths = Vec::new();

#[cfg(unix)]
{
return config;
let system = PathBuf::from("/etc/copy/copyconfig.toml");
if system.exists() {
paths.push(system);
}
}

if let Some(config_dir) = dirs::config_dir() {
let user = config_dir.join("copy").join("copyconfig.toml");
if user.exists()
&& let Ok(config) = load_config_file(&user)
{
return config;
if user.exists() {
paths.push(user);
}
}

#[cfg(unix)]
{
let system = PathBuf::from("/etc/copy/copyconfig.toml");
if system.exists()
&& let Ok(config) = load_config_file(&system)
{
return config;
}
let project = PathBuf::from("./copyconfig.toml");
if project.exists() {
paths.push(project);
}

Config::default()
paths

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'fn merge_ordered_config_files|\.exists\(|try_exists\(' src/config/loader.rs
rg -n -C2 'cannot be read or parsed|not silently ignore|Defaults → System Config → User Config → Project Config → CLI Flags' docs/configuration.md

Repository: UnbreakableMJ/copy

Length of output: 1785


🏁 Script executed:

# Check which function is used in load_config()
rg -n -A10 'fn load_config' src/config/loader.rs

# Find all usages of both functions
rg -n 'merge_ordered_config_files\|find_config_files' src/config/loader.rs

# Check if find_config_files is used elsewhere in the codebase
rg -r 'find_config_files\|merge_ordered_config_files' src/ --type rs

Repository: UnbreakableMJ/copy

Length of output: 930


🏁 Script executed:

# Find all usages of both functions without type filter
rg -n 'merge_ordered_config_files\|find_config_files' src/

# Get the full loader.rs file to see context
wc -l src/config/loader.rs
head -80 src/config/loader.rs

Repository: UnbreakableMJ/copy

Length of output: 2182


🏁 Script executed:

# Check if find_config_files is exported from config module
rg -n 'find_config_files\|pub.*mod' src/config/

# Check all usages of find_config_files across entire codebase
rg -n 'find_config_files' src/ --glob '*.rs' -i

# Check what's exported from the config module
cat src/config/mod.rs

Repository: UnbreakableMJ/copy

Length of output: 346


🏁 Script executed:

# Check how find_config_files is used in config_command.rs
sed -n '80,95p' src/config/config_command.rs

# Show more context around that usage
sed -n '75,100p' src/config/config_command.rs

Repository: UnbreakableMJ/copy

Length of output: 1351


🏁 Script executed:

# Check the complete config_command.rs to understand find_config_files usage
rg -n 'fn.*config' src/config/config_command.rs | head -20

# Check if there are other callers of load_config or merge_ordered_config_files
rg -n 'load_config\|merge_ordered_config_files' src/ --glob '*.rs' -i

# Verify the coding guideline requirement about precedence
rg -n 'defaults.*system.*user.*project' docs/ -i -A2 -B2

Repository: UnbreakableMJ/copy

Length of output: 492


Expand .try_exists() fix to both config discovery functions; proposed diff is incomplete.

merge_ordered_config_files() and the public find_config_files() both use Path::exists(), which silently collapses permission errors to false. Per coding guidelines, invalid discovered config files must be errors, not silent fallbacks.

This affects two paths:

  1. load_config() path: merge_ordered_config_files() must return ConfigResult<Vec<PathBuf>> and propagate errors via try_exists().
  2. find_config_files() path: Also public and used by show_config(). Must propagate metadata errors so unreadable paths aren't hidden from the user.

The proposed diff in the original comment only fixes merge_ordered_config_files(). Both functions require the same .exists().try_exists().map_err(ConfigError::Io)? change.

Extended fix scope

Both find_config_files() (line 6) and merge_ordered_config_files() (line 49) must be updated. The latter's caller load_config() (line 37) must also propagate the result type change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/loader.rs` around lines 49 - 72, The fix requires updating both
`merge_ordered_config_files()` and `find_config_files()` to propagate permission
errors instead of silently collapsing them. Replace all instances of `.exists()`
with `.try_exists().map_err(ConfigError::Io)?` in both functions. Change
`merge_ordered_config_files()` to return `ConfigResult<Vec<PathBuf>>` instead of
`Vec<PathBuf>`, and update its caller `load_config()` to handle the new return
type using the `?` operator. Apply the same `.try_exists()` change to
`find_config_files()` which is public and used by `show_config()`.

Source: Coding guidelines

@UnbreakableMJ UnbreakableMJ merged commit 2f58900 into main Jun 22, 2026
2 checks passed
@UnbreakableMJ UnbreakableMJ deleted the gpl-reuse-migration branch June 22, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant