git 5.1.0 → 5.2.0

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/CONTRIBUTING.md +185 -165
  4. data/LICENSE +1 -1
  5. data/README.md +151 -151
  6. data/git.gemspec +35 -3
  7. data/lib/git/command_line/base.rb +1 -2
  8. data/lib/git/commands/base.rb +1 -1
  9. data/lib/git/commands/cat_file/raw.rb +8 -7
  10. data/lib/git/repository/branching.rb +79 -5
  11. data/lib/git/version.rb +1 -1
  12. metadata +5 -94
  13. data/.claude/commands/address-copilot-reviews.md +0 -14
  14. data/.claude/settings.json +0 -16
  15. data/.claude/skills +0 -1
  16. data/.commitlintrc.yml +0 -38
  17. data/.dockerignore +0 -27
  18. data/.github/copilot-instructions.md +0 -30
  19. data/.github/hooks/bin-setup-on-worktree.json +0 -11
  20. data/.github/hooks/run-bin-setup-once.sh +0 -20
  21. data/.github/issue_template.md +0 -15
  22. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +0 -188
  23. data/.github/pull_request_template.md +0 -21
  24. data/.github/skills/breaking-change-analysis/SKILL.md +0 -99
  25. data/.github/skills/ci-cd-troubleshooting/SKILL.md +0 -264
  26. data/.github/skills/command-implementation/REFERENCE.md +0 -994
  27. data/.github/skills/command-implementation/SKILL.md +0 -230
  28. data/.github/skills/command-test-conventions/SKILL.md +0 -664
  29. data/.github/skills/command-yard-documentation/SKILL.md +0 -434
  30. data/.github/skills/dependency-management/SKILL.md +0 -72
  31. data/.github/skills/development-workflow/SKILL.md +0 -512
  32. data/.github/skills/facade-implementation/REFERENCE.md +0 -837
  33. data/.github/skills/facade-implementation/SKILL.md +0 -269
  34. data/.github/skills/facade-test-conventions/SKILL.md +0 -391
  35. data/.github/skills/facade-yard-documentation/SKILL.md +0 -435
  36. data/.github/skills/make-skill-template/SKILL.md +0 -226
  37. data/.github/skills/pr-readiness-review/SKILL.md +0 -205
  38. data/.github/skills/project-context/SKILL.md +0 -306
  39. data/.github/skills/pull-request-review/SKILL.md +0 -168
  40. data/.github/skills/rebase/SKILL.md +0 -148
  41. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +0 -131
  42. data/.github/skills/release-management/SKILL.md +0 -125
  43. data/.github/skills/resolve-feedback/SKILL.md +0 -288
  44. data/.github/skills/review-arguments-dsl/CHECKLIST.md +0 -788
  45. data/.github/skills/review-arguments-dsl/SKILL.md +0 -214
  46. data/.github/skills/review-cross-command-consistency/SKILL.md +0 -139
  47. data/.github/skills/reviewing-skills/SKILL.md +0 -214
  48. data/.github/skills/rspec-unit-testing-standards/SKILL.md +0 -685
  49. data/.github/skills/tdd-refactor-step/SKILL.md +0 -236
  50. data/.github/skills/test-debugging/SKILL.md +0 -161
  51. data/.github/skills/yard-documentation/SKILL.md +0 -981
  52. data/.github/skills/yard-documentation/element-rules.md +0 -162
  53. data/.github/skills-deprecated/README.md +0 -21
  54. data/.github/skills-deprecated/extract-command-from-lib/SKILL.md +0 -487
  55. data/.github/skills-deprecated/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +0 -22
  56. data/.github/skills-deprecated/extract-facade-from-base-lib/SKILL.md +0 -600
  57. data/.github/skills-deprecated/review-backward-compatibility/SKILL.md +0 -275
  58. data/.github/workflows/continuous_integration.yml +0 -358
  59. data/.github/workflows/enforce_conventional_commits.yml +0 -35
  60. data/.github/workflows/experimental_continuous_integration.yml +0 -59
  61. data/.github/workflows/release.yml +0 -52
  62. data/.github/workflows/warm_bundler_caches.yml +0 -82
  63. data/.gitignore +0 -30
  64. data/.husky/commit-msg +0 -1
  65. data/.husky/pre-commit +0 -13
  66. data/.release-please-config.json +0 -36
  67. data/.release-please-manifest.json +0 -3
  68. data/.rspec +0 -2
  69. data/.rubocop.yml +0 -44
  70. data/.rubocop_todo.yml +0 -30
  71. data/.yard-lint.yml +0 -75
  72. data/CLAUDE.md +0 -11
  73. data/Gemfile +0 -22
  74. data/Rakefile +0 -41
  75. data/docker/test/Dockerfile +0 -32
  76. data/docker/test/docker-compose.yml +0 -0
  77. data/package.json +0 -10
  78. data/redesign/1_architecture_existing.md +0 -102
  79. data/redesign/2_architecture_redesign.md +0 -449
  80. data/redesign/3_architecture_implementation.md +0 -1623
  81. data/redesign/Phase 4 - Step A.md +0 -366
  82. data/redesign/Phase 4 - Step B.md +0 -921
  83. data/redesign/Phase 4 - Step C.md +0 -833
  84. data/redesign/beta_release.md +0 -107
  85. data/redesign/branch_parse_refactor_plan.md +0 -163
  86. data/redesign/c1a-public-api-scope.tsv +0 -256
  87. data/redesign/c1c2_audit.md +0 -566
  88. data/redesign/c1c2_bucket6_lib_orphans.md +0 -626
  89. data/redesign/config_design.rb +0 -501
  90. data/redesign/index.md +0 -34
  91. data/redesign/info_object_migration_plan.md +0 -126
  92. data/redesign/integration_test_analysis.md +0 -521
  93. data/redesign/phase-4-step-b-test-audit.tsv +0 -485
  94. data/redesign/remote_refactor_plan.md +0 -164
  95. data/redesign/reverse_dependencies.sql +0 -44
  96. data/tasks/gem_tasks.rake +0 -14
  97. data/tasks/npm_tasks.rake +0 -7
  98. data/tasks/rspec.rake +0 -111
  99. data/tasks/rubocop.rake +0 -5
  100. data/tasks/test_gem.rake +0 -12
  101. data/tasks/yard.rake +0 -57
@@ -1,275 +0,0 @@
1
- ---
2
- name: review-backward-compatibility
3
- description: "Audits Git::Lib methods for backward compatibility after commands are moved to Git::Commands::* classes. Use to verify that migrated commands maintain their existing public API."
4
- ---
5
-
6
- # Review Backward Compatibility
7
-
8
- Review `Git::Lib` methods for backward compatibility after commands are moved to
9
- `Git::Commands::*` classes. This skill guides the process of restoring backward
10
- compatibility for a specific set of git commands while maintaining the benefits of
11
- the new command infrastructure.
12
-
13
- ## Contents
14
-
15
- - [How to use this skill](#how-to-use-this-skill)
16
- - [Related skills](#related-skills)
17
- - [Objective](#objective)
18
- - [Current Command Architecture Note](#current-command-architecture-note)
19
- - [Instructions](#instructions)
20
- - [Branch setup](#branch-setup)
21
- - [Step 1: Identify Methods Added Since v4.3.0](#step-1-identify-methods-added-since-v430)
22
- - [Step 2: Remove New Methods](#step-2-remove-new-methods)
23
- - [Step 3: Restore Backward-Compatible Implementations](#step-3-restore-backward-compatible-implementations)
24
- - [Step 4: Add Required Dependencies](#step-4-add-required-dependencies)
25
- - [Step 5: Verify the Changes](#step-5-verify-the-changes)
26
- - [Example: Stash Commands](#example-stash-commands)
27
- - [Legacy Methods (v4.3.0)](#legacy-methods-v430)
28
- - [New Methods (removed)](#new-methods-removed)
29
- - [Implementation Pattern](#implementation-pattern)
30
- - [Key Principles](#key-principles)
31
- - [Validation Checklist](#validation-checklist)
32
- - [Usage](#usage)
33
-
34
- ## How to use this skill
35
-
36
- Attach this file to your Copilot Chat context, then invoke it with the git
37
- command name(s) to audit. Example:
38
-
39
- ```text
40
- Remove methods added to Git::Lib since v4.3.0 for the `branch` git command
41
- and ensure the remaining methods are backward compatible.
42
- ```
43
-
44
- Replace `branch` with the specific git command(s) you want to audit (e.g.,
45
- `worktree`, `tag`, `merge`, `reset`).
46
-
47
- ## Related skills
48
-
49
- - [Refactor Command to CommandLineResult](../refactor-command-to-commandlineresult/SKILL.md) — migrating command classes to Base;
50
- the counterpart to this skill's `Git::Lib` facade focus
51
- - [Command Implementation](../command-implementation/SKILL.md) — class structure, phased rollout gates, and
52
- internal compatibility contracts
53
-
54
- ## Objective
55
-
56
- For the specified git command(s), remove methods added to `Git::Lib` since v4.3.0 and ensure that remaining methods (which existed in v4.3.0) are backward compatible.
57
-
58
- ## Current Command Architecture Note
59
-
60
- When auditing modern implementations, assume command classes follow `Git::Commands::Base`:
61
-
62
- - classes use `class < Git::Commands::Base` with `arguments do ... end`
63
- - command entrypoint is `call(*, **)` (inherited from `Base`)
64
- - exit-status behavior is centralized via `allow_exit_status` declarations on the class
65
-
66
- Because of this, backward-compatibility adaptation should happen in `Git::Lib`
67
- methods (facade/adapter layer), not by reintroducing legacy execution logic inside
68
- command classes.
69
-
70
- ## Instructions
71
-
72
- ### Branch setup
73
-
74
- All work must be done on a feature branch. **Never commit or push directly to
75
- `main`.**
76
-
77
- Before starting, create a new branch:
78
-
79
- ```bash
80
- git checkout -b <feature-branch-name>
81
- ```
82
-
83
- All commits in this workflow go on the feature branch. When work is complete,
84
- open a pull request — do not merge or push directly into `main`.
85
-
86
- ### Step 1: Identify Methods Added Since v4.3.0
87
-
88
- 1. Check out the v4.3.0 tag to examine the historical state:
89
-
90
- **Warning:** `git checkout` modifies the working tree. Stash or commit any
91
- uncommitted changes first (`git stash`).
92
-
93
- ```bash
94
- git checkout v4.3.0
95
- ```
96
-
97
- 2. Search `lib/git/lib.rb` for all methods related to the specified git command(s):
98
- ```bash
99
- grep -n "def <command_name>" lib/git/lib.rb
100
- ```
101
-
102
- 3. Document each method found, including:
103
- - Method name and signature
104
- - Return value type (String, Array, Hash, Boolean, etc.)
105
- - Exact return value format (e.g., `Array<[Integer, String]>`, `String` with specific content)
106
-
107
- 4. Return to your feature branch:
108
- ```bash
109
- git checkout -
110
- ```
111
-
112
- 5. Search for the same command methods in the current version:
113
- ```bash
114
- grep -n "def <command_name>" lib/git/lib.rb
115
- ```
116
-
117
- 6. Create two lists:
118
- - **Legacy methods**: Methods that existed in v4.3.0 (must be preserved and made compatible)
119
- - **New methods**: Methods that don't exist in v4.3.0 (should be removed)
120
-
121
- ### Step 2: Remove New Methods
122
-
123
- 1. Identify all new methods that were added after v4.3.0
124
- 2. Remove these methods entirely from `lib/git/lib.rb`
125
- 3. Remove any `require` statements that are only used by the removed methods
126
-
127
- ### Step 3: Restore Backward-Compatible Implementations
128
-
129
- For each legacy method that needs to be preserved:
130
-
131
- 1. **Use modern command infrastructure internally:**
132
- - Call the appropriate `Git::Commands::<Command>::*` class
133
- - Ensure all necessary command classes are required at the top of the file
134
-
135
- 2. **Convert return values to match v4.3.0 exactly:**
136
- - Compare the return type from the modern command with the v4.3.0 return type
137
- - If they differ, add conversion logic to transform the result
138
- - Modern commands typically return `CommandLineResult` objects with `.stdout`, `.stderr`, and `.status`
139
- - Legacy methods may have returned raw strings, arrays, or other types
140
-
141
- 3. **Create helper methods if needed:**
142
- - For complex conversions (e.g., parsing output into specific formats), create private helper methods
143
- - Name helpers clearly (e.g., `<command>_info_to_legacy`)
144
- - Place helpers near the bottom of the file in the private section
145
-
146
- ### Step 4: Add Required Dependencies
147
-
148
- 1. Ensure all necessary `require` statements are present:
149
- ```ruby
150
- require_relative 'git/commands/<command>/<action>'
151
- require_relative 'git/parsers/<command>' # if using parsers
152
- ```
153
-
154
- 2. Only keep requires for:
155
- - Command classes used by the legacy methods
156
- - Parsers needed for conversion
157
- - Remove requires for deleted commands
158
-
159
- ### Step 5: Verify the Changes
160
-
161
- 1. Check the diff to ensure:
162
- ```bash
163
- git diff lib/git/lib.rb
164
- ```
165
- - All new methods are removed
166
- - All legacy methods are present with correct signatures
167
- - Return value conversions are in place
168
- - Correct require statements are present
169
-
170
- 2. Verify that the implementation uses modern command classes:
171
- ```bash
172
- git diff lib/git/lib.rb | grep -E "^\+.*Git::Commands::<Command>"
173
- ```
174
-
175
- 3. Check the net line change (should be negative if removing methods):
176
- ```bash
177
- git diff --stat lib/git/lib.rb
178
- ```
179
-
180
- ## Example: Stash Commands
181
-
182
- Here's how this process was applied to the `stash` commands:
183
-
184
- ### Legacy Methods (v4.3.0)
185
- - `stashes_all` → returned `Array<[Integer, String]>` (index and message pairs)
186
- - `stash_save(message)` → returned regex match result (truthy/falsy)
187
- - `stash_apply(id)` → returned `String` (stdout)
188
- - `stash_clear` → returned `String` (stdout)
189
- - `stash_list` → returned `String` (stdout)
190
-
191
- ### New Methods (removed)
192
- - `stash_branch`
193
- - `stash_create`
194
- - `stash_drop`
195
- - `stash_pop`
196
- - `stash_push`
197
- - `stashes_list`
198
- - `git_stash_show_*` methods
199
-
200
- ### Implementation Pattern
201
-
202
- ```ruby
203
- # Requires at top of file
204
- require_relative 'git/commands/stash/apply'
205
- require_relative 'git/commands/stash/clear'
206
- require_relative 'git/commands/stash/list'
207
- require_relative 'git/commands/stash/push'
208
- require_relative 'git/parsers/stash'
209
-
210
- # Legacy method implementations
211
- def stashes_all
212
- result = Git::Commands::Stash::List.new(self).call
213
- stashes = Git::Parsers::Stash.parse_list(result.stdout)
214
- stashes.map { |info| stash_info_to_legacy(info) }
215
- end
216
-
217
- def stash_save(message)
218
- result = Git::Commands::Stash::Push.new(self).call(message: message)
219
- result.stdout =~ /HEAD is now at/
220
- end
221
-
222
- def stash_apply(id = nil)
223
- result = Git::Commands::Stash::Apply.new(self).call(id)
224
- result.stdout
225
- end
226
-
227
- def stash_clear
228
- result = Git::Commands::Stash::Clear.new(self).call
229
- result.stdout
230
- end
231
-
232
- def stash_list
233
- result = Git::Commands::Stash::List.new(self).call
234
- result.stdout
235
- end
236
-
237
- # Helper method (in private section)
238
- private
239
-
240
- def stash_info_to_legacy(stash_info)
241
- [stash_info.index, stash_info.message]
242
- end
243
- ```
244
-
245
- ## Key Principles
246
-
247
- 1. **Maintain exact v4.3.0 return values**: Don't change return types even slightly
248
- 2. **Use modern infrastructure internally**: Leverage `Git::Commands::*` classes for actual git operations
249
- 3. **Remove all new methods**: Don't try to make new methods backward compatible - just remove them
250
- 4. **Minimize changes**: Only modify what's necessary for backward compatibility
251
- 5. **Document conversions**: Make helper methods clear and well-named
252
- 6. **Apply policy at the facade**: `Git::Lib` methods pass policy options explicitly
253
- (e.g. `no_edit: true`, `verbose: true`, `no_progress: true`) — command classes stay
254
- neutral. See "Command-layer neutrality" in CONTRIBUTING.md.
255
-
256
- ## Validation Checklist
257
-
258
- - [ ] Working on a feature branch (not `main`)
259
- - [ ] Identified all methods for the command in v4.3.0
260
- - [ ] Documented v4.3.0 return values exactly
261
- - [ ] Removed all methods not present in v4.3.0
262
- - [ ] Implemented legacy methods using modern command classes
263
- - [ ] Added conversion logic where return types differ
264
- - [ ] Created helper methods for complex conversions
265
- - [ ] Updated require statements correctly
266
- - [ ] Verified changes with git diff
267
- - [ ] Net line count decreased (removed more than added)
268
-
269
- ## Usage
270
-
271
- To apply this process, say:
272
-
273
- > Remove methods added to Git::Lib since v4.3.0 for `<command_name>` git command(s) and ensure the remaining methods are backward compatible.
274
-
275
- Replace `<command_name>` with the specific git command(s) you want to audit (e.g., `branch`, `merge`, `tag`, `reset`, etc.).
@@ -1,358 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- branches: [main, 4.x]
6
- workflow_dispatch:
7
-
8
- # Cancel an in-progress run when the same PR (or branch) gets a new push. For
9
- # pull_request events github.ref is refs/pull/<n>/merge, so runs are grouped per PR
10
- # and pushing a new commit -- or force-pushing during a rebase -- no longer leaves a
11
- # full matrix running to completion against a commit nobody will merge.
12
- concurrency:
13
- group: ${{ github.workflow }}-${{ github.ref }}
14
- cancel-in-progress: true
15
-
16
- jobs:
17
- # RuboCop and YARD analyze source text, so their results do not depend on the
18
- # runtime executing them: RuboCop's verdict is fixed by TargetRubyVersion in
19
- # .rubocop.yml, not by the host Ruby. Running them once here instead of inside the
20
- # matrix removes the single largest cost in the build -- RuboCop alone accounted for
21
- # over half the wall time of the JRuby and TruffleRuby jobs -- while checking exactly
22
- # the same things.
23
- #
24
- # Ruby 3.4 hosts this job rather than the newest supported Ruby because it gates
25
- # merges: a lint gate should be insulated from churn in a brand-new major, and Ruby
26
- # 4.0 deprecations still surface through the matrix jobs below, which do run 4.0.
27
- # 3.3+ is also a hard floor here, since yard-lint is only installed on 3.3 and later
28
- # (see the gemspec) and YARD itself cannot build on JRuby or TruffleRuby.
29
- lint:
30
- name: Lint and Docs
31
-
32
- # Skip this job if triggered by a release PR
33
- if: >-
34
- github.event_name == 'workflow_dispatch' ||
35
- (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--'))
36
-
37
- runs-on: ubuntu-latest
38
-
39
- steps:
40
- - name: Checkout Code
41
- uses: actions/checkout@v6
42
-
43
- - name: Setup Ruby
44
- uses: ruby/setup-ruby@v1
45
- with:
46
- ruby-version: "3.4"
47
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
48
-
49
- - name: Run RuboCop and YARD
50
- run: bundle exec rake rubocop yard
51
- timeout-minutes: 15
52
-
53
- build:
54
- name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }}
55
-
56
- # Skip this job if triggered by a release PR
57
- if: >-
58
- github.event_name == 'workflow_dispatch' ||
59
- (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--'))
60
-
61
- runs-on: ${{ matrix.operating-system }}
62
- env: { JAVA_OPTS: -Djdk.io.File.enableADS=true }
63
-
64
- strategy:
65
- fail-fast: false
66
- matrix:
67
- # Only the minimum required versions of JRuby and TruffleRuby are tested.
68
- #
69
- ruby: ["3.2", "4.0", "truffleruby-24.2.1", "jruby-10.0.0.1"]
70
- operating-system: [ubuntu-latest]
71
- include:
72
- - ruby: "3.2"
73
- operating-system: windows-latest
74
-
75
- steps:
76
- - name: Checkout Code
77
- uses: actions/checkout@v6
78
-
79
- - name: Setup Ruby
80
- uses: ruby/setup-ruby@v1
81
- with:
82
- ruby-version: ${{ matrix.ruby }}
83
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
84
-
85
- # Specs only. RuboCop and YARD run once in the lint job above instead of being
86
- # repeated identically on every runtime. `rake build` still runs on every
87
- # platform, as a prerequisite of the Test Gem step below.
88
- - name: Run Specs
89
- run: bundle exec rake spec
90
- timeout-minutes: 15
91
-
92
- # The unit suite enforces 100% line and branch coverage (see the "Test coverage
93
- # policy" section of CONTRIBUTING.md). The failure log names the uncovered lines
94
- # and branches; this artifact adds the browsable HTML report for the same run.
95
- - name: Upload Coverage Report
96
- if: failure()
97
- uses: actions/upload-artifact@v7
98
- with:
99
- name: coverage-ruby-${{ matrix.ruby }}-${{ matrix.operating-system }}
100
- path: coverage/
101
- if-no-files-found: ignore
102
- retention-days: 7
103
-
104
- - name: Test Gem
105
- run: bundle exec rake test:gem
106
-
107
- # The branch ruleset cannot require the matrix jobs above directly. A required status
108
- # check is matched by name, and `build`'s name is a template -- GitHub only expands it
109
- # into "Ruby 3.2 on ubuntu-latest" and friends when it evaluates the matrix. The
110
- # job-level `if:` short-circuits before that happens, so a release PR reports one check
111
- # named literally "Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }}" and the
112
- # five expanded names never report at all. Required checks that never report block a PR
113
- # indefinitely rather than passing, which is what stranded the v5.0.5 release PR.
114
- #
115
- # So the ruleset requires this job instead: one stable name, no matrix, no template to
116
- # expand. It also decouples the required-check list from the matrix, so adding or
117
- # dropping a Ruby version no longer needs a matching ruleset edit.
118
- #
119
- # This job carries the same release-PR guard as every job above, so that a release PR
120
- # reports it as skipped rather than as a success for specs that never ran. Skipping is
121
- # safe here and was not safe for `build`: this job's name is a literal string, so the
122
- # check run exists and reports a skipped conclusion whether or not the job runs, and
123
- # rulesets treat skipped as passing. A matrix job's name does not exist at all until the
124
- # matrix is evaluated, which is the whole reason this job exists.
125
- #
126
- # `always()` is load-bearing and must stay. Without a status check function a job-level
127
- # `if:` implies `success()`, so a failing matrix would skip this job -- and a skipped
128
- # required check counts as passing, meaning a red build would merge. `always()` forces a
129
- # real verdict on every run that is not a release PR. `!cancelled()` is not a substitute:
130
- # a cancelled run would then skip the gate, and a skipped conclusion is one rulesets treat
131
- # as passing -- so a build that never finished would stop blocking the merge.
132
- #
133
- # The case below accepts `success` only. `build` and this job now carry the same release-PR
134
- # guard, so this job runs exactly when `build` ran; a `skipped` result would mean the two
135
- # guards have drifted apart, and failing closed on it turns that into a loud failure rather
136
- # than a silent pass.
137
- build-complete:
138
- name: All Specs Passed
139
-
140
- needs: [build]
141
-
142
- # Report a verdict on every non-release PR regardless of the matrix result, and skip
143
- # on release PRs, where the matrix was never meant to run.
144
- if: >-
145
- always() &&
146
- (github.event_name == 'workflow_dispatch' ||
147
- (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')))
148
-
149
- runs-on: ubuntu-latest
150
-
151
- steps:
152
- - name: Check the result of the build job
153
- run: |
154
- case "${{ needs.build.result }}" in
155
- success)
156
- echo "build: ${{ needs.build.result }}"
157
- ;;
158
- *)
159
- echo "::error::build: ${{ needs.build.result }}"
160
- exit 1
161
- ;;
162
- esac
163
-
164
- # Everything above runs in English, because GitHub-hosted runners generate no locale
165
- # beyond C/C.UTF-8. That makes git's translated diagnostics invisible to CI even
166
- # though they are the default experience for any contributor whose shell locale is not
167
- # English -- so a spec that matches an English git message passes here and fails for
168
- # them, on code they did not touch. This job is what stops such a spec from reaching
169
- # main. See issue #1670.
170
- #
171
- # German because git's `de` translation is close enough to complete that a
172
- # message-matching assumption cannot hide behind a string that was never translated.
173
- # Most of the suite is insulated from this by the LC_ALL pin in Git::ExecutionContext;
174
- # what this job really exercises is the code that bypasses that pin.
175
- locale:
176
- name: Non-English Locale
177
-
178
- # Skip this job if triggered by a release PR
179
- if: >-
180
- github.event_name == 'workflow_dispatch' ||
181
- (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--'))
182
-
183
- runs-on: ubuntu-latest
184
-
185
- # Set at job scope rather than on the spec step so that every step -- including the
186
- # check below that the locale is real -- sees exactly the environment the suite runs
187
- # under. Setting these before locale-gen is harmless: they are simply inert until
188
- # the locale exists.
189
- env:
190
- LANG: de_DE.UTF-8
191
- LC_ALL: de_DE.UTF-8
192
-
193
- steps:
194
- - name: Checkout Code
195
- uses: actions/checkout@v6
196
-
197
- # Two separate things are missing from a stock runner, and skipping either one
198
- # leaves the env above inert -- git keeps answering in English and the job proves
199
- # nothing:
200
- #
201
- # 1. The locale itself. Only C and C.UTF-8 are generated, so setlocale fails for
202
- # anything else and every process falls back to English.
203
- # 2. git's German message catalog. Unlike Debian, Ubuntu strips gettext catalogs
204
- # out of packages and ships them in language packs, so the installed git has
205
- # no de translation until language-pack-de provides one.
206
- #
207
- # `update-locale` is deliberately not run: it edits /etc/default/locale, which is
208
- # only consulted for logins that do not already set LANG and LC_ALL, and this job
209
- # sets both explicitly.
210
- #
211
- # The two apt settings below exist because this step reaches out to the Ubuntu
212
- # archive, which is the only part of this job that can fail for reasons that have
213
- # nothing to do with the code under test:
214
- #
215
- # * `Acquire::Retries` retries an individual download that fails outright,
216
- # rather than failing the job on one unlucky mirror.
217
- # * `timeout-minutes` bounds a mirror that accepts the connection and then
218
- # stops responding, which retries cannot help with. Without it the step
219
- # inherits the 6 hour job default: run 32087601711 sat here for 12 minutes
220
- # before it was cancelled by hand, and would otherwise have held a runner
221
- # for the rest of the day. The whole job normally finishes in 53-118
222
- # seconds, so 5 minutes is generous while still failing the same morning.
223
- #
224
- - name: Install the German locale and message catalogs
225
- timeout-minutes: 5
226
- run: |
227
- sudo apt-get -o Acquire::Retries=3 update -qq
228
- sudo apt-get -o Acquire::Retries=3 install -y --no-install-recommends language-pack-de
229
- sudo locale-gen de_DE.UTF-8
230
-
231
- # Ruby 3.4 to share the bundler cache the lint job already warms. Nothing about
232
- # this job is Ruby-version specific -- the locale is what is under test.
233
- - name: Setup Ruby
234
- uses: ruby/setup-ruby@v1
235
- with:
236
- ruby-version: "3.4"
237
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
238
-
239
- # Guard against a vacuous pass. Diffing git's output against the same command run
240
- # under LC_ALL=C proves translation is actually active, without pinning a German
241
- # string that a future git release is free to reword.
242
- - name: Verify the Locale Took Effect
243
- run: |
244
- dir="$(mktemp -d)"
245
- localized="$(cd "$dir" && git status 2>&1 || true)"
246
- english="$(cd "$dir" && LC_ALL=C git status 2>&1 || true)"
247
- echo "LC_ALL=$LC_ALL -> $localized"
248
- echo "LC_ALL=C -> $english"
249
- if [ "$localized" = "$english" ]; then
250
- echo "::error::git is still emitting English: de_DE.UTF-8 did not take effect, so this job would test nothing"
251
- exit 1
252
- fi
253
-
254
- - name: Run Specs
255
- run: bundle exec rake spec
256
- timeout-minutes: 15
257
-
258
- # On the ubuntu jobs above, `C.UTF-8` and `en_US.UTF-8` both resolve to a UTF-8 ctype
259
- # (measured on the runner in issue #1669), and on windows the pinned value makes no
260
- # difference under any value at all. So the non-ASCII regex coverage those jobs run
261
- # passes whatever `Git::ExecutionContext` pins, and cannot tell a correct `LC_ALL`
262
- # value from a wrong one. That blind spot is what let the original defect go
263
- # unreported for eighteen months: the gem pinned `en_US.UTF-8`, stock Debian, Ubuntu,
264
- # and RHEL images generate no such locale, git silently fell back to a C ctype, and
265
- # every non-ASCII regex in `grep`, `log`, and the `config` value regexes returned a
266
- # wrong answer with exit status zero -- while CI stayed green throughout.
267
- #
268
- # This job makes one run discriminating. `LOCPATH` overrides where glibc looks for
269
- # compiled locale data, so pointing it at an empty directory takes away every locale
270
- # that needs a locale archive -- `en_US.UTF-8` among them -- while leaving `C.UTF-8`
271
- # working, because glibc 2.35 and later compile that one into the library itself.
272
- # The suite then runs in an environment where the value shipped before #1681 fails
273
- # and the value shipped now passes: no container image, no second bundle install,
274
- # no cache miss. Measured on ubuntu:24.04 (glibc 2.39, the runner's base) with
275
- # `en_US.UTF-8` generated first:
276
- #
277
- # normal en_US.UTF-8 charmap=UTF-8 C.UTF-8 charmap=UTF-8
278
- # LOCPATH=empty en_US.UTF-8 charmap=ANSI_X3.4-1968 C.UTF-8 charmap=UTF-8
279
- #
280
- # This has to be a property of the run rather than of a spec. Never gate a spec on
281
- # whether the pinned locale loads: such a spec skips itself in exactly the
282
- # environments where this defect lives, which is how the defect went unreported for
283
- # eighteen months.
284
- #
285
- # LANG is set explicitly so only `en_US.UTF-8` is taken away and the ambient
286
- # environment stays UTF-8. The runner already sets this value; pinning it here keeps
287
- # the job from depending on the image's default.
288
- missing-locale:
289
- name: Missing en_US.UTF-8 Locale
290
-
291
- # Skip this job if triggered by a release PR
292
- if: >-
293
- github.event_name == 'workflow_dispatch' ||
294
- (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--'))
295
-
296
- runs-on: ubuntu-latest
297
-
298
- env:
299
- LANG: C.UTF-8
300
-
301
- steps:
302
- - name: Checkout Code
303
- uses: actions/checkout@v6
304
-
305
- # Exported through GITHUB_ENV rather than set at job scope because the `runner`
306
- # context is not available there, and the directory has to exist before anything
307
- # reads a locale -- glibc warns on every process when LOCPATH names a directory
308
- # that does not exist. RUNNER_TEMP rather than the workspace so that
309
- # actions/checkout cannot clean it away.
310
- - name: Point LOCPATH at an Empty Directory
311
- run: |
312
- mkdir -p "$RUNNER_TEMP/empty-locale-dir"
313
- echo "LOCPATH=$RUNNER_TEMP/empty-locale-dir" >> "$GITHUB_ENV"
314
-
315
- # Ruby 3.4 to share the bundler cache the lint job already warms. Nothing about
316
- # this job is Ruby-version specific -- the locale environment is what differs.
317
- - name: Setup Ruby
318
- uses: ruby/setup-ruby@v1
319
- with:
320
- ruby-version: "3.4"
321
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
322
-
323
- # Guard against a vacuous pass, functionally. `locale -a` still lists en_US.utf8
324
- # under LOCPATH (measured) even though setlocale then refuses it, so asking git
325
- # to match is the only assertion worth making: `.` spans the two-byte U+00C4 only
326
- # under a UTF-8 ctype. The metacharacter-free control has to keep matching under
327
- # the removed locale as well -- it matches bytes under any ctype, so if it fails,
328
- # the probe never delivered UTF-8 bytes to git and the rest proves nothing.
329
- - name: Verify the Run Discriminates
330
- run: |
331
- dir="$(mktemp -d)"
332
- cd "$dir"
333
- git init -q .
334
- printf '\xc3\x84PFEL sind gut\n' > w.txt
335
- git add w.txt
336
-
337
- if ! LC_ALL=C.UTF-8 git grep -q '^.PFEL' -- w.txt; then
338
- echo "::error::C.UTF-8 no longer yields a UTF-8 ctype under LOCPATH, so this job would fail for the wrong reason"
339
- exit 1
340
- fi
341
- if ! LC_ALL=en_US.UTF-8 git grep -q 'PFEL sind' -- w.txt; then
342
- echo "::error::the control pattern did not match, so this probe is not delivering UTF-8 bytes to git"
343
- exit 1
344
- fi
345
- if LC_ALL=en_US.UTF-8 git grep -q '^.PFEL' -- w.txt; then
346
- echo "::error::en_US.UTF-8 still yields a UTF-8 ctype, so this job cannot detect a wrong LC_ALL pin"
347
- exit 1
348
- fi
349
- echo "en_US.UTF-8 is unusable and C.UTF-8 works: a wrong pin fails this job"
350
-
351
- # The whole suite rather than the non-ASCII regex spec alone: the point is that
352
- # this run is indistinguishable from any other except for the missing locale, so
353
- # every locale-sensitive surface added later is covered without touching this
354
- # file. It also keeps the coverage gate satisfied, which a single spec file
355
- # cannot do.
356
- - name: Run Specs
357
- run: bundle exec rake spec
358
- timeout-minutes: 15
@@ -1,35 +0,0 @@
1
- ---
2
- name: Conventional Commits
3
-
4
- permissions:
5
- contents: read
6
-
7
- on:
8
- pull_request:
9
- branches:
10
- - main
11
- - 4.x
12
-
13
- # Cancel an in-progress run when the same PR gets a new push; only the commit messages
14
- # as they stand after the latest push are worth linting.
15
- concurrency:
16
- group: ${{ github.workflow }}-${{ github.ref }}
17
- cancel-in-progress: true
18
-
19
- jobs:
20
- commit-lint:
21
- name: Verify Conventional Commits
22
-
23
- # Skip this job if this is a release PR
24
- if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--'))
25
-
26
- runs-on: ubuntu-latest
27
-
28
- steps:
29
- - name: Checkout
30
- uses: actions/checkout@v6
31
- with: { fetch-depth: 0 }
32
-
33
- - name: Check Commit Messages
34
- uses: wagoid/commitlint-github-action@v6
35
- with: { configFile: .commitlintrc.yml }