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:
- Exit code 0 (success)
- 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():
// 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(())frommain, which exits with code 0; the code does not callstd::process::exit(0) - This behavior is specifically designed for winget compatibility
Exit Code Matrix
Success Cases (Exit Code 0)
| Command | Behavior |
|---|---|
| (no args) | Print help and exit 0 |
--help | Print help and exit 0 |
--version | Print version and exit 0 |
<subcommand> --help | Print subcommand help and exit 0 |
config | Display current config and exit 0 |
config --reset | Reset 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)
| Command | Reason |
|---|---|
invalid-command | Unknown subcommand |
bundle (without --accept-license) | Missing required flag |
setup --dir <nonexistent> | No MSVC installation found |
env --dir <nonexistent> | No MSVC installation found |
download --arch invalid | Invalid architecture |
Testing
Exit code behavior is validated in tests/cli_exit_code_tests.rs:
# Run all exit code tests
cargo test --test cli_exit_code_tests
# Run specific test
cargo test test_no_subcommand_exits_zeroKey Tests
test_no_subcommand_exits_zero- Critical for wingettest_help_flag_exits_zero- Standard behaviortest_version_flag_exits_zero- Standard behaviortest_bundle_without_license_exits_nonzero- Error handlingtest_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:
Always return appropriate exit codes:
- Use
Ok(())for success (exits with 0) - Use
anyhow::bail!()orErr()for errors (exits with 1) - Prefer returning from
mainoverstd::process::exit()
- Use
Add tests for new commands:
- Test success cases exit with 0
- Test error cases exit with non-zero
- Update
cli_exit_code_tests.rsaccordingly
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:
- Single architecture binary: Only the x64 Windows binary (
msvc-kit-x86_64-windows.exe) is uploaded to GitHub Releases - Strict regex matching: The
installers-regexis set to^msvc-kit-x86_64-windows\.exe$to match exactly one file - Sequential workflow: The
update-wingetjob runs only after the GitHub Release is fully created with assets available
If the error "Duplicate installer entry found" occurs:
- Verify that only one
.exefile is attached to the GitHub Release - Check that the
installers-regexdoes not match multiple files - Ensure no manual manifest was submitted to winget-pkgs with the same version