diff --git a/crates/tower-cmd/src/package.rs b/crates/tower-cmd/src/package.rs index b01b3f13..d065bf52 100644 --- a/crates/tower-cmd/src/package.rs +++ b/crates/tower-cmd/src/package.rs @@ -58,17 +58,20 @@ pub async fn do_package(out: &crate::output::Out, _config: Config, args: &ArgMat } Err(err) => { out.error(&format!("Failed to save package: {}", err)); + std::process::exit(1); } } } Err(err) => { spinner.failure(out); out.package_error(err); + std::process::exit(1); } } } Err(err) => { out.package_error(err); + std::process::exit(1); } } } diff --git a/tests/integration/features/cli_package.feature b/tests/integration/features/cli_package.feature new file mode 100644 index 00000000..02a48aa2 --- /dev/null +++ b/tests/integration/features/cli_package.feature @@ -0,0 +1,21 @@ +Feature: CLI Package + As a developer scripting against the Tower CLI + I want `tower package` to exit non-zero when it cannot build a package + So that a broken Towerfile fails my pipeline instead of passing silently + + Scenario: Packaging a Towerfile with no script fails loudly + Given I have a Towerfile that is missing its script + When I run "tower package -d . -o pkg.tar.gz" via CLI + Then the command should exit with a non-zero status + And the output should show "script" + + Scenario: Packaging without any Towerfile fails loudly + When I run "tower package -d . -o pkg.tar.gz" via CLI + Then the command should exit with a non-zero status + And the output should show "Towerfile" + + Scenario: Packaging a valid app succeeds + Given I have a valid Towerfile in the current directory + When I run "tower package -d . -o pkg.tar.gz" via CLI + Then the command should exit with status 0 + And the output should show "Package created successfully" diff --git a/tests/integration/features/steps/cli_steps.py b/tests/integration/features/steps/cli_steps.py index 7332e98b..532805c6 100644 --- a/tests/integration/features/steps/cli_steps.py +++ b/tests/integration/features/steps/cli_steps.py @@ -65,6 +65,27 @@ def step_run_cli_command(context, command): return run_command_with_env(context, command, test_env) +@given("I have a Towerfile that is missing its script") +def step_create_towerfile_missing_script(context): + # Deliberately invalid: `script` is required. Used to check that commands + # fail loudly rather than printing an error and exiting 0. + Path("Towerfile").write_text('[app]\nname = "broken-app"\n') + + +@then("the command should exit with a non-zero status") +def step_check_nonzero_exit(context): + assert ( + context.cli_return_code != 0 + ), f"Expected a non-zero exit, got {context.cli_return_code}. Output: {context.cli_output}" + + +@then("the command should exit with status 0") +def step_check_zero_exit(context): + assert ( + context.cli_return_code == 0 + ), f"Expected exit 0, got {context.cli_return_code}. Output: {context.cli_output}" + + @step('I run "{command}" via CLI with API key') def step_run_cli_command_with_api_key(context, command): """Run a Tower CLI command authenticating via TOWER_API_KEY instead of session.json"""