Skip to content

Commit f1f877e

Browse files
authored
Add exit-on-failure flag (#43)
* Add --exit-on-failure (-E) flag for CI integration Adds a new flag that causes xcsift to exit with a non-zero exit code when the build status is not "success". This is useful for CI pipelines that need to fail the job when the build fails. * Test & documentation for exit-on-failure flag - Add unit tests for exitOnFailure logic - Add configuration decoding test to verify exit_on_failure parses correctly - Add documentation for exit-on-failure flag
1 parent 60d75b5 commit f1f877e

File tree

8 files changed

+378
-4
lines changed

8 files changed

+378
-4
lines changed

CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ xcodebuild build 2>&1 | xcsift -q
5656
swift build 2>&1 | xcsift --Werror
5757
xcodebuild build 2>&1 | xcsift -W
5858

59+
# Exit on failure - exit with non-zero code if build does not succeed
60+
swift build 2>&1 | xcsift --exit-on-failure
61+
xcodebuild build 2>&1 | xcsift -E
62+
63+
# CI integration - combine --Werror and --exit-on-failure for strict CI builds
64+
# This fails the build if there are any errors, warnings, or test failures
65+
xcodebuild build 2>&1 | xcsift --Werror --exit-on-failure
66+
swift build 2>&1 | xcsift -W -E
67+
5968
# Executable targets - output executable targets generated by the build
6069
# Parses RegisterWithLaunchServices lines from xcodebuild output
6170
xcodebuild build 2>&1 | xcsift --executable
@@ -664,6 +673,9 @@ coverage_path = "" # Custom path to coverage data (empty = auto-detect)
664673
build_info = false # Include per-target build phases and timing
665674
executable = false # Include executable targets (-e)
666675
676+
# Exit behavior
677+
exit_on_failure = false # Exit with failure code if build does not succeed (-E)
678+
667679
# TOON format configuration
668680
[toon]
669681
delimiter = "comma" # "comma", "tab", or "pipe"

Sources/ConfigLoader.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ struct ConfigLoader {
103103
# build_info = false # Include per-target build phases and timing
104104
# executable = false # Include executable targets (-e)
105105
106+
# Exit behavior
107+
# exit_on_failure = false # Exit with failure code if build does not succeed (-E)
108+
106109
# TOON format configuration
107110
# [toon]
108111
# delimiter = "comma" # "comma", "tab", or "pipe"

Sources/ConfigMerger.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct ResolvedConfig: Sendable {
1515
let slowThreshold: Double?
1616
let buildInfo: Bool
1717
let executable: Bool
18+
let exitOnFailure: Bool
1819
let toonDelimiter: TOONDelimiterType
1920
let toonKeyFolding: TOONKeyFoldingType
2021
let toonFlattenDepth: Int?
@@ -39,6 +40,7 @@ enum ConfigMerger {
3940
/// - cliSlowThreshold: Slow threshold from CLI (nil if not set)
4041
/// - cliBuildInfo: Build info flag from CLI
4142
/// - cliExecutable: Executable flag from CLI
43+
/// - cliExitOnFailure: Exit on failure flag from CLI
4244
/// - cliToonDelimiter: TOON delimiter from CLI (nil if not explicitly set)
4345
/// - cliToonKeyFolding: TOON key folding from CLI (nil if not explicitly set)
4446
/// - cliToonFlattenDepth: TOON flatten depth from CLI (nil if not set)
@@ -55,6 +57,7 @@ enum ConfigMerger {
5557
cliSlowThreshold: Double?,
5658
cliBuildInfo: Bool,
5759
cliExecutable: Bool,
60+
cliExitOnFailure: Bool,
5861
cliToonDelimiter: TOONDelimiterType?,
5962
cliToonKeyFolding: TOONKeyFoldingType?,
6063
cliToonFlattenDepth: Int?
@@ -82,6 +85,7 @@ enum ConfigMerger {
8285
let coverageDetails = cliCoverageDetails || (config.coverageDetails ?? false)
8386
let buildInfo = cliBuildInfo || (config.buildInfo ?? false)
8487
let executable = cliExecutable || (config.executable ?? false)
88+
let exitOnFailure = cliExitOnFailure || (config.exitOnFailure ?? false)
8589

8690
// Optional string/numeric values: CLI > config > nil
8791
let coveragePath = cliCoveragePath ?? nonEmptyString(config.coveragePath)
@@ -126,6 +130,7 @@ enum ConfigMerger {
126130
slowThreshold: slowThreshold,
127131
buildInfo: buildInfo,
128132
executable: executable,
133+
exitOnFailure: exitOnFailure,
129134
toonDelimiter: toonDelimiter,
130135
toonKeyFolding: toonKeyFolding,
131136
toonFlattenDepth: toonFlattenDepth

Sources/Configuration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct Configuration: Codable, Sendable {
1616
var coveragePath: String?
1717
var buildInfo: Bool?
1818
var executable: Bool?
19+
var exitOnFailure: Bool?
1920
var toon: TOONConfiguration?
2021

2122
enum CodingKeys: String, CodingKey {
@@ -29,6 +30,7 @@ struct Configuration: Codable, Sendable {
2930
case coveragePath = "coverage_path"
3031
case buildInfo = "build_info"
3132
case executable
33+
case exitOnFailure = "exit_on_failure"
3234
case toon
3335
}
3436

Sources/main.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ struct XCSift: ParsableCommand {
157157
@Flag(name: .long, help: "Include per-target build phases and timing")
158158
var buildInfo: Bool = false
159159

160+
@Flag(
161+
name: [.customShort("E"), .customLong("exit-on-failure")],
162+
help: "Exit with failure code if build does not succeed"
163+
)
164+
var exitOnFailure: Bool = false
165+
160166
@Option(
161167
name: [.customShort("f"), .long],
162168
help: "Output format (json, toon, or github-actions). Default: json. On CI, annotations are auto-appended."
@@ -224,6 +230,7 @@ struct XCSift: ParsableCommand {
224230
cliSlowThreshold: slowThreshold,
225231
cliBuildInfo: buildInfo,
226232
cliExecutable: executable,
233+
cliExitOnFailure: exitOnFailure,
227234
cliToonDelimiter: toonDelimiter,
228235
cliToonKeyFolding: toonKeyFolding,
229236
cliToonFlattenDepth: toonFlattenDepth
@@ -272,6 +279,11 @@ struct XCSift: ParsableCommand {
272279
printExecutables: resolved.executable
273280
)
274281
outputResult(result, resolved: resolved)
282+
283+
// Exit with failure if requested and build did not succeed
284+
if resolved.exitOnFailure && result.status != "success" {
285+
throw ExitCode.failure
286+
}
275287
}
276288

277289
private func generateConfigFile() throws {

Sources/xcsift.docc/Configuration.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ coverage_path = "" # Custom path to coverage data (empty = auto-detect)
6464
build_info = false # Include per-target build phases and timing
6565
executable = false # Include executable targets (-e)
6666

67+
# Exit behavior
68+
exit_on_failure = false # Exit with failure code if build does not succeed (-E)
69+
6770
# TOON format configuration
6871
[toon]
6972
delimiter = "comma" # "comma", "tab", or "pipe"
@@ -113,6 +116,12 @@ flatten_depth = 0 # 0 = unlimited, or positive integer
113116
| `build_info` | bool | `false` | Include per-target build phases and timing |
114117
| `executable` | bool | `false` | Include executable targets |
115118

119+
### Exit Behavior
120+
121+
| Option | Type | Default | Description |
122+
|--------|------|---------|-------------|
123+
| `exit_on_failure` | bool | `false` | Exit with failure code if build does not succeed |
124+
116125
### TOON Configuration
117126

118127
Options in the `[toon]` section:
@@ -211,12 +220,15 @@ For CI, you might want different settings:
211220
# .xcsift.toml for CI
212221
format = "json"
213222
warnings = true
214-
werror = true # Fail on warnings
223+
werror = true # Fail on warnings
224+
exit_on_failure = true # Return non-zero exit code on failure
215225
coverage = true
216226
coverage_details = true
217227
build_info = true
218228
```
219229

230+
**Tip:** Combining `werror = true` and `exit_on_failure = true` ensures your CI pipeline fails on any errors, warnings, or test failures. This is useful for enforcing code quality standards.
231+
220232
## Topics
221233

222234
### Related

Sources/xcsift.docc/Usage.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,29 @@ xcodebuild build 2>&1 | xcsift --quiet
7171
swift build 2>&1 | xcsift -q
7272
```
7373

74+
## Exit Behavior
75+
76+
### `--exit-on-failure`, `-E`
77+
78+
Exit with non-zero code if build does not succeed. Useful for CI pipelines where you want the build step to fail based on xcsift's analysis.
79+
80+
```bash
81+
# Exit with failure if build fails
82+
xcodebuild build 2>&1 | xcsift --exit-on-failure
83+
swift build 2>&1 | xcsift -E
84+
85+
# CI integration: combine with --Werror for strict builds
86+
# Fails if there are any errors, warnings, or test failures
87+
xcodebuild build 2>&1 | xcsift --Werror --exit-on-failure
88+
swift build 2>&1 | xcsift -W -E
89+
```
90+
91+
Build is considered failed when:
92+
- Compiler errors are present
93+
- Linker errors are present
94+
- Test failures are present
95+
- Warnings are present (when `--Werror` is also used)
96+
7497
## Test Analysis Options
7598

7699
### `--slow-threshold`
@@ -269,10 +292,16 @@ xcodebuild build 2>&1 | xcsift -e -w -f toon
269292

270293
# Full build analysis
271294
xcodebuild build 2>&1 | xcsift -f toon -e -w --build-info
295+
296+
# CI integration: strict builds that fail on any issues
297+
xcodebuild build 2>&1 | xcsift --Werror --exit-on-failure
298+
swift test 2>&1 | xcsift -W -E -f toon
272299
```
273300

274301
## Exit Codes
275302

276-
- `0` — Build succeeded
277-
- `1` — Build failed (errors, linker errors, or test failures)
278-
- `1` — Build has warnings (when `--Werror` is used)
303+
- `0` — Build succeeded (or xcsift completed normally without `--exit-on-failure`)
304+
- `1` — Build failed (errors, linker errors, or test failures) when `--exit-on-failure` is used
305+
- `1` — Build has warnings when both `--Werror` and `--exit-on-failure` are used
306+
307+
**Note:** Without `--exit-on-failure`, xcsift always exits with `0` regardless of build status. The exit code reflects xcsift's own execution, not the build result. Use `--exit-on-failure` (`-E`) when you want xcsift's exit code to reflect the build status for CI integration.

0 commit comments

Comments
 (0)