Skip to content

Exit Code Behavior ​

This document describes the exit code behavior of msvc-kit and explains why it matters for package manager compatibility.

Overview ​

msvc-kit follows standard Unix/Windows conventions for exit codes:

  • Exit code 0: Success or help information displayed
  • Exit code 1 (or non-zero): Error occurred

winget Compatibility ​

The Windows Package Manager (winget) has specific requirements for package installers:

Requirement: Zero Exit Code for Help ​

When winget validates a package installer, it runs the executable without arguments and expects:

  1. Exit code 0 (success)
  2. Help or usage information to be printed

This is documented in the winget validation guide.

Implementation in msvc-kit ​

In src/bin/msvc-kit.rs, main():

rust
// Handle the case where no subcommand is provided (for winget compatibility)
let command = match cli.command {
    Some(cmd) => cmd,
    None => {
        // Print help and exit with code 0 for winget validation
        Cli::command().print_help().unwrap();
        return Ok(());
    }
};

Key points:

  • When no subcommand is provided, we print the help text
  • We return Ok(()) from main, which exits with code 0; the code does not call std::process::exit(0)
  • This behavior is specifically designed for winget compatibility

Exit Code Matrix ​

Success Cases (Exit Code 0) ​

CommandBehavior
(no args)Print help and exit 0
--helpPrint help and exit 0
--versionPrint version and exit 0
<subcommand> --helpPrint subcommand help and exit 0
configDisplay current config and exit 0
config --resetReset config and exit 0
list --dir <empty>Show "No installations found" and exit 0
clean --msvc-version <nonexistent>Exit 0 (idempotent operation)

Error Cases (Exit Code != 0) ​

CommandReason
invalid-commandUnknown subcommand
bundle (without --accept-license)Missing required flag
setup --dir <nonexistent>No MSVC installation found
env --dir <nonexistent>No MSVC installation found
download --arch invalidInvalid architecture

Testing ​

Exit code behavior is validated in tests/cli_exit_code_tests.rs:

bash
# Run all exit code tests
cargo test --test cli_exit_code_tests

# Run specific test
cargo test test_no_subcommand_exits_zero

Key Tests ​

  1. test_no_subcommand_exits_zero - Critical for winget
  2. test_help_flag_exits_zero - Standard behavior
  3. test_version_flag_exits_zero - Standard behavior
  4. test_bundle_without_license_exits_nonzero - Error handling
  5. test_setup_without_installation_exits_nonzero - Error handling

CI Validation ​

These tests run automatically in GitHub Actions to ensure:

  • winget compatibility is maintained across changes
  • Exit codes remain consistent
  • Error handling works correctly

See .github/workflows/ci.yml for the CI configuration.

Best Practices ​

When adding new commands or modifying existing ones:

  1. Always return appropriate exit codes:

    • Use Ok(()) for success (exits with 0)
    • Use anyhow::bail!() or Err() for errors (exits with 1)
    • Prefer returning from main over std::process::exit()
  2. Add tests for new commands:

    • Test success cases exit with 0
    • Test error cases exit with non-zero
    • Update cli_exit_code_tests.rs accordingly
  3. Document expected behavior:

    • Add entries to the exit code matrix above
    • Note any special cases or requirements

WinGet Manifest Publishing ​

The release workflow automatically publishes the WinGet manifest using vedantmgoyal2009/winget-releaser@v2.

Avoiding Duplicate Installer Entries ​

The WinGet validation pipeline rejects manifests with duplicate installer entries (same Architecture + InstallerType). To prevent this:

  1. Single architecture binary: Only the x64 Windows binary (msvc-kit-x86_64-windows.exe) is uploaded to GitHub Releases
  2. Strict regex matching: The installers-regex is set to ^msvc-kit-x86_64-windows\.exe$ to match exactly one file
  3. Sequential workflow: The update-winget job runs only after the GitHub Release is fully created with assets available

If the error "Duplicate installer entry found" occurs:

  • Verify that only one .exe file is attached to the GitHub Release
  • Check that the installers-regex does not match multiple files
  • Ensure no manual manifest was submitted to winget-pkgs with the same version

References ​

Released under the MIT License.