-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRakefile
More file actions
173 lines (147 loc) · 5.93 KB
/
Copy pathRakefile
File metadata and controls
173 lines (147 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# frozen_string_literal: true
require "rspec/core/rake_task"
require "rubocop/rake_task"
RSpec::Core::RakeTask.new(:spec)
# Lint only the gem's own source — NOT the dogfood docs/ site. docs/ is a separate
# consuming app with its own bundle and .rubocop.yml (it `inherit_gem`s
# rubocop-rails-omakase, absent from the gem's bundle). Passing explicit paths
# stops RuboCop from discovering and loading docs/.rubocop.yml, whose gem
# inheritance can't resolve here (and crashes CI). docs/ lints itself.
RuboCop::RakeTask.new do |task|
task.patterns = %w[app lib spec Rakefile Gemfile docs-kit.gemspec]
end
# Colored status helpers shared by the build/release tasks.
module ReleaseHelpers
def info(msg) = puts "\e[34m→\e[0m #{msg}"
def success(msg) = puts "\e[32m✓\e[0m #{msg}"
def skip(msg) = puts "\e[33m⊘\e[0m #{msg} \e[33m(skipped)\e[0m"
def warn(msg) = puts "\e[33m⚠\e[0m #{msg}"
def error(msg) = puts "\e[31m✗\e[0m #{msg}"
def header(msg) = puts "\n\e[1;36m#{msg}\e[0m\n#{'─' * msg.length}"
end
desc "Build gem and verify contents"
task :build do
sh("gem build docs-kit.gemspec --strict")
gem_file = Dir["docs-kit-*.gem"].first
abort "Gem file not found after build" unless gem_file
sh("gem unpack #{gem_file} --target /tmp/gem-verify")
puts "\n=== Gem contents ==="
sh("find /tmp/gem-verify -type f | sort")
sh("rm -rf /tmp/gem-verify #{gem_file}")
end
desc "Release a new version (rake release[1.2.3] or rake release[pre] or rake release[1.2.3,force])"
task :release, %i[version force] do |_t, args|
include ReleaseHelpers
require_relative "lib/docs_kit/version"
new_version = args[:version]
abort "\e[31mUsage: rake release[X.Y.Z] or rake release[X.Y.Z,force]\e[0m" unless new_version
force = args[:force]&.to_s&.downcase == "force"
current_branch = `git branch --show-current`.strip
unless current_branch == "main"
abort "\e[31mAborting: must be on main branch to release (currently on #{current_branch})\e[0m"
end
dirty = `git status --porcelain`.strip
abort "\e[31mAborting: working directory is not clean.\e[0m\n#{dirty}" unless dirty.empty?
current = DocsKit::VERSION
prerelease = new_version.match?(/alpha|beta|rc|pre/) || new_version == "pre"
if new_version == "pre"
new_version = current
prerelease = true
end
tag = "v#{new_version}"
version_file = "lib/docs_kit/version.rb"
title = "Release #{tag}"
title += " (force)" if force
header title
info "Current version: #{current}"
info "New version: #{new_version}"
info "Pre-release: #{prerelease}"
# Step 0: Force cleanup — delete existing release and tag
if force
header "Force cleanup"
if system("gh release view #{tag} >/dev/null 2>&1")
sh("gh release delete #{tag} --yes --cleanup-tag")
success "Deleted release and remote tag #{tag}"
else
skip "No release #{tag} to delete"
end
if system("git rev-parse #{tag} >/dev/null 2>&1")
sh("git tag -d #{tag}")
success "Deleted local tag #{tag}"
else
skip "No local tag #{tag} to delete"
end
end
# Step 1: Update version file
header "Version"
if new_version == current
skip "Version already #{new_version}"
else
content = File.read(version_file)
content.sub!(/VERSION = ".*"/, "VERSION = \"#{new_version}\"")
File.write(version_file, content)
success "Updated #{version_file}"
end
# Step 2: Refresh lockfiles and verify the gem builds cleanly. docs/ is the
# dogfood site — refresh its lock too so `bundle install` there resolves the
# new gem version. The lockfiles are gitignored (a gem doesn't commit them),
# so this is verification only; nothing here gets committed.
header "Build verification"
sh("bundle install --quiet")
success "Gemfile.lock refreshed"
if File.exist?("docs/Gemfile.lock")
sh("cd docs && bundle install --quiet")
success "docs/Gemfile.lock refreshed"
end
sh("gem build docs-kit.gemspec --strict")
sh("rm -f docs-kit-*.gem")
success "Gem builds cleanly"
# Step 3: Commit version bump. Only version.rb is committed — Gemfile.lock and
# docs/Gemfile.lock are gitignored (conventional for a gem), so staging them
# would abort on `git add`. The build above already verified they resolve.
header "Git commit"
version_changed =
!`git diff #{version_file}`.strip.empty? || !`git diff --cached #{version_file}`.strip.empty?
if version_changed
sh("git add #{version_file}")
sh("git commit -m 'chore: bump version to #{new_version}'")
success "Committed version bump"
else
skip "No version change to commit"
end
# Step 4: Push to origin
header "Git push"
local_sha = `git rev-parse HEAD`.strip
remote_sha = `git rev-parse origin/main 2>/dev/null`.strip
if local_sha == remote_sha
skip "origin/main already at #{local_sha[0..6]}"
else
sh("git push origin main")
success "Pushed to origin/main"
end
# Step 5: Create release (the Release workflow publishes to RubyGems via OIDC
# trusted publishing on `release: published`).
header "Release"
tag_exists = system("git rev-parse #{tag} >/dev/null 2>&1")
release_exists = system("gh release view #{tag} >/dev/null 2>&1")
if release_exists
skip "Release #{tag} already exists (use force to re-create)"
elsif tag_exists
info "Tag #{tag} exists, creating release from it"
pre_flag = prerelease ? "--prerelease" : ""
sh("gh release create #{tag} --generate-notes #{pre_flag}".strip)
success "Release #{tag} created from existing tag"
else
pre_flag = prerelease ? "--prerelease" : ""
sh("gh release create #{tag} --generate-notes --target main #{pre_flag}".strip)
success "Release #{tag} created"
end
puts ""
success "\e[1mRelease #{tag} complete!\e[0m CI will handle the rest:"
puts " • Run tests"
puts " • Build + verify gem"
puts " • Sign with Sigstore"
puts " • Publish to RubyGems (trusted publishing)"
puts " • Upload assets to the release"
end
task default: %i[spec rubocop]