ruby-repo-kit 0.1.0 → 0.1.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 +16 -1
- data/README.md +8 -1
- data/lib/ruby_repo_kit/checks/lockfile.rb +33 -0
- data/lib/ruby_repo_kit/checks.rb +2 -0
- data/lib/ruby_repo_kit/github/policy.rb +11 -2
- data/lib/ruby_repo_kit/package.rb +15 -3
- data/lib/ruby_repo_kit/project.rb +19 -5
- data/lib/ruby_repo_kit/rake_tasks.rb +7 -6
- data/lib/ruby_repo_kit/version.rb +1 -1
- data/templates/cli/CHANGELOG.md.erb +1 -4
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffeaa438a6aabff617d58d356184c706f8e95d279bd74d292a9077c51e32c9d9
|
|
4
|
+
data.tar.gz: 72e653e7fea5dcc94dcaa99e4116c5dec4e670ca8e7e2bd4f0dcd78c7d9991ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0955bcebae7be6166af09ce531861e01a8396f8c49fdf42d2eb69467c8847ca39760bd7e27bff3a229abbf28162eb1f50d97394a553ed5e8a7cdb9967c906ec
|
|
7
|
+
data.tar.gz: d532a0e0a945ef4bd39d47eb28fc7bec468200419e0b77ad022fefb589346dc815dfbd684cd07295e19c0c49fe64ac8bbb055aaf60248e3bcdf8b8f6a7d2fc56
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.1.1] - 2026-10-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Preserve consumer package smoke checks with application environment overrides while keeping isolated gem loading.
|
|
10
|
+
- Configure hotfix-branch protection and required review-thread resolution without replacing the shared GitHub policy engine.
|
|
11
|
+
- Document supported release-task and packaging APIs for existing repositories with their own maintenance tasks.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Prevent automatic RubyGems and Bundler setup from loading dependencies or checkout code outside package smoke isolation.
|
|
16
|
+
- Detect missing or empty RubyGems lockfile checksums in doctor and repo:check before frozen CI fails, without fetching or changing dependencies.
|
|
17
|
+
- Keep generated CLI release notes under Unreleased so the first 0.1.0 release can be prepared without correcting a premature release history.
|
|
18
|
+
|
|
5
19
|
## [0.1.0] - 2026-10-05
|
|
6
20
|
|
|
7
21
|
### Added
|
|
@@ -10,5 +24,6 @@
|
|
|
10
24
|
- Share release preparation, recovery, artifact verification and isolated package checks through a development gem.
|
|
11
25
|
- Validate repository contracts and plan, verify or apply the initial GitHub maintenance policy.
|
|
12
26
|
|
|
13
|
-
[Unreleased]: https://github.com/hvpaiva/ruby-repo-kit/compare/v0.1.
|
|
27
|
+
[Unreleased]: https://github.com/hvpaiva/ruby-repo-kit/compare/v0.1.1...HEAD
|
|
14
28
|
[0.1.0]: https://github.com/hvpaiva/ruby-repo-kit/releases/tag/v0.1.0
|
|
29
|
+
[0.1.1]: https://github.com/hvpaiva/ruby-repo-kit/releases/tag/v0.1.1
|
data/README.md
CHANGED
|
@@ -83,7 +83,14 @@ the Ruby API, ownership boundaries and configuration example.
|
|
|
83
83
|
|
|
84
84
|
Doctor validates the local maintenance contract: required files, package
|
|
85
85
|
metadata, version consistency, package manifest, development dependency and
|
|
86
|
-
shared lint configuration.
|
|
86
|
+
shared lint configuration. If Gemfile.lock enables CHECKSUMS, it also detects
|
|
87
|
+
missing or empty checksums for RubyGems dependencies before frozen CI installation.
|
|
88
|
+
PATH and Git dependencies do not need gem artifact checksums. This inspection
|
|
89
|
+
does not install dependencies, access the network or modify the lockfile. To fix
|
|
90
|
+
incomplete checksums, run `bundle lock --add-checksums`, review and commit the
|
|
91
|
+
lockfile. This check verifies checksum presence; Bundler verifies artifact bytes
|
|
92
|
+
when installing. Existing lockfiles without CHECKSUMS retain Bundler's opt-in
|
|
93
|
+
behavior. Doctor does not certify code quality, run the full test
|
|
87
94
|
suite, or verify hosted settings.
|
|
88
95
|
|
|
89
96
|
The repository's check task combines lint, tests with coverage, structural checks
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler"
|
|
4
|
+
|
|
5
|
+
module RubyRepoKit
|
|
6
|
+
class Checks
|
|
7
|
+
# Parse only: never resolve, install, fetch or rewrite the consumer's bundle.
|
|
8
|
+
class Lockfile
|
|
9
|
+
def initialize(path:)
|
|
10
|
+
@path = path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def check
|
|
14
|
+
return true unless File.exist?(@path)
|
|
15
|
+
|
|
16
|
+
parsed = Bundler::LockfileParser.new(File.read(@path))
|
|
17
|
+
# Checksums are opt-in for existing lockfiles, including Bundler 2.6.
|
|
18
|
+
return true unless parsed.checksums
|
|
19
|
+
|
|
20
|
+
incomplete = parsed.specs.select do |spec|
|
|
21
|
+
spec.source.is_a?(Bundler::Source::Rubygems) &&
|
|
22
|
+
spec.source.checksum_store.to_lock(spec) == spec.name_tuple.lock_name
|
|
23
|
+
end
|
|
24
|
+
return true if incomplete.empty?
|
|
25
|
+
|
|
26
|
+
raise Error, "Gemfile.lock has empty or missing CHECKSUMS for: #{incomplete.map(&:full_name).join(', ')}. " \
|
|
27
|
+
"Run `bundle lock --add-checksums`, then review and commit Gemfile.lock before frozen CI."
|
|
28
|
+
rescue Bundler::BundlerError, ArgumentError => e
|
|
29
|
+
raise Error, "Cannot inspect Gemfile.lock checksums: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/ruby_repo_kit/checks.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "open3"
|
|
|
5
5
|
require "psych"
|
|
6
6
|
require "rbconfig"
|
|
7
7
|
require_relative "specification"
|
|
8
|
+
require_relative "checks/lockfile"
|
|
8
9
|
|
|
9
10
|
module RubyRepoKit
|
|
10
11
|
# Local structural checks. Evaluating a gemspec/Gemfile is trusted project code;
|
|
@@ -27,6 +28,7 @@ module RubyRepoKit
|
|
|
27
28
|
[@project.gemspec, @project.version_file, @project.changelog, "Gemfile", "Rakefile"].each do |file|
|
|
28
29
|
require_file(file)
|
|
29
30
|
end
|
|
31
|
+
Lockfile.new(path: @project.path("Gemfile.lock")).check
|
|
30
32
|
specification = Specification.load(project: @project)
|
|
31
33
|
metadata(specification)
|
|
32
34
|
manifest(specification)
|
|
@@ -21,12 +21,13 @@ module RubyRepoKit
|
|
|
21
21
|
def main_ruleset
|
|
22
22
|
{
|
|
23
23
|
"name" => "main", "target" => "branch", "enforcement" => "active", "bypass_actors" => [],
|
|
24
|
-
"conditions" => { "ref_name" => { "include" =>
|
|
24
|
+
"conditions" => { "ref_name" => { "include" => protected_branches, "exclude" => [] } },
|
|
25
25
|
"rules" => [
|
|
26
26
|
{ "type" => "pull_request", "parameters" => {
|
|
27
27
|
"required_approving_review_count" => 0, "dismiss_stale_reviews_on_push" => false,
|
|
28
28
|
"require_code_owner_review" => false, "require_last_push_approval" => false,
|
|
29
|
-
"required_review_thread_resolution" =>
|
|
29
|
+
"required_review_thread_resolution" => @project.require_review_thread_resolution,
|
|
30
|
+
"allowed_merge_methods" => ["merge"]
|
|
30
31
|
} },
|
|
31
32
|
{ "type" => "required_status_checks", "parameters" => {
|
|
32
33
|
"strict_required_status_checks_policy" => true, "do_not_enforce_on_create" => false,
|
|
@@ -47,6 +48,14 @@ module RubyRepoKit
|
|
|
47
48
|
"rules" => %w[creation update deletion].map { |type| { "type" => type } }
|
|
48
49
|
}
|
|
49
50
|
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def protected_branches
|
|
55
|
+
["refs/heads/main"].tap do |branches|
|
|
56
|
+
branches << "refs/heads/hotfix/*" if @project.protect_hotfix_branches
|
|
57
|
+
end
|
|
58
|
+
end
|
|
50
59
|
end
|
|
51
60
|
end
|
|
52
61
|
end
|
|
@@ -25,15 +25,17 @@ module RubyRepoKit
|
|
|
25
25
|
target
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def check(artifact: nil)
|
|
28
|
+
def check(artifact: nil, environment: {})
|
|
29
|
+
environment = validated_environment(environment)
|
|
29
30
|
Dir.mktmpdir("ruby-repo-package-") do |dir|
|
|
30
31
|
package = artifact ? File.expand_path(artifact, @project.root) : build(output: File.join(dir, "package.gem"))
|
|
31
32
|
spec = verify(package)
|
|
32
33
|
home = File.join(dir, "gems")
|
|
33
34
|
cache_dependencies(dir)
|
|
34
35
|
Bundler.with_unbundled_env do
|
|
35
|
-
env =
|
|
36
|
-
|
|
36
|
+
env = environment.merge("GEM_HOME" => home, "GEM_PATH" => home, "RUBYOPT" => nil, "RUBYLIB" => nil,
|
|
37
|
+
"BUNDLER_SETUP" => nil, "RUBYGEMS_GEMDEPS" => nil,
|
|
38
|
+
"XDG_CONFIG_HOME" => File.join(dir, "config"), "NO_COLOR" => "1")
|
|
37
39
|
# GEM_HOME selects the destination. --install-dir would make RubyGems
|
|
38
40
|
# ignore installed specifications, including Ruby's uncached default gems.
|
|
39
41
|
execute(env, [*gem_command, "install", "--local", "--no-document", "--norc",
|
|
@@ -70,6 +72,16 @@ module RubyRepoKit
|
|
|
70
72
|
|
|
71
73
|
private
|
|
72
74
|
|
|
75
|
+
def validated_environment(environment)
|
|
76
|
+
valid = environment.is_a?(Hash) && environment.all? do |key, value|
|
|
77
|
+
key.is_a?(String) && key.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/) &&
|
|
78
|
+
(value.nil? || (value.is_a?(String) && !value.include?("\0")))
|
|
79
|
+
end
|
|
80
|
+
raise Error, "Package environment must map variable names to strings or nil" unless valid
|
|
81
|
+
|
|
82
|
+
environment.to_h { |key, value| [key.dup, value&.dup] }
|
|
83
|
+
end
|
|
84
|
+
|
|
73
85
|
def specification
|
|
74
86
|
spec = Specification.load(project: @project)
|
|
75
87
|
raise Error, "Gemspec name differs from project name" unless spec.name == @project.name
|
|
@@ -10,10 +10,11 @@ module RubyRepoKit
|
|
|
10
10
|
DEFAULT_CHECKS = ["quality", "commits", "audit", "fresh-dependencies", "test (ubuntu-latest, 3.4)",
|
|
11
11
|
"test (ubuntu-latest, 4.0)", "test (macos-latest, 4.0)"].freeze
|
|
12
12
|
KEYS = %w[schema name repository version_file gemspec changelog generated_paths
|
|
13
|
-
generate_command check_command required_checks workflow environment
|
|
13
|
+
generate_command check_command required_checks workflow environment
|
|
14
|
+
protect_hotfix_branches require_review_thread_resolution].freeze
|
|
14
15
|
attr_reader :root, :name, :repository, :version_file, :gemspec, :changelog,
|
|
15
16
|
:generated_paths, :generate_command, :check_command, :required_checks,
|
|
16
|
-
:workflow, :environment
|
|
17
|
+
:workflow, :environment, :protect_hotfix_branches, :require_review_thread_resolution
|
|
17
18
|
|
|
18
19
|
def self.load(root: Dir.pwd)
|
|
19
20
|
source = File.join(root, CONFIG_FILE)
|
|
@@ -25,9 +26,7 @@ module RubyRepoKit
|
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def initialize(root:, settings:)
|
|
28
|
-
|
|
29
|
-
raise Error, "Unknown project settings: #{settings.keys - KEYS}" unless (settings.keys - KEYS).empty?
|
|
30
|
-
raise Error, "Unsupported configuration schema (expected 1)" unless settings.fetch("schema", nil) == 1
|
|
29
|
+
validate_settings(settings)
|
|
31
30
|
|
|
32
31
|
@root = File.realpath(root).freeze
|
|
33
32
|
@name = identifier(settings["name"], /\A[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*\z/, "gem name")
|
|
@@ -43,6 +42,8 @@ module RubyRepoKit
|
|
|
43
42
|
@workflow = identifier(settings.fetch("workflow", "release.yml"), /\A[a-zA-Z0-9_-]+\.ya?ml\z/,
|
|
44
43
|
"workflow filename")
|
|
45
44
|
@environment = identifier(settings.fetch("environment", "release"), /\A[a-zA-Z0-9_-]+\z/, "environment")
|
|
45
|
+
@protect_hotfix_branches = boolean_setting(settings, "protect_hotfix_branches")
|
|
46
|
+
@require_review_thread_resolution = boolean_setting(settings, "require_review_thread_resolution")
|
|
46
47
|
freeze
|
|
47
48
|
end
|
|
48
49
|
|
|
@@ -67,6 +68,19 @@ module RubyRepoKit
|
|
|
67
68
|
|
|
68
69
|
private
|
|
69
70
|
|
|
71
|
+
def validate_settings(settings)
|
|
72
|
+
raise Error, "Project configuration must be a mapping" unless settings.is_a?(Hash)
|
|
73
|
+
raise Error, "Unknown project settings: #{settings.keys - KEYS}" unless (settings.keys - KEYS).empty?
|
|
74
|
+
raise Error, "Unsupported configuration schema (expected 1)" unless settings.fetch("schema", nil) == 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def boolean_setting(settings, name)
|
|
78
|
+
value = settings.fetch(name, true)
|
|
79
|
+
raise Error, "#{name} must be true or false" unless [true, false].include?(value)
|
|
80
|
+
|
|
81
|
+
value
|
|
82
|
+
end
|
|
83
|
+
|
|
70
84
|
def identifier(value, pattern, description)
|
|
71
85
|
raise Error, "Invalid #{description}: #{value.inspect}" unless value.is_a?(String) && pattern.match?(value)
|
|
72
86
|
|
|
@@ -10,10 +10,7 @@ module RubyRepoKit
|
|
|
10
10
|
extend Rake::DSL
|
|
11
11
|
|
|
12
12
|
def self.install(project:)
|
|
13
|
-
|
|
14
|
-
raise Error,
|
|
15
|
-
"A release task already exists; remove competing release tooling before installing RubyRepoKit tasks"
|
|
16
|
-
end
|
|
13
|
+
install_release(project: project)
|
|
17
14
|
|
|
18
15
|
desc "Validate local repository maintenance contracts"
|
|
19
16
|
task "repo:check" do
|
|
@@ -43,11 +40,15 @@ module RubyRepoKit
|
|
|
43
40
|
sh "bundle", "exec", "bundler-audit", "check", "--update"
|
|
44
41
|
end
|
|
45
42
|
|
|
46
|
-
install_release(project)
|
|
47
43
|
install_github(project)
|
|
48
44
|
end
|
|
49
45
|
|
|
50
|
-
def self.install_release(project)
|
|
46
|
+
def self.install_release(project:)
|
|
47
|
+
if Rake::Task.task_defined?("release")
|
|
48
|
+
raise Error,
|
|
49
|
+
"A release task already exists; remove competing release tooling before installing RubyRepoKit tasks"
|
|
50
|
+
end
|
|
51
|
+
|
|
51
52
|
require_relative "release"
|
|
52
53
|
|
|
53
54
|
desc "Verify the release tag, version and changelog"
|
|
@@ -4,11 +4,8 @@ All notable user-visible changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
## [0.1.0] - <%= Time.now.strftime("%Y-%m-%d") %>
|
|
8
|
-
|
|
9
7
|
### Added
|
|
10
8
|
|
|
11
9
|
- Initial text-printing CLI with help, version and predictable exit statuses.
|
|
12
10
|
|
|
13
|
-
[Unreleased]: https://github.com/<%= @repository %>/
|
|
14
|
-
[0.1.0]: https://github.com/<%= @repository %>/releases/tag/v0.1.0
|
|
11
|
+
[Unreleased]: https://github.com/<%= @repository %>/commits/main
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-repo-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Highlander Paiva
|
|
@@ -174,6 +174,7 @@ files:
|
|
|
174
174
|
- lib/ruby_repo_kit.rb
|
|
175
175
|
- lib/ruby_repo_kit/checks.rb
|
|
176
176
|
- lib/ruby_repo_kit/checks/commits.rb
|
|
177
|
+
- lib/ruby_repo_kit/checks/lockfile.rb
|
|
177
178
|
- lib/ruby_repo_kit/cli.rb
|
|
178
179
|
- lib/ruby_repo_kit/commands.rb
|
|
179
180
|
- lib/ruby_repo_kit/github.rb
|