fun_ci 2.0.0 → 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 +18 -0
- data/README.md +2 -0
- data/lib/fun_ci/agent/git.rb +3 -2
- data/lib/fun_ci/agent/report_stage.rb +2 -4
- data/lib/fun_ci/persistence/active_runs.rb +2 -2
- data/lib/fun_ci/persistence/database.rb +4 -0
- data/lib/fun_ci/persistence/stage_job.rb +3 -1
- data/lib/fun_ci/pipeline/trigger_params.rb +8 -0
- data/lib/fun_ci/version.rb +1 -1
- metadata +1 -1
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
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
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
|
+
|
|
10
28
|
## [2.0.0] - 2026-09-29
|
|
11
29
|
|
|
12
30
|
fun-ci 2.0 has two ways in. People watch a new console, drawn by a Rust
|
data/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/fun_ci)
|
|
2
|
+
|
|
1
3
|
# Fun-CI
|
|
2
4
|
|
|
3
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.
|
data/lib/fun_ci/agent/git.rb
CHANGED
|
@@ -16,8 +16,9 @@ module FunCi
|
|
|
16
16
|
def subject(sha) = git("log", "-1", "--format=%s", sha).to_s
|
|
17
17
|
def toplevel = File.realpath(git("rev-parse", "--show-toplevel"))
|
|
18
18
|
|
|
19
|
-
# The branch checked out, as the git hooks name it
|
|
20
|
-
|
|
19
|
+
# The branch checked out, as the git hooks name it: "" when HEAD is
|
|
20
|
+
# detached, which trigger names (Pipeline::Commit).
|
|
21
|
+
def branch = git("branch", "--show-current").to_s
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
-
require "time"
|
|
5
4
|
require_relative "../evidence/document"
|
|
5
|
+
require_relative "../persistence/stage_job"
|
|
6
6
|
|
|
7
7
|
module FunCi
|
|
8
8
|
module Agent
|
|
@@ -62,9 +62,7 @@ module FunCi
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def self.seconds(job)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
(Time.parse(job[:completed_at]) - Time.parse(job[:started_at])).round(1)
|
|
65
|
+
Persistence::StageJob.elapsed_duration(job)&.round(1)
|
|
68
66
|
end
|
|
69
67
|
private_class_method :kept, :seconds
|
|
70
68
|
end
|
|
@@ -42,12 +42,12 @@ module FunCi
|
|
|
42
42
|
# Records the slow stage failed, unless its result was recorded meanwhile.
|
|
43
43
|
def self.slow_suite_died(db, job_id)
|
|
44
44
|
db.execute("UPDATE stage_jobs SET status = 'failed', completed_at = ? WHERE id = ? AND status = 'running'",
|
|
45
|
-
[Time.now.utc.iso8601, job_id])
|
|
45
|
+
[Time.now.utc.iso8601(3), job_id])
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def self.cancelled(db, run)
|
|
49
49
|
db.execute("UPDATE stage_jobs SET status = 'cancelled', completed_at = ? " \
|
|
50
|
-
"WHERE pipeline_run_id = ? AND #{ACTIVE}", [Time.now.utc.iso8601, run.id])
|
|
50
|
+
"WHERE pipeline_run_id = ? AND #{ACTIVE}", [Time.now.utc.iso8601(3), run.id])
|
|
51
51
|
PipelineRun.update_status(db, run.id, "cancelled")
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -50,9 +50,13 @@ module FunCi
|
|
|
50
50
|
with_setup_lock(db.filename("main")) do
|
|
51
51
|
[PIPELINE_RUNS_TABLE, STAGE_JOBS_TABLE].each { |table| db.execute(table) }
|
|
52
52
|
ADDED_COLUMNS.each { |table, column, type| add_column_if_missing(db, table, column, type) }
|
|
53
|
+
db.execute(NAME_DETACHED)
|
|
53
54
|
end
|
|
54
55
|
end
|
|
55
56
|
|
|
57
|
+
# Runs started on a detached HEAD before 2.0.1 were kept with no branch name.
|
|
58
|
+
NAME_DETACHED = "UPDATE pipeline_runs SET branch = 'detached' WHERE branch = ''"
|
|
59
|
+
|
|
56
60
|
def self.with_setup_lock(db_path)
|
|
57
61
|
File.open("#{db_path}.setup-lock", File::RDWR | File::CREAT, 0o644) do |lock|
|
|
58
62
|
lock.flock(File::LOCK_EX)
|
|
@@ -76,10 +76,12 @@ module FunCi
|
|
|
76
76
|
[new_status, Time.now.utc.iso8601(3), id])
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
# Never less than nothing: rows kept before ends were recorded to the
|
|
80
|
+
# millisecond can end in the second they started, before their start.
|
|
79
81
|
def self.elapsed_duration(job)
|
|
80
82
|
return nil unless job[:started_at] && job[:completed_at]
|
|
81
83
|
|
|
82
|
-
Time.parse(job[:completed_at]) - Time.parse(job[:started_at])
|
|
84
|
+
[Time.parse(job[:completed_at]) - Time.parse(job[:started_at]), 0].max
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
def self.row_to_hash(row)
|
|
@@ -14,6 +14,14 @@ module FunCi
|
|
|
14
14
|
|
|
15
15
|
Commit = Data.define(:sha, :branch)
|
|
16
16
|
|
|
17
|
+
# On a detached HEAD git names no branch, and the hooks pass "": the run
|
|
18
|
+
# is kept, shown and cancelled by others on the branch "detached".
|
|
19
|
+
class Commit
|
|
20
|
+
DETACHED = "detached"
|
|
21
|
+
|
|
22
|
+
def initialize(sha:, branch:) = super(sha: sha, branch: branch.empty? ? DETACHED : branch)
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
Io = Data.define(:stdout, :stderr) do
|
|
18
26
|
def initialize(stdout: $stdout, stderr: $stderr) = super
|
|
19
27
|
end
|
data/lib/fun_ci/version.rb
CHANGED