fun_ci 1.2.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +236 -0
- data/README.md +182 -59
- data/lib/fun_ci/agent/age.rb +15 -0
- data/lib/fun_ci/agent/command_support.rb +27 -0
- data/lib/fun_ci/agent/commands.rb +27 -0
- data/lib/fun_ci/agent/context.rb +10 -0
- data/lib/fun_ci/agent/digest.rb +49 -0
- data/lib/fun_ci/agent/events.rb +47 -0
- data/lib/fun_ci/agent/events_command.rb +61 -0
- data/lib/fun_ci/agent/evidence_text.rb +46 -0
- data/lib/fun_ci/agent/exit_code.rb +11 -0
- data/lib/fun_ci/agent/git.rb +32 -0
- data/lib/fun_ci/agent/live_pipeline.rb +35 -0
- data/lib/fun_ci/agent/options.rb +75 -0
- data/lib/fun_ci/agent/output.rb +81 -0
- data/lib/fun_ci/agent/report_stage.rb +71 -0
- data/lib/fun_ci/agent/reports.rb +47 -0
- data/lib/fun_ci/agent/run_report.rb +30 -0
- data/lib/fun_ci/agent/runs_command.rb +34 -0
- data/lib/fun_ci/agent/runs_text.rb +27 -0
- data/lib/fun_ci/agent/snapshots.rb +39 -0
- data/lib/fun_ci/agent/stage_summary.rb +30 -0
- data/lib/fun_ci/agent/status_command.rb +36 -0
- data/lib/fun_ci/agent/status_json.rb +27 -0
- data/lib/fun_ci/agent/status_text.rb +37 -0
- data/lib/fun_ci/agent/system_clock.rb +11 -0
- data/lib/fun_ci/agent/verdict.rb +32 -0
- data/lib/fun_ci/agent/wait_command.rb +71 -0
- data/lib/fun_ci/agent/waiting.rb +32 -0
- data/lib/fun_ci/agent/why_command.rb +50 -0
- data/lib/fun_ci/agent/why_json.rb +35 -0
- data/lib/fun_ci/agent/why_text.rb +43 -0
- data/lib/fun_ci/cli.rb +72 -94
- data/lib/fun_ci/cli_help.rb +65 -0
- data/lib/fun_ci/console/board_data.rb +66 -0
- data/lib/fun_ci/console/board_heartbeat.rb +40 -0
- data/lib/fun_ci/console/console_events.rb +20 -0
- data/lib/fun_ci/console/console_log.rb +26 -0
- data/lib/fun_ci/console/console_loop.rb +41 -0
- data/lib/fun_ci/console/console_session.rb +82 -0
- data/lib/fun_ci/console/console_state.rb +42 -0
- data/lib/fun_ci/console/key_handler.rb +73 -0
- data/lib/fun_ci/console/launcher.rb +43 -0
- data/lib/fun_ci/console/milestone_events.rb +38 -0
- data/lib/fun_ci/console/milestones.rb +25 -0
- data/lib/fun_ci/console/renderer_lookup.rb +51 -0
- data/lib/fun_ci/console/renderer_process.rb +60 -0
- data/lib/fun_ci/console/run_message.rb +33 -0
- data/lib/fun_ci/console/stage_change_detector.rb +32 -0
- data/lib/fun_ci/console/stage_events.rb +31 -0
- data/lib/fun_ci/console/stop_report.rb +39 -0
- data/lib/fun_ci/console/streak_counter.rb +19 -0
- data/lib/fun_ci/console/view.rb +47 -0
- data/lib/fun_ci/evidence/about.rb +12 -0
- data/lib/fun_ci/evidence/about_stage.rb +29 -0
- data/lib/fun_ci/evidence/caps.rb +77 -0
- data/lib/fun_ci/evidence/catalog.rb +83 -0
- data/lib/fun_ci/evidence/collector.rb +95 -0
- data/lib/fun_ci/evidence/command_output.rb +45 -0
- data/lib/fun_ci/evidence/command_reading.rb +38 -0
- data/lib/fun_ci/evidence/command_runner.rb +75 -0
- data/lib/fun_ci/evidence/context.rb +21 -0
- data/lib/fun_ci/evidence/context_document.rb +29 -0
- data/lib/fun_ci/evidence/deadline.rb +15 -0
- data/lib/fun_ci/evidence/detection.rb +82 -0
- data/lib/fun_ci/evidence/document.rb +59 -0
- data/lib/fun_ci/evidence/extract_command.rb +83 -0
- data/lib/fun_ci/evidence/extract_options.rb +45 -0
- data/lib/fun_ci/evidence/extraction.rb +54 -0
- data/lib/fun_ci/evidence/extractors/command.rb +52 -0
- data/lib/fun_ci/evidence/extractors/grep.rb +49 -0
- data/lib/fun_ci/evidence/extractors/json_log.rb +45 -0
- data/lib/fun_ci/evidence/extractors/junit_files.rb +32 -0
- data/lib/fun_ci/evidence/extractors/last_lines.rb +25 -0
- data/lib/fun_ci/evidence/extractors/log_file.rb +62 -0
- data/lib/fun_ci/evidence/extractors/process_tree.rb +51 -0
- data/lib/fun_ci/evidence/extractors/section.rb +49 -0
- data/lib/fun_ci/evidence/findings.rb +14 -0
- data/lib/fun_ci/evidence/line_ranges.rb +21 -0
- data/lib/fun_ci/evidence/line_scan.rb +22 -0
- data/lib/fun_ci/evidence/log_record.rb +58 -0
- data/lib/fun_ci/evidence/masking.rb +55 -0
- data/lib/fun_ci/evidence/option_check.rb +53 -0
- data/lib/fun_ci/evidence/outcome.rb +15 -0
- data/lib/fun_ci/evidence/patterns.rb +22 -0
- data/lib/fun_ci/evidence/presets/README.md +7 -0
- data/lib/fun_ci/evidence/presets/bun-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/cargo-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/dart-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/deno-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/dotnet-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/ecs.yml +3 -0
- data/lib/fun_ci/evidence/presets/eslint.yml +6 -0
- data/lib/fun_ci/evidence/presets/exunit.yml +6 -0
- data/lib/fun_ci/evidence/presets/gcc.yml +6 -0
- data/lib/fun_ci/evidence/presets/go-build.yml +5 -0
- data/lib/fun_ci/evidence/presets/go-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/gradle.yml +6 -0
- data/lib/fun_ci/evidence/presets/gtest.yml +6 -0
- data/lib/fun_ci/evidence/presets/jest.yml +6 -0
- data/lib/fun_ci/evidence/presets/logstash.yml +3 -0
- data/lib/fun_ci/evidence/presets/maven.yml +6 -0
- data/lib/fun_ci/evidence/presets/minitest.yml +6 -0
- data/lib/fun_ci/evidence/presets/mocha.yml +6 -0
- data/lib/fun_ci/evidence/presets/mypy.yml +6 -0
- data/lib/fun_ci/evidence/presets/node-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/phpunit.yml +6 -0
- data/lib/fun_ci/evidence/presets/pino.yml +5 -0
- data/lib/fun_ci/evidence/presets/prove.yml +6 -0
- data/lib/fun_ci/evidence/presets/pytest.yml +6 -0
- data/lib/fun_ci/evidence/presets/rspec.yml +6 -0
- data/lib/fun_ci/evidence/presets/rubocop.yml +6 -0
- data/lib/fun_ci/evidence/presets/ruff.yml +6 -0
- data/lib/fun_ci/evidence/presets/rustc.yml +6 -0
- data/lib/fun_ci/evidence/presets/shellcheck.yml +5 -0
- data/lib/fun_ci/evidence/presets/structlog.yml +4 -0
- data/lib/fun_ci/evidence/presets/swift-test.yml +5 -0
- data/lib/fun_ci/evidence/presets/tsc.yml +6 -0
- data/lib/fun_ci/evidence/presets/unittest.yml +6 -0
- data/lib/fun_ci/evidence/presets/vitest.yml +6 -0
- data/lib/fun_ci/evidence/presets.rb +30 -0
- data/lib/fun_ci/evidence/problem.rb +9 -0
- data/lib/fun_ci/evidence/process_table.rb +40 -0
- data/lib/fun_ci/evidence/saved_run.rb +22 -0
- data/lib/fun_ci/evidence/section_walk.rb +49 -0
- data/lib/fun_ci/evidence/settings.rb +64 -0
- data/lib/fun_ci/evidence/settings_check.rb +78 -0
- data/lib/fun_ci/evidence/source.rb +34 -0
- data/lib/fun_ci/evidence/sources.rb +21 -0
- data/lib/fun_ci/evidence/stage_context.rb +30 -0
- data/lib/fun_ci/evidence/stamp.rb +10 -0
- data/lib/fun_ci/evidence/start.rb +36 -0
- data/lib/fun_ci/evidence/worktree.rb +62 -0
- data/lib/fun_ci/persistence/active_runs.rb +69 -0
- data/lib/fun_ci/persistence/database.rb +63 -30
- data/lib/fun_ci/persistence/null_recorder.rb +25 -0
- data/lib/fun_ci/persistence/output_tail.rb +24 -0
- data/lib/fun_ci/persistence/pipeline_recorder.rb +78 -32
- data/lib/fun_ci/persistence/pipeline_run.rb +26 -17
- data/lib/fun_ci/persistence/project_runs.rb +52 -0
- data/lib/fun_ci/persistence/raw_outputs.rb +46 -0
- data/lib/fun_ci/persistence/retention.rb +25 -0
- data/lib/fun_ci/persistence/run_status.rb +31 -0
- data/lib/fun_ci/persistence/stage_job.rb +61 -14
- data/lib/fun_ci/persistence/state_dir.rb +26 -0
- data/lib/fun_ci/persistence/write_trouble.rb +46 -0
- data/lib/fun_ci/pipeline/background_fork.rb +42 -0
- data/lib/fun_ci/pipeline/command_executor.rb +58 -0
- data/lib/fun_ci/pipeline/gate.rb +35 -0
- data/lib/fun_ci/pipeline/git_environment.rb +16 -0
- data/lib/fun_ci/pipeline/head_part.rb +40 -0
- data/lib/fun_ci/pipeline/in_place.rb +17 -0
- data/lib/fun_ci/pipeline/output_window.rb +73 -0
- data/lib/fun_ci/pipeline/pipeline_forker.rb +18 -16
- data/lib/fun_ci/pipeline/process_runner.rb +90 -17
- data/lib/fun_ci/pipeline/run_canceller.rb +75 -0
- data/lib/fun_ci/pipeline/slot.rb +30 -0
- data/lib/fun_ci/pipeline/slot_lock.rb +13 -0
- data/lib/fun_ci/pipeline/slot_run.rb +86 -0
- data/lib/fun_ci/pipeline/stage_dir.rb +50 -0
- data/lib/fun_ci/pipeline/stage_end.rb +70 -0
- data/lib/fun_ci/pipeline/stage_execution.rb +67 -0
- data/lib/fun_ci/pipeline/stage_runner.rb +42 -65
- data/lib/fun_ci/pipeline/stale_pipeline_canceller.rb +16 -33
- data/lib/fun_ci/pipeline/tail_segments.rb +60 -0
- data/lib/fun_ci/pipeline/test_report.rb +48 -0
- data/lib/fun_ci/pipeline/trigger.rb +55 -115
- data/lib/fun_ci/pipeline/trigger_command.rb +62 -0
- data/lib/fun_ci/pipeline/trigger_params.rb +56 -0
- data/lib/fun_ci/pipeline/workspaces.rb +22 -0
- data/lib/fun_ci/pipeline/worktree_pool.rb +66 -0
- data/lib/fun_ci/pipeline/worktree_prune.rb +49 -0
- data/lib/fun_ci/pipeline/worktrees.rb +45 -0
- data/lib/fun_ci/setup/agent_instructions.rb +31 -0
- data/lib/fun_ci/setup/commands.rb +49 -0
- data/lib/fun_ci/setup/git_hooks.rb +20 -0
- data/lib/fun_ci/setup/hook_script.rb +47 -0
- data/lib/fun_ci/setup/hook_writer.rb +13 -31
- data/lib/fun_ci/setup/installer.rb +39 -23
- data/lib/fun_ci/setup/legacy_hooks.rb +38 -0
- data/lib/fun_ci/setup/project_config.rb +43 -17
- data/lib/fun_ci/setup/project_detector.rb +39 -7
- data/lib/fun_ci/setup/settings.rb +36 -0
- data/lib/fun_ci/setup/setup_checker.rb +18 -12
- data/lib/fun_ci/setup/stage_templates/javascript.rb +18 -0
- data/lib/fun_ci/setup/stage_templates/managed.rb +24 -0
- data/lib/fun_ci/setup/stage_templates/native.rb +25 -0
- data/lib/fun_ci/setup/stage_templates/scripting.rb +27 -0
- data/lib/fun_ci/setup/stage_templates/shell.rb +16 -0
- data/lib/fun_ci/setup/stage_templates.rb +25 -0
- data/lib/fun_ci/setup/template_writer.rb +11 -36
- data/lib/fun_ci/version.rb +1 -1
- data/lib/fun_ci.rb +0 -1
- metadata +205 -41
- data/exe/fun-ci-trigger +0 -19
- data/exe/fun-ci-tui +0 -23
- data/lib/fun_ci/animations/celebrate.rb +0 -210
- data/lib/fun_ci/animations/explosion.rb +0 -138
- data/lib/fun_ci/animations/flash.rb +0 -205
- data/lib/fun_ci/animations/idle.rb +0 -127
- data/lib/fun_ci/animations/leprechauns.rb +0 -240
- data/lib/fun_ci/animations/running.rb +0 -129
- data/lib/fun_ci/animations/success.rb +0 -281
- data/lib/fun_ci/animations/yay.rb +0 -169
- data/lib/fun_ci/persistence/state_machine.rb +0 -50
- data/lib/fun_ci/pipeline/background_wrapper.rb +0 -27
- data/lib/fun_ci/tui/admin_tui.rb +0 -90
- data/lib/fun_ci/tui/animation.rb +0 -49
- data/lib/fun_ci/tui/animation_compositor.rb +0 -107
- data/lib/fun_ci/tui/animation_frames.rb +0 -112
- data/lib/fun_ci/tui/animation_library.rb +0 -46
- data/lib/fun_ci/tui/animation_renderer.rb +0 -144
- data/lib/fun_ci/tui/ansi.rb +0 -34
- data/lib/fun_ci/tui/board_data.rb +0 -53
- data/lib/fun_ci/tui/board_renderer.rb +0 -105
- data/lib/fun_ci/tui/duration_formatter.rb +0 -24
- data/lib/fun_ci/tui/header_animation_manager.rb +0 -71
- data/lib/fun_ci/tui/header_animation_player.rb +0 -45
- data/lib/fun_ci/tui/key_handler.rb +0 -86
- data/lib/fun_ci/tui/looping_animation_player.rb +0 -45
- data/lib/fun_ci/tui/relative_time.rb +0 -22
- data/lib/fun_ci/tui/row_formatter.rb +0 -108
- data/lib/fun_ci/tui/screen.rb +0 -103
- data/lib/fun_ci/tui/spinner.rb +0 -24
- data/lib/fun_ci/tui/stage_change_detector.rb +0 -58
- data/lib/fun_ci/tui/streak_counter.rb +0 -29
- data/lib/fun_ci/tui/terminal_input.rb +0 -69
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c30d5baf69419b87d2fd79cd45ac0f0f38de132b8d75e97cd7da0d0deef0c7fc
|
|
4
|
+
data.tar.gz: 504335aa06c6c3f3a2b2e067349bfaac1a90402843a206a7da76bbf51afd34ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8a2e2817e52d36a4cd27743727b25c98b1063396bb64777fe3fd1d3d4323765073433dbe5366b622092bdabca0944fb62818ac64ebd441b468467040c84aa9e6
|
|
7
|
+
data.tar.gz: c04951b18e20491ce6388c5a51cbda72ee4417fc44fb21bb7484d0ad82dd3c137a6e3657c313281a6c6ebb40da185972ad7a57e019a0ee46004db9caae59dbb0
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,242 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.0.1] - 2026-09-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `fun-ci console` could show a blank screen and stay that way. When a new
|
|
14
|
+
commit cancelled a run in the same second one of its stages started, the
|
|
15
|
+
stage was recorded as ending before it began. The renderer rejected the
|
|
16
|
+
negative duration and, with it, every board Ruby sent, so nothing was drawn
|
|
17
|
+
while that run was listed; the only sign was a line per second in
|
|
18
|
+
`.fun-ci/console.log`. Ends are now recorded to the millisecond, and a stage
|
|
19
|
+
already recorded that way reads as taking no time, so an existing database
|
|
20
|
+
needs no repair. `fun-ci status`, `why` and `events` no longer report a
|
|
21
|
+
negative time for such a stage either.
|
|
22
|
+
- A commit made on a detached HEAD, as every commit a rebase replays is, was
|
|
23
|
+
recorded with no branch name, so `fun-ci status` ended in "on" and the
|
|
24
|
+
console showed a gap where the branch goes. Such runs are now on the branch
|
|
25
|
+
`detached`, whether a hook or `fun-ci wait` started them, and runs already
|
|
26
|
+
recorded without a name are renamed when fun-ci next opens its database.
|
|
27
|
+
|
|
28
|
+
## [2.0.0] - 2026-09-29
|
|
29
|
+
|
|
30
|
+
fun-ci 2.0 has two ways in. People watch a new console, drawn by a Rust
|
|
31
|
+
renderer in 24-bit colour, with a scene for each milestone a run passes. Coding
|
|
32
|
+
agents and scripts ask with new commands (`status`, `wait`, `why`, `runs`,
|
|
33
|
+
`events`) that answer with exit codes and keep enough of each failure to say
|
|
34
|
+
why it failed without running the suite again. Pipelines now run in git
|
|
35
|
+
worktrees at the commit they test, started after the commit rather than
|
|
36
|
+
before it. See "Upgrading to 2.0" in the README.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- The hook that runs a pipeline in the background is now `post-commit`, so it
|
|
40
|
+
tests the commit you just made; the `pre-commit` hook tested the commit
|
|
41
|
+
before it. `fun-ci install-hooks` writes `post-commit` and `pre-push`, and
|
|
42
|
+
removes the `pre-commit` hook fun-ci 1.x wrote. A `pre-commit` hook of your
|
|
43
|
+
own is left alone, and if it still calls fun-ci, `fun-ci check` warns you
|
|
44
|
+
to take that out. Run `fun-ci install-hooks` again to update your hooks;
|
|
45
|
+
until you do, `fun-ci check` warns that each run tests the commit before
|
|
46
|
+
yours.
|
|
47
|
+
- The pre-push hook waits for the fast verdict of each commit the push sends
|
|
48
|
+
(`fun-ci wait SHA --need fast`) instead of running the pipeline again, so a
|
|
49
|
+
push after a finished post-commit run goes through at once. It reads the
|
|
50
|
+
commits from what git passes it, so pushing a branch other than the one
|
|
51
|
+
checked out tests the right commits.
|
|
52
|
+
- Every pipeline runs in a git worktree of its own, checked out at the commit
|
|
53
|
+
it tests, under `.git/fun-ci/worktrees/`. Before, the stages ran in your
|
|
54
|
+
checkout while you kept editing it. Ignored files such as `vendor/bundle`
|
|
55
|
+
stay in a worktree from one run to the next. The stage scripts come from
|
|
56
|
+
the commit too, unless it has no `.fun-ci/`, in which case the checkout's
|
|
57
|
+
are used. Two pipelines run at once by default; set `worktree_slots: 3` (or
|
|
58
|
+
any number above 0) in `.fun-ci/config` for more.
|
|
59
|
+
- fun-ci keeps its database in `$XDG_STATE_HOME/fun-ci/`, or
|
|
60
|
+
`~/.local/state/fun-ci/` without it, instead of under `$TMPDIR`, so every
|
|
61
|
+
process of a user finds the same database, including an agent in a sandbox
|
|
62
|
+
with a `TMPDIR` of its own. Runs recorded by 1.x are not carried over. The
|
|
63
|
+
state directory is readable by its user alone (0700).
|
|
64
|
+
- A newer commit cancels an older unfinished run on its branch only in its own
|
|
65
|
+
project, never a run an agent is waiting on, and never a run of the same
|
|
66
|
+
commit.
|
|
67
|
+
- `fun-ci console` is drawn by a separate program, `fun-ci-renderer`, written
|
|
68
|
+
in Rust. The gems for Linux (x86_64, aarch64, musl) and macOS (arm64,
|
|
69
|
+
x86_64) include it. Elsewhere, install it with `cargo install
|
|
70
|
+
fun-ci-renderer` or point `FUN_CI_RENDERER` at a copy you built; without
|
|
71
|
+
it, `console` says so and every other command works as before. What goes
|
|
72
|
+
wrong between the two ends up in `.fun-ci/console.log`, since the renderer
|
|
73
|
+
owns the terminal.
|
|
74
|
+
- The console's header is a picture rather than ASCII art: soft light,
|
|
75
|
+
gradients and particles in 24-bit colour across the whole width, or the
|
|
76
|
+
nearest of 256 colours when `COLORTERM` doesn't say `truecolor` or `24bit`.
|
|
77
|
+
Only the parts that change are sent to the terminal.
|
|
78
|
+
- The header plays a scene for each milestone a run passes, each milestone
|
|
79
|
+
with scenes of its own, so you can tell which it was without reading: a teal
|
|
80
|
+
sweep, ripple or spirit level for lint; amber blocks, gears or a ringing
|
|
81
|
+
anvil for the build; a lightning strike, YAY or a jump to warp speed for the
|
|
82
|
+
fast suite; fireworks, a trophy, dancing leprechauns or a sunrise for the
|
|
83
|
+
whole run; an explosion or shattering glass for a failure. Scenes queue and
|
|
84
|
+
each plays to its end, so a failure no longer cuts a celebration short, and
|
|
85
|
+
a run gets one failure scene however many of its stages fail.
|
|
86
|
+
- When the scenes are done, the header rests on how the latest run went: a
|
|
87
|
+
calm dusk with a tick after a pass, a pulsing hazard sign after a failure.
|
|
88
|
+
Five minutes after the latest run finished it goes quiet: a starry night,
|
|
89
|
+
an aurora, fireflies, a fire in a medieval stone hearth with a storm at the
|
|
90
|
+
window, a moonlit island with a palm, or snowfall over pines, a different
|
|
91
|
+
one every five minutes while nothing runs, with a small lamp in the corner
|
|
92
|
+
that stays green after a pass and flickers red after a failure. The streak
|
|
93
|
+
("7 in a row!", or "streak broken") sits in the top right.
|
|
94
|
+
- The console's board fits the terminal's height and scrolls to keep the
|
|
95
|
+
cursor on screen.
|
|
96
|
+
- The console's cancel prompt names the run it would cancel, as in
|
|
97
|
+
`Cancel feat/search (d4e5f67)? y / n`.
|
|
98
|
+
- With no runs yet, the console suggests `fun-ci init --everything`. It used to
|
|
99
|
+
suggest `fun-ci trigger HEAD`, which fails without a branch, and
|
|
100
|
+
`fun-ci install-hook pre-push`, which does not exist.
|
|
101
|
+
- When a stage fails, `fun-ci trigger` says what to do next, such as "Fix the
|
|
102
|
+
failing tests above, then try again."
|
|
103
|
+
- A project's colour in the console stays the same from one start to the next.
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
- `fun-ci status [REV]` says where a commit's run stands, stage by stage, and
|
|
107
|
+
exits with a verdict an agent can branch on: 0 passed, 1 failed, 2 over
|
|
108
|
+
budget, 3 undecided, 4 superseded, 5 no run, 64 usage error. `--need
|
|
109
|
+
build|fast|all` says which stages must pass (fast by default), and `--json`
|
|
110
|
+
prints one JSON document.
|
|
111
|
+
- `fun-ci wait [REV]` waits for that verdict and exits with it: as soon as the
|
|
112
|
+
stages `--need` names have passed, or at the first of them to fail or
|
|
113
|
+
overrun. `--within 30s` gives up undecided (exit 3). A commit with no run
|
|
114
|
+
after 5 seconds gets one started. `wait` on a superseded run exits 4 naming
|
|
115
|
+
the newer commit, and `--follow-branch` moves on to that commit's run.
|
|
116
|
+
- `fun-ci runs` lists the project's recent runs, one line each; `-n N`,
|
|
117
|
+
`--branch NAME` and `--json`.
|
|
118
|
+
- `fun-ci events` prints the project's runs' events as JSON lines (run
|
|
119
|
+
started, stage finished, run finished, run superseded); `--follow` keeps
|
|
120
|
+
printing them as they happen, and `--only failures` keeps to failed or
|
|
121
|
+
over-budget stages and superseded runs.
|
|
122
|
+
- After each commit, the hook's output ends with the `fun-ci wait` command
|
|
123
|
+
that gets its verdict, and `fun-ci init` adds a short section to the
|
|
124
|
+
project's `AGENTS.md` (or `CLAUDE.md` when that is the only one) telling
|
|
125
|
+
agents to run it in the background.
|
|
126
|
+
- `fun-ci why [REV] [STAGE]` prints everything fun-ci kept about a failed
|
|
127
|
+
stage: how it exited, how long it took against its budget, each reported
|
|
128
|
+
failure with its whole message and its own output, what the extractors
|
|
129
|
+
picked out, and the last lines of its output. `status` and `wait` end with
|
|
130
|
+
a digest of it and the `why` command. `why --raw` prints the stage's whole
|
|
131
|
+
output, kept for a project's 10 newest runs as its first 1 MB and last
|
|
132
|
+
7 MB, compressed. The end of a failed stage's output (200 lines, at most
|
|
133
|
+
64 KB) is kept for the 50 newest.
|
|
134
|
+
- A `junit-files` entry under `evidence:` keeps each failure in the JUnit XML
|
|
135
|
+
a stage wrote (`build/test-results/**/*.xml`, `target/surefire-reports/*.xml`),
|
|
136
|
+
with the output each failure printed, and leaves alone reports an earlier
|
|
137
|
+
run left behind. `status --json` gives them to a program. fun-ci now depends
|
|
138
|
+
on `rexml` to read them.
|
|
139
|
+
- Every stage script finds its stage's name in `FUN_CI_STAGE`.
|
|
140
|
+
- Secrets are masked before fun-ci keeps anything a stage printed: the value
|
|
141
|
+
of any variable in the stage's environment whose name holds TOKEN, SECRET,
|
|
142
|
+
PASSWORD, PASSWD, API_KEY, PRIVATE_KEY or CREDENTIAL (8 characters or more)
|
|
143
|
+
shows as `[masked:NAME]`, and GitHub, AWS and Slack tokens, private keys and
|
|
144
|
+
`Authorization:` headers as `[masked]`. `mask:` under `evidence:` adds
|
|
145
|
+
patterns.
|
|
146
|
+
- Presets pick out a tool's failures from a stage's output, for 34 tools,
|
|
147
|
+
each checked against the recorded output of a real failing run: rspec,
|
|
148
|
+
Minitest, Gradle, Maven, pytest, unittest, Jest, Vitest, Mocha, Node's test
|
|
149
|
+
runner, Bun, Deno, go test, cargo test, dotnet test, PHPUnit, ExUnit, swift
|
|
150
|
+
test, dart test, GoogleTest, tsc, ESLint, RuboCop, Ruff, mypy, go build,
|
|
151
|
+
rustc, gcc, ShellCheck, Perl's prove, and JSON logs from
|
|
152
|
+
logstash-logback-encoder, Elastic's ECS encoders, pino and structlog. With
|
|
153
|
+
no configuration fun-ci runs those whose marker files the project has
|
|
154
|
+
(`Gemfile`, `pom.xml`, ...) and whose tool's output the failure shows;
|
|
155
|
+
`fun-ci why` says why each was chosen and `fun-ci check` lists them.
|
|
156
|
+
- `fun-ci init` writes stage scripts for every stack a preset reads, not only
|
|
157
|
+
Ruby and the JVM: rspec projects, Rust, Go, Elixir, Dart, Swift, PHP, .NET,
|
|
158
|
+
Python (uv, Poetry, pip), Deno, Bun, Node (pnpm, Yarn, npm), Perl, CMake and
|
|
159
|
+
make.
|
|
160
|
+
- A project says what else to keep when a stage fails under `evidence:` in
|
|
161
|
+
`.fun-ci/config`, within a budget of 2 seconds: `grep`, `section` (with a
|
|
162
|
+
`preset:`), `log-file` (what the stage wrote to a log file, and only that),
|
|
163
|
+
`json-log` (records at or above a level, with the fields of a named preset or
|
|
164
|
+
your own), `junit-files` (JUnit XML a build tool writes in its own place),
|
|
165
|
+
and `run:`, your own extractor in any language, which gets the context as
|
|
166
|
+
JSON on stdin and prints text or JSON. `detect: false` and `skip: [name]`
|
|
167
|
+
turn presets off. The documents a `run:` extractor reads and prints are
|
|
168
|
+
pinned in `contract/evidence/`.
|
|
169
|
+
- A stage that runs over budget says what it was doing: before the kill,
|
|
170
|
+
fun-ci lists its processes and the deepest one still running, and `status`
|
|
171
|
+
and `wait` lead with it (`fast ran over budget: running java ...
|
|
172
|
+
GradleWorkerMain (9.8s)`). A `run:` entry with `on: overrun` runs then, with
|
|
173
|
+
the stage's process group in `FUN_CI_PGID`, for a thread dump.
|
|
174
|
+
- When another stage shared the worktree while a stage ran (lint beside
|
|
175
|
+
build, the slow suite beside the fast one), `fun-ci why` says so
|
|
176
|
+
(`alongside: slow`), since a log file may hold both stages' lines.
|
|
177
|
+
- `fun-ci extract STAGE --output FILE` runs a stage's extractors against a
|
|
178
|
+
saved output and prints what `why` would, so an extractor can be tried
|
|
179
|
+
without making a commit.
|
|
180
|
+
- `fun-ci check` reports a mistake under `evidence:` or a `run:` script that
|
|
181
|
+
is missing or can't be run. A mistake never stops a pipeline: the entry is
|
|
182
|
+
left out and `fun-ci why` names it as a problem.
|
|
183
|
+
- `fun-ci prune` deletes the worktrees fun-ci keeps and has git forget them.
|
|
184
|
+
It refuses, and deletes nothing, while a pipeline is running.
|
|
185
|
+
- `.fun-ci/console.log` notes when the console starts and stops and why it
|
|
186
|
+
stopped, what it sent every ten minutes, and when the renderer stops
|
|
187
|
+
reading or a frame takes a second or more to reach the terminal.
|
|
188
|
+
- `fun-ci-renderer --colours 24bit|256` chooses the colours to draw in.
|
|
189
|
+
|
|
190
|
+
### Deprecated
|
|
191
|
+
- `fun-ci trigger --no-validate` is the old name for `--background`. It works
|
|
192
|
+
until 2.1 and says so on stderr.
|
|
193
|
+
|
|
194
|
+
### Removed
|
|
195
|
+
- The `fun-ci-trigger` and `fun-ci-tui` executables. Use `fun-ci trigger` and
|
|
196
|
+
`fun-ci console`, which have done the same job since 1.0.
|
|
197
|
+
|
|
198
|
+
### Fixed
|
|
199
|
+
- In a pipeline started in the background, as the post-commit hook starts
|
|
200
|
+
them (and 1.x's pre-commit hook did), the fast suite waited for the slow
|
|
201
|
+
suite to finish before it began. It now runs beside it, so the verdict a
|
|
202
|
+
push waits for comes when the fast suite is done, not the slow one.
|
|
203
|
+
- A commit on a branch cancelled every unfinished run on a branch of that
|
|
204
|
+
name in every project, because all projects share one database. It now
|
|
205
|
+
cancels only its own project's runs.
|
|
206
|
+
- Cancelling a stale pipeline left its stage scripts running and could let
|
|
207
|
+
the old run record itself as failed after it was cancelled. fun-ci now
|
|
208
|
+
records every process a run starts and stops all of them, and cancels runs
|
|
209
|
+
still waiting for a worktree as well. A run that had already died is only
|
|
210
|
+
marked cancelled, since the process ids it left behind may belong to
|
|
211
|
+
something else by now.
|
|
212
|
+
- Cancelling a run from the console (`c`, then `y`) only marked it
|
|
213
|
+
cancelled; its stages kept running. It now stops the run.
|
|
214
|
+
- A run whose fast suite failed could end up PASSED when its slow suite passed
|
|
215
|
+
afterwards, and a run showed PASSED for a moment when the slow suite
|
|
216
|
+
finished before the fast suite. A run's status now follows from all four
|
|
217
|
+
stages, whichever finishes first.
|
|
218
|
+
- A slow suite whose process died without finishing left its run RUNNING for
|
|
219
|
+
ever. The console now records it failed on its next poll.
|
|
220
|
+
- A database that stays busy, or can't be written because the disk is full,
|
|
221
|
+
no longer stops `fun-ci trigger`: the stages still run, the exit code is
|
|
222
|
+
theirs, and fun-ci says once that the run wasn't recorded.
|
|
223
|
+
- Two fun-ci processes starting at once against a database that didn't exist
|
|
224
|
+
yet (the first commit and push on a machine, say) could die with `database
|
|
225
|
+
is locked` or `duplicate column name: project_path`. They now take turns.
|
|
226
|
+
- The git hooks blocked a push when fun-ci wasn't installed, failing with
|
|
227
|
+
"fun-ci: command not found". Now they say fun-ci is missing, how to install
|
|
228
|
+
it, and let the push go ahead.
|
|
229
|
+
- Git runs hooks with variables such as `GIT_INDEX_FILE` pointing at the
|
|
230
|
+
checkout. fun-ci passed them on to the git commands it runs and to your
|
|
231
|
+
stage scripts, which then read the checkout's index instead of the
|
|
232
|
+
commit's. They are cleared now.
|
|
233
|
+
- `fun-ci install-hooks` wrote into `.git/hooks` even when `core.hooksPath`
|
|
234
|
+
sends git elsewhere (as husky sets it), so git never ran the hooks; and in
|
|
235
|
+
a linked worktree, whose `.git` is a file, it said the project wasn't a git
|
|
236
|
+
repository. It now asks git where the hooks go.
|
|
237
|
+
- Every stage failed in a project whose path held a space, with "cannot
|
|
238
|
+
execute: No such file or directory". The stage script's path is quoted now.
|
|
239
|
+
- A stage that printed without end grew fun-ci's memory without end, since
|
|
240
|
+
its output was held in memory. It is written to disk as it runs now.
|
|
241
|
+
- The phase 1 line printed lint and build in whichever order they finished.
|
|
242
|
+
Lint always comes first now.
|
|
243
|
+
|
|
8
244
|
## [1.2.1] - 2026-09-20
|
|
9
245
|
|
|
10
246
|
### Bug Fixes
|
data/README.md
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://badge.fury.io/rb/fun_ci)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Fun-CI
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Opinionated local CI that checks your code before it leaves your machine. Every commit runs lint, build, a fast suite and a slow suite on your own computer, each held to a strict time budget, so the feedback stays fast.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. **Slow suite** (spawned in background, 5min budget) -- integration/end-to-end tests
|
|
11
|
-
3. **Fast suite** (synchronous, 10s budget) -- unit tests
|
|
9
|
+
Fun-CI has two goals. It should be extremely easy to tell whether all is well, and it should be fun. The design behind both is in [docs/design.md](docs/design.md).
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
There are two ways to use it, and both read the same results:
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
- **You watch.** `fun-ci console` sits on a second screen. Colour and motion tell you from the corner of your eye that lint passed, the build passed, the fast suite passed, or that something failed and you should look closer. A pass gets a celebration, a failure gets an explosion.
|
|
14
|
+
- **Your agent asks.** A coding agent can't glance at a screen, so it runs commands. `fun-ci wait` blocks until a commit's verdict is in and exits with it, and `fun-ci why` prints everything fun-ci kept about a failure, so the agent never has to rerun a suite to find out what broke.
|
|
16
15
|
|
|
17
|
-
## Getting
|
|
16
|
+
## Getting started
|
|
18
17
|
|
|
19
18
|
```bash
|
|
20
19
|
gem install fun_ci
|
|
@@ -22,87 +21,211 @@ cd your-project
|
|
|
22
21
|
fun-ci init --everything
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
This
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
This detects your project type, writes four stage scripts into `.fun-ci/`, installs a `post-commit` and a `pre-push` git hook, and checks the setup. It also adds a short section to your `AGENTS.md` (or `CLAUDE.md`, if that is the only one) telling coding agents what to do after a commit.
|
|
25
|
+
|
|
26
|
+
`fun-ci init` has templates for Ruby, Gradle, Maven, Rust, Go, Elixir, Dart, Swift, PHP, .NET, Python, Deno, Bun, Node, Perl, and C or C++ built with CMake or make. [docs/stacks.md](docs/stacks.md) shows how it recognises each one and the commands it writes. Fun-CI works with any project: the stages are shell scripts, so edit them to run whatever your project uses.
|
|
27
|
+
|
|
28
|
+
- `lint.sh`: linter and static analysis
|
|
29
|
+
- `build.sh`: compile or build
|
|
30
|
+
- `fast.sh`: the fast suite (unit tests)
|
|
31
|
+
- `slow.sh`: the slow suite (integration tests)
|
|
32
|
+
|
|
33
|
+
Each script gets the commit hash as its first argument, its stage's name in `FUN_CI_STAGE`, and exits 0 to pass.
|
|
34
|
+
|
|
35
|
+
Upgrading from 1.x? Read [Upgrading to 2.0](#upgrading-to-20) first.
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
## How a run works
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
| Stage | Budget | Runs | Blocks |
|
|
40
|
+
|-------|--------|------|--------|
|
|
41
|
+
| Lint | 30 s | in parallel with build | the push |
|
|
42
|
+
| Build | 30 s | in parallel with lint | the push |
|
|
43
|
+
| Fast | 10 s | after lint and build pass | the push |
|
|
44
|
+
| Slow | 5 min | in the background, beside the fast suite | nothing |
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
A stage that overruns its budget is killed and reported as over budget, in yellow rather than red: a fast suite that takes 12 seconds has grown too heavy, which is a different problem from a failing test.
|
|
47
|
+
|
|
48
|
+
After each commit, the `post-commit` hook starts the pipeline in the background and returns at once, so a commit is never held up. The `pre-push` hook waits for the verdict of each commit you push and stops the push if lint, build or the fast suite failed. A commit whose run has already finished goes straight through. If fun-ci isn't installed, both hooks say so and let git carry on.
|
|
49
|
+
|
|
50
|
+
Each run happens in a git worktree of its own under `.git/fun-ci/worktrees/`, checked out at the commit it tests, so you can keep editing while it runs. Ignored files such as `vendor/bundle` or `node_modules` stay in a worktree from one run to the next, which keeps builds quick. Two runs can go at once; set `worktree_slots: 3` in `.fun-ci/config` for more, and `fun-ci prune` removes the worktrees when you want the space back. A newer commit on a branch cancels the older run that is still going, unless an agent is waiting on it.
|
|
51
|
+
|
|
52
|
+
Results are kept in SQLite under `$XDG_STATE_HOME/fun-ci/` (`~/.local/state/fun-ci/` by default), shared by every project on the machine.
|
|
53
|
+
|
|
54
|
+
## Watching: the console
|
|
35
55
|
|
|
36
56
|
```bash
|
|
37
|
-
fun-ci
|
|
38
|
-
fun-ci install-hooks # Install git hooks
|
|
39
|
-
fun-ci check # Verify everything is configured
|
|
57
|
+
fun-ci console
|
|
40
58
|
```
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
- `lint.sh` -- linter/static analysis
|
|
44
|
-
- `build.sh` -- build/compile step
|
|
45
|
-
- `fast.sh` -- fast test suite (unit tests)
|
|
46
|
-
- `slow.sh` -- slow test suite (integration tests)
|
|
60
|
+
The console lists your runs across branches, newest first, one row each: the commit, the branch, each stage with its time, and the outcome. Above them is a picture that tells you what is happening without reading a word.
|
|
47
61
|
|
|
48
|
-
Each
|
|
62
|
+
While a run runs, a rocket flies. Each milestone a run passes has its own set of scenes, so you can tell which one it was from across the room, and a failure has its own.
|
|
49
63
|
|
|
50
|
-
|
|
64
|
+
| | |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Lint passed: a teal sweep, a ripple or a spirit level | The build passed: amber blocks, gears or a ringing anvil |
|
|
67
|
+
|  |  |
|
|
68
|
+
| The fast suite passed: a lightning strike, a very happy YAY or a jump to warp speed | The whole run passed: fireworks, a trophy, dancing leprechauns or a sunrise |
|
|
69
|
+
|  |  |
|
|
70
|
+
|
|
71
|
+
Scenes queue and each plays to its end, so a failure never cuts a celebration short, and a run gets one explosion however many of its stages fail.
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
When the scenes are done, the header rests on how the latest run went, so a glance a minute later still tells you: a calm tick after a pass, a pulsing hazard sign after a failure. The top right keeps count of the passes in a row.
|
|
76
|
+
|
|
77
|
+
| | |
|
|
78
|
+
|---|---|
|
|
79
|
+
|  |  |
|
|
80
|
+
|
|
81
|
+
Five minutes after the latest run finished, the header goes quiet: a starry night, an aurora, fireflies, a fire in a medieval stone hearth while a storm rages at the window, a moonlit island with a lone palm, or snow falling on a pine forest, changing every five minutes while nothing runs. A small lamp in the corner stays green after a pass and flickers red after a failure.
|
|
82
|
+
|
|
83
|
+
| | |
|
|
84
|
+
|---|---|
|
|
85
|
+
|  |  |
|
|
86
|
+
|
|
87
|
+
Keys:
|
|
88
|
+
|
|
89
|
+
- `j` and `k`, or the arrow keys, move the cursor down and up
|
|
90
|
+
- `c` cancels the run under the cursor: a scheduled one at once, a running one once you answer `y` (`n` or `Esc` keeps it running)
|
|
91
|
+
- `q` quits
|
|
92
|
+
|
|
93
|
+
The console is drawn in 24-bit colour when `COLORTERM` says the terminal has it (`truecolor` or `24bit`), and in 256 colours otherwise. The pictures above come from the renderer's headless mode, drawn with its own bitmap font; in your terminal the text is in your terminal's font.
|
|
94
|
+
|
|
95
|
+
The drawing is done by a separate program, `fun-ci-renderer`, written in Rust. The gem for Linux (x86_64, aarch64, musl) and macOS (arm64, x86_64) includes it. On other Unix-like systems you get the plain gem, where every command except `console` works; install the renderer with `cargo install fun-ci-renderer` (Rust 1.88 or newer), or point `FUN_CI_RENDERER` at a copy you built. Windows is not supported, because fun-ci forks its background pipeline, but WSL runs the Linux gem. When something goes wrong between the two, the details are in `.fun-ci/console.log`.
|
|
96
|
+
|
|
97
|
+
## Asking: commands for agents and scripts
|
|
98
|
+
|
|
99
|
+
Every commit's output ends with the command that gets its verdict:
|
|
51
100
|
|
|
52
101
|
```
|
|
53
|
-
fun-ci
|
|
54
|
-
fun-ci trigger --no-validate <commit> <branch> Fork pipeline to background (used by pre-commit)
|
|
55
|
-
fun-ci console Launch the TUI dashboard
|
|
56
|
-
fun-ci init Scaffold .fun-ci/ for detected project type
|
|
57
|
-
fun-ci init --everything init + install-hooks + check in one step
|
|
58
|
-
fun-ci install-hooks Install pre-commit and pre-push hooks
|
|
59
|
-
fun-ci install-hooks pre-commit Install a single hook type
|
|
60
|
-
fun-ci check Verify .fun-ci/ setup
|
|
102
|
+
fun-ci: testing 3f9c2ab. Verdict: fun-ci wait 3f9c2ab --need all
|
|
61
103
|
```
|
|
62
104
|
|
|
63
|
-
|
|
105
|
+
The section `fun-ci init` writes into `AGENTS.md` tells an agent to run that command in the background after each commit. Agent harnesses wake the agent when a background command exits, so it hears about a failure without having to remember to ask.
|
|
64
106
|
|
|
65
|
-
|
|
107
|
+
`status` says where a commit's run stands and `wait` blocks until it is decided. Both exit with the verdict for the stages you need: `--need build` (lint and build), `fast` (the default) or `all` (the slow suite too).
|
|
66
108
|
|
|
67
|
-
|
|
109
|
+
| Exit code | Meaning |
|
|
110
|
+
|---|---|
|
|
111
|
+
| 0 | Passed |
|
|
112
|
+
| 1 | A needed stage failed |
|
|
113
|
+
| 2 | A needed stage ran over budget |
|
|
114
|
+
| 3 | Still undecided (`wait --within 30s` gives up at your deadline) |
|
|
115
|
+
| 4 | Superseded by a newer commit on the branch (`wait --follow-branch` moves on to it) |
|
|
116
|
+
| 5 | No run for the commit |
|
|
117
|
+
| 64 | Usage error |
|
|
68
118
|
|
|
69
|
-
|
|
119
|
+
A failed stage comes with its evidence. `status` and `wait` end with a digest and the command that shows the rest:
|
|
70
120
|
|
|
71
|
-
|
|
121
|
+
```
|
|
122
|
+
$ fun-ci status
|
|
123
|
+
fun-ci: 2330979 "Add shipping to the cart total" on main
|
|
124
|
+
lint passed 0.6s
|
|
125
|
+
build passed 0.3s
|
|
126
|
+
fast FAILED 0.3s
|
|
127
|
+
slow passed 0.5s (not needed)
|
|
128
|
+
fast failed:
|
|
129
|
+
1) Failure:
|
|
130
|
+
CartTest#test_total_includes_shipping [test/cart_test.rb:16]:
|
|
131
|
+
Expected: 104.95
|
|
132
|
+
Actual: 99.95
|
|
133
|
+
fun-ci why 2330979 fast
|
|
134
|
+
```
|
|
72
135
|
|
|
73
|
-
|
|
74
|
-
|
|
136
|
+
`why` prints how the stage ended, each failure with its whole message and its own output, what the extractors picked out of the output and the log files, and anything that went wrong collecting it. `why --raw` prints the whole output. Every command takes `--json`.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
$ fun-ci why 2330979 fast
|
|
140
|
+
fun-ci: 2330979 "Add shipping to the cart total" on main
|
|
141
|
+
fast failed (exit 1) after 0.2s, budget 10s
|
|
142
|
+
|
|
143
|
+
Minitest's failures and errors (output:9-12), from section:minitest:
|
|
144
|
+
1) Failure:
|
|
145
|
+
CartTest#test_total_includes_shipping [test/cart_test.rb:16]:
|
|
146
|
+
Expected: 104.95
|
|
147
|
+
Actual: 99.95
|
|
148
|
+
|
|
149
|
+
The output's last lines, from output-tail:
|
|
150
|
+
Run options: --seed 20502
|
|
151
|
+
...
|
|
152
|
+
3 runs, 3 assertions, 1 failures, 0 errors, 0 skips
|
|
153
|
+
rake aborted!
|
|
154
|
+
Command failed with status (1)
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
The whole output: fun-ci why 2330979 fast --raw
|
|
75
158
|
```
|
|
76
159
|
|
|
77
|
-
|
|
160
|
+
`fun-ci runs` lists recent runs one line each, and `fun-ci events --follow` prints each run's events as JSON lines as they happen, for a supervising agent or a status bar.
|
|
161
|
+
|
|
162
|
+
### What fun-ci keeps when a stage fails
|
|
163
|
+
|
|
164
|
+
With no configuration, fun-ci runs the presets for the tools your project has when a failure shows them. There are presets for the test runners, compilers, linters and type checkers of every stack `fun-ci init` knows, and for JSON logs from logstash-logback-encoder, ECS, pino and structlog; [docs/stacks.md](docs/stacks.md#presets) lists each one with what it picks out and when it runs. Each preset is checked against the recorded output of a real failing run of its tool. `fun-ci check` lists the ones that apply to your project.
|
|
165
|
+
|
|
166
|
+
To keep more, add entries under `evidence:` in `.fun-ci/config`:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
evidence:
|
|
170
|
+
stages:
|
|
171
|
+
fast:
|
|
172
|
+
- use: junit-files # each failure in the JUnit XML the stage wrote,
|
|
173
|
+
paths: [build/test-results/test/*.xml] # as file, line, test and message
|
|
174
|
+
- use: log-file # what the stage wrote to it, and only that
|
|
175
|
+
path: log/test.log
|
|
176
|
+
grep: ["ERROR"]
|
|
177
|
+
context: 5
|
|
178
|
+
slow:
|
|
179
|
+
- run: .fun-ci/evidence/problems # your own extractor, in any language:
|
|
180
|
+
format: json # the context on stdin, JSON on stdout
|
|
181
|
+
- run: pkill -QUIT -g "$FUN_CI_PGID" java # a thread dump before an overrun is killed
|
|
182
|
+
on: overrun
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`fun-ci extract fast --output saved.log` runs a stage's extractors against a saved output (`fun-ci why --raw > saved.log`), so you can try an entry without making a commit. A mistake under `evidence:` never stops a pipeline: `fun-ci check` reports it and `fun-ci why` names it.
|
|
186
|
+
|
|
187
|
+
Secrets in the stage's environment are masked before anything is kept: the value of any variable whose name holds TOKEN, SECRET, PASSWORD, PASSWD, API_KEY, PRIVATE_KEY or CREDENTIAL, and GitHub, AWS and Slack tokens, private keys and `Authorization:` headers wherever they appear. The state directory is readable by your user alone.
|
|
188
|
+
|
|
189
|
+
## Commands
|
|
78
190
|
|
|
79
|
-
|
|
191
|
+
```
|
|
192
|
+
fun-ci init Scaffold .fun-ci/ for the detected project type
|
|
193
|
+
fun-ci init --everything init + install-hooks + check in one step
|
|
194
|
+
fun-ci install-hooks Install post-commit and pre-push hooks
|
|
195
|
+
fun-ci install-hooks post-commit Install a single hook type
|
|
196
|
+
fun-ci check Verify .fun-ci/ setup
|
|
197
|
+
fun-ci console Watch the runs
|
|
198
|
+
fun-ci status [commit] Where a commit's run stands; the verdict is the exit code
|
|
199
|
+
fun-ci wait [commit] Wait until that verdict is decided, then exit with it
|
|
200
|
+
fun-ci why [commit] [stage] Everything kept about why a stage failed
|
|
201
|
+
fun-ci runs This project's recent runs, newest first
|
|
202
|
+
fun-ci events [--follow] The runs' events as JSON lines
|
|
203
|
+
fun-ci extract <stage> --output <file> Try a stage's extractors on a saved output
|
|
204
|
+
fun-ci trigger <commit> <branch> Run the whole pipeline in the foreground
|
|
205
|
+
fun-ci trigger --background <commit> <branch> Start the pipeline in the background (what post-commit runs)
|
|
206
|
+
fun-ci prune Remove fun-ci's worktrees when no pipeline is running
|
|
207
|
+
```
|
|
80
208
|
|
|
81
|
-
- `
|
|
82
|
-
- `c` -- cancel a running pipeline
|
|
83
|
-
- `q` -- quit
|
|
209
|
+
`fun-ci --help` lists every option.
|
|
84
210
|
|
|
85
|
-
##
|
|
211
|
+
## Upgrading to 2.0
|
|
86
212
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
| Slow | 5min | nothing (background) |
|
|
213
|
+
- Run `fun-ci install-hooks` again. The background pipeline now runs after the commit (`post-commit`), so it tests the commit you just made; the `pre-commit` hook tested the one before it. Until you do, each run tests the commit before yours, and `fun-ci check` warns you. Installing removes the `pre-commit` hook fun-ci 1.x wrote and leaves a hook of your own alone (`fun-ci check` warns if yours still calls fun-ci). The new `pre-push` hook waits for the verdict instead of running the pipeline again.
|
|
214
|
+
- The stages run in a worktree at the commit, not in your checkout. A script that relied on uncommitted files in your checkout won't see them any more.
|
|
215
|
+
- The database moved from `$TMPDIR` to `$XDG_STATE_HOME/fun-ci/`. Runs recorded by 1.x are not carried over.
|
|
216
|
+
- `fun-ci-trigger` and `fun-ci-tui` are gone; use `fun-ci trigger` and `fun-ci console`. `fun-ci trigger --no-validate` still works until 2.1, as the old name for `--background`.
|
|
217
|
+
- The console needs `fun-ci-renderer`, which the platform gems include (see [Watching: the console](#watching-the-console)).
|
|
93
218
|
|
|
94
|
-
|
|
219
|
+
The whole list is in [CHANGELOG.md](CHANGELOG.md).
|
|
95
220
|
|
|
96
221
|
## Development
|
|
97
222
|
|
|
98
223
|
```bash
|
|
99
224
|
bundle install
|
|
100
|
-
rake
|
|
225
|
+
bundle exec rake # the gate: tests, the renderer's tests, the binary contract, rubocop, clippy
|
|
101
226
|
```
|
|
102
227
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
228
|
+
How it is built is in [docs/architecture.md](docs/architecture.md), and the requirements are in [docs/acceptance-tests.md](docs/acceptance-tests.md). The renderer is written in Rust; install Rust with [rustup](https://rustup.rs), which picks up the version the repository pins in `rust-toolchain.toml`. `ruby script/readme_screenshots.rb` draws the pictures in this README again.
|
|
106
229
|
|
|
107
230
|
## License
|
|
108
231
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FunCi
|
|
4
|
+
module Agent
|
|
5
|
+
# How long ago something happened, in the largest whole unit.
|
|
6
|
+
module Age
|
|
7
|
+
UNITS = [[86_400, "d"], [3600, "h"], [60, "m"]].freeze
|
|
8
|
+
|
|
9
|
+
def self.words(seconds)
|
|
10
|
+
size, unit = UNITS.find { |unit_seconds, _| seconds >= unit_seconds }
|
|
11
|
+
size ? "#{(seconds / size).floor}#{unit} ago" : "just now"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "options"
|
|
4
|
+
require_relative "reports"
|
|
5
|
+
require_relative "exit_code"
|
|
6
|
+
|
|
7
|
+
module FunCi
|
|
8
|
+
module Agent
|
|
9
|
+
# What the agent commands share: the commit a revision names, the reports
|
|
10
|
+
# of its runs, and refusing arguments they can't use. Expects @context and
|
|
11
|
+
# a NAME constant.
|
|
12
|
+
module CommandSupport
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def resolve(rev)
|
|
16
|
+
@context.git.resolve(rev) || raise(Options::Invalid, "git can't find the commit '#{rev}'")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reports = @reports ||= Reports.new(@context.db, @context.git)
|
|
20
|
+
|
|
21
|
+
def usage(message)
|
|
22
|
+
@context.io.stderr.puts "fun-ci #{self.class::NAME}: #{message}"
|
|
23
|
+
ExitCode::USAGE
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../pipeline/trigger_params"
|
|
4
|
+
require_relative "context"
|
|
5
|
+
require_relative "git"
|
|
6
|
+
require_relative "system_clock"
|
|
7
|
+
require_relative "live_pipeline"
|
|
8
|
+
require_relative "status_command"
|
|
9
|
+
require_relative "runs_command"
|
|
10
|
+
require_relative "wait_command"
|
|
11
|
+
require_relative "events_command"
|
|
12
|
+
require_relative "why_command"
|
|
13
|
+
|
|
14
|
+
module FunCi
|
|
15
|
+
module Agent
|
|
16
|
+
# The commands an agent runs in a project, each answered from the
|
|
17
|
+
# database and the project's git.
|
|
18
|
+
module Commands
|
|
19
|
+
ALL = { "status" => StatusCommand, "runs" => RunsCommand, "wait" => WaitCommand,
|
|
20
|
+
"events" => EventsCommand, "why" => WhyCommand }.freeze
|
|
21
|
+
|
|
22
|
+
def self.run(name, args, context)
|
|
23
|
+
ALL.fetch(name).new(context).run(args)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FunCi
|
|
4
|
+
module Agent
|
|
5
|
+
# What every agent command answers from: the database, the project's git,
|
|
6
|
+
# where to print, the time (#now, #pause) and the pipeline (#start a run,
|
|
7
|
+
# #watch the database for slow suites that died).
|
|
8
|
+
Context = Data.define(:db, :git, :io, :clock, :pipeline)
|
|
9
|
+
end
|
|
10
|
+
end
|