Skip to content

Exit non-zero when a run fails, is aborted, or cannot parse its arguments [patch] - #170

Merged
matt-edmondson merged 3 commits into
mainfrom
fix/exit-codes
Oct 6, 2026
Merged

matt-edmondson merged 3 commits into
mainfrom
fix/exit-codes

Conversation

@matt-edmondson

@matt-edmondson matt-edmondson commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #144

Before: Main and every verb returned void, so the process exited 0 for a missing directory, an abort, failed deletions and unknown options. Deduplicate -p /data && echo done reported success even when nothing was deleted.

After, measured against the built dll:

Command Exit code
Scan -p /nonexistent 1
Scan -p <a file> 1
Scan --nope 2
--help 0
Scan -p <dir> 0
echo n | Deduplicate -p <dir with duplicates> 1

How:

  • Main returns int and goes through Program.Run(parser, args), which uses ParseArguments(...).MapResult(...). A parse failure returns 2. Asking for help or the version returns 0.

  • BaseVerb.Run() returns the exit code, and ICommand.Execute (the menu) discards it. BaseVerb<T>.Run returns 1 in two cases:

    • the path prompt is aborted
    • the root is missing or isn't a directory, checked by the new FileScanner.IsScannableDirectory before the verb runs

    Otherwise it returns the verb's ExitCode, which starts at 0. Deduplicate sets it to 1 when the user declines or a deletion fails. Menu opts out of the root check.

  • Scan, DryRun and Stats are unchanged. Putting the exit code in their bodies added lines inside the scan pipeline that all four verbs copy (Scan, DryRun, Deduplicate and Stats each copy the path prompt and the scan→hash→group pipeline, so every fix to that path must be made four times #157), and SonarCloud failed the first push on 10.5% duplication on new code.

  • ExitCodeTests covers each case, including a real command line through Program.Run. With BaseVerb<T>.Run and Main forced back to returning 0, six of them fail.

  • The deletion-failure test is inconclusive as root, the same as the existing ACopyThatCannotBeDeletedIsReportedAndTheRunCarriesOn. I ran both as nobody and both passed.

Not covered: an unknown verb (FileDeduplicator Nope) still falls through to the default Menu verb rather than exiting 2. That's the path #161 is about.

This branches from main, separately from #169 (for #145). Both touch BaseVerb, so whichever merges second may need a small merge.

Tests: 77 total, 74 passed, 3 skipped (the permission-staging tests, which can't stage the failure as root).

🤖 Generated with Claude Code

https://claude.ai/code/session_01U5RBnMaTjZdMDRNttjeZ3u

claude added 2 commits October 6, 2026 18:27
…ents [patch]

Main and every verb returned void, so the process exited 0 for a missing
directory, an abort, failed deletions and unknown options alike. Main now
returns the verb's exit code through MapResult: 0 on success (and for
--help/--version), 1 when the root is missing or not a directory, the user
aborts at a prompt, or Deduplicate finishes with deletion errors, and 2
when the arguments cannot be parsed. FileScanner.TryScanForFiles tells a
root that could not be scanned apart from an empty one.

Fixes #144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5RBnMaTjZdMDRNttjeZ3u
…the copied pipeline

SonarCloud failed the PR on 10.5% duplication on new code: the new
return-value lines sat inside the scan pipeline that Scan, DryRun, Stats
and Deduplicate each carry a copy of (#157). BaseVerb<T>.Run now checks the
root with FileScanner.IsScannableDirectory before the verb runs and returns
the verb's ExitCode, which Deduplicate sets when the user declines or a
deletion fails. Scan, DryRun and Stats go back to their main versions. The
exit-code tests share their console capture and assertions instead of
repeating them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U5RBnMaTjZdMDRNttjeZ3u
@sonarqubecloud

sonarqubecloud Bot commented Oct 6, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 86d6b67 into main Oct 6, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/exit-codes branch October 6, 2026 23:00
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.

Every verb exits with code 0 even when the directory is missing, deletions fail, or the arguments are invalid

2 participants