Skip to content

Commit 80f95d2

Browse files
committed
πŸ› Skip published releases past tag
1 parent 5601018 commit 80f95d2

3 files changed

Lines changed: 29 additions & 37 deletions

File tree

β€ŽCHANGELOG.mdβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Please file a bug if you notice a violation of semantic versioning.
2828

2929
### Fixed
3030

31+
- `kettle-family release --publish` now skips already-published members even
32+
when post-release commits leave HEAD ahead of the release tag, allowing later
33+
pending members to continue.
34+
3135
### Security
3236

3337
## [1.0.3] - 2026-07-21

β€Žlib/kettle/family/workflow.rbβ€Ž

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -792,40 +792,26 @@ def already_released_result(member)
792792
if current_release_head
793793
stdout = "#{member.name} #{member.version} is already published and current HEAD matches #{tag}; skipping release"
794794
reason = "already released"
795-
skipped = true
796-
elsif release_pending?(member)
797-
stdout = "#{member.name} #{member.version} is already published, but current HEAD is not #{tag}. " \
798-
"Bump the version with `kettle-family bump-version patch --execute --only #{member.name}` before releasing."
799-
reason = "published version does not match current HEAD"
800-
skipped = false
801795
else
802-
stdout = "#{member.name} #{member.version} is already published and release-state reports no pending release; skipping release"
803-
reason = "already released; no pending release"
804-
skipped = true
796+
stdout = "#{member.name} #{member.version} is already published and current HEAD is newer than #{tag}; skipping release"
797+
reason = "already released; current HEAD is newer than release tag"
805798
end
806799

807800
CommandResult.new(
808801
member_name: member.name,
809802
phase: "release_skip",
810803
command: ["internal", "released-version-check", member.version],
811804
workdir: member.root,
812-
status: skipped ? 0 : 1,
813-
success: skipped,
805+
status: 0,
806+
success: true,
814807
stdout: stdout,
815808
stderr: "",
816809
elapsed_seconds: 0.0,
817-
skipped: skipped,
810+
skipped: true,
818811
reason: reason
819812
)
820813
end
821814

822-
def release_pending?(member)
823-
results = ReleaseStateCheck.new(members: [member], config: config).results
824-
return true unless results.all?(&:ok?)
825-
826-
results.any? { |result| result.state.fetch("pending_release", true) }
827-
end
828-
829815
def release_tag_name(version)
830816
"v#{version}"
831817
end

β€Žspec/kettle/family/workflow_release_spec.rbβ€Ž

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@
424424
expect(results.first.stdout).to include("already published")
425425
end
426426

427-
it "fails published-version skips when local HEAD is newer than the release tag" do
427+
it "skips already published versions when local HEAD is newer than the release tag" do
428428
write_release_config(publish_command: [RbConfig.ruby, "-e", "abort 'should not run'"])
429429
config = Kettle::Family::Config.load(root: @tmpdir)
430430
member = ready_member("alpha")
@@ -434,35 +434,37 @@
434434
allow(workflow).to receive(:git_work_tree?).with(member.root).and_return(true)
435435
allow(workflow).to receive(:git_rev_parse).with(member.root, "refs/tags/v1.0.0^{}").and_return("tag-sha")
436436
allow(workflow).to receive(:git_rev_parse).with(member.root, "HEAD").and_return("head-sha")
437-
allow(workflow).to receive(:release_pending?).with(member).and_return(true)
438437

439438
results = workflow.results
440439

441440
expect(results.map(&:phase)).to eq(["release_skip"])
442-
expect(results.first).not_to be_ok
443-
expect(results.first.stdout).to include("current HEAD is not v1.0.0")
444-
expect(results.first.stdout).to include("bump-version patch --execute --only alpha")
441+
expect(results.first).to be_ok
442+
expect(results.first.skipped).to be(true)
443+
expect(results.first.reason).to eq("already released; current HEAD is newer than release tag")
444+
expect(results.first.stdout).to include("current HEAD is newer than v1.0.0")
445+
expect(results.first.stdout).not_to include("bump-version")
445446
end
446447

447-
it "skips already published versions when release state reports no pending release" do
448-
write_release_config(publish_command: [RbConfig.ruby, "-e", "abort 'should not run'"])
448+
it "continues release after skipping an already published version whose HEAD moved past the tag" do
449+
write_release_config(publish_command: [RbConfig.ruby, "-e", "puts 'publish'"])
449450
config = Kettle::Family::Config.load(root: @tmpdir)
450-
member = ready_member("alpha")
451-
workflow = described_class.new(command: "release", config: config, members: [member], execute: true, publish: true)
451+
alpha = ready_member("alpha")
452+
beta = ready_member("beta")
453+
workflow = described_class.new(command: "release", config: config, members: [alpha, beta], execute: true, publish: true)
452454
allow(workflow).to receive(:prompt_for_gem_signing_password)
453455
allow(workflow).to receive(:released_version?).with("alpha", "1.0.0").and_return(true)
454-
allow(workflow).to receive(:git_work_tree?).with(member.root).and_return(true)
455-
allow(workflow).to receive(:git_rev_parse).with(member.root, "refs/tags/v1.0.0^{}").and_return("tag-sha")
456-
allow(workflow).to receive(:git_rev_parse).with(member.root, "HEAD").and_return("head-sha")
457-
allow(workflow).to receive(:release_pending?).with(member).and_return(false)
456+
allow(workflow).to receive(:released_version?).with("beta", "1.0.0").and_return(false)
457+
allow(workflow).to receive(:git_work_tree?).with(alpha.root).and_return(true)
458+
allow(workflow).to receive(:git_rev_parse).with(alpha.root, "refs/tags/v1.0.0^{}").and_return("tag-sha")
459+
allow(workflow).to receive(:git_rev_parse).with(alpha.root, "HEAD").and_return("head-sha")
458460

459461
results = workflow.results
460462

461-
expect(results.map(&:phase)).to eq(["release_skip"])
462-
expect(results.first).to be_ok
463-
expect(results.first.skipped).to be(true)
464-
expect(results.first.reason).to eq("already released; no pending release")
465-
expect(results.first.stdout).to include("no pending release")
463+
expect(results.map(&:phase)).to include("release_skip", "check", "release_publish")
464+
alpha_skip = results.find { |result| result.member_name == "alpha" && result.phase == "release_skip" }
465+
expect(alpha_skip).to be_ok
466+
expect(alpha_skip.skipped).to be(true)
467+
expect(results.find { |result| result.member_name == "beta" && result.phase == "release_publish" }).to be_ok
466468
end
467469

468470
it "rediscovers member metadata after each target branch checkout" do

0 commit comments

Comments
Β (0)