From 2dcfb9a08d63297db99cdb1c198bdaed3d188908 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sun, 8 Nov 2026 07:58:39 +0000 Subject: [PATCH] =?UTF-8?q?fix(hooks):=20=F0=9F=90=9B=20accept=20conventio?= =?UTF-8?q?nal=20commit=20prefixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/kettle/jem/tasks/install_task.rb | 2 +- .../templates/.git-hooks/commit-msg.example | 12 +++---- .../jem/templates/CONTRIBUTING.md.example | 6 +++- .../spec/system/commit_msg_hook_spec.rb | 35 +++++++++++++++++++ .../spec/system/install_orchestration_spec.rb | 4 +-- 5 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 gems/kettle-jem/spec/system/commit_msg_hook_spec.rb diff --git a/gems/kettle-jem/lib/kettle/jem/tasks/install_task.rb b/gems/kettle-jem/lib/kettle/jem/tasks/install_task.rb index 7bdc6fedd..694187654 100644 --- a/gems/kettle-jem/lib/kettle/jem/tasks/install_task.rb +++ b/gems/kettle-jem/lib/kettle/jem/tasks/install_task.rb @@ -1862,7 +1862,7 @@ def bootstrap_commit_step(project_root, run_options:) commands = [] commands << %w[git add -A -- .] - commands << ["git", "commit", "-m", "🎨 Template bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"] + commands << ["git", "commit", "-m", "chore(templates): 🎨 bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"] { name: "bootstrap_commit", status: "ready", diff --git a/gems/kettle-jem/lib/kettle/jem/templates/.git-hooks/commit-msg.example b/gems/kettle-jem/lib/kettle/jem/templates/.git-hooks/commit-msg.example index 1dfaa5b40..fd2681446 100755 --- a/gems/kettle-jem/lib/kettle/jem/templates/.git-hooks/commit-msg.example +++ b/gems/kettle-jem/lib/kettle/jem/templates/.git-hooks/commit-msg.example @@ -19,8 +19,10 @@ begin require "gitmoji/regex" full_text = File.read(ARGV[0]) - # Is the first character a GitMoji? - gitmoji_index = full_text =~ Gitmoji::Regex::REGEX + # A Conventional Commit prefix may precede the Gitmoji in the subject. + subject = full_text.lines.first.to_s + subject = subject.sub(/\A[a-zA-Z][a-zA-Z0-9-]*(?:\([^\s()]+\))?!?: /, "") + gitmoji_index = subject =~ Gitmoji::Regex::REGEX if gitmoji_index == 0 exit(0) else @@ -37,12 +39,10 @@ begin Did you forget to add a relevant gitmoji? (see https://gitmoji.dev/ for tools) - In this project, a Gitmoji must be the first grapheme of the commit message. - What's a grapheme? - A symbol rendered to be visually identifiable as a single character, but which may be composed of multiple Unicode code points) + A Gitmoji must start the subject, or follow a Conventional Commit prefix. Must match: #{Gitmoji::Regex::REGEX} #{"Found a gitmoji at character index #{gitmoji_index}... not good enough.\n" if gitmoji_index} - Example: git commit -m "✨ My excellent new feature" + Example: git commit -m "feat(core): ✨ add feature" EOM puts denied diff --git a/gems/kettle-jem/lib/kettle/jem/templates/CONTRIBUTING.md.example b/gems/kettle-jem/lib/kettle/jem/templates/CONTRIBUTING.md.example index ebbecc43e..17909ac0b 100644 --- a/gems/kettle-jem/lib/kettle/jem/templates/CONTRIBUTING.md.example +++ b/gems/kettle-jem/lib/kettle/jem/templates/CONTRIBUTING.md.example @@ -24,7 +24,7 @@ Follow these instructions: 2. Fork the repository 3. Create your feature branch (`git checkout -b my-new-feature`) 4. Make some fixes. -5. Commit your changes (`git commit -am 'Added some feature'`) +5. Commit your changes (`git commit -am 'feat(core): ✨ add feature'`) 6. Push to the branch (`git push origin my-new-feature`) 7. Make sure to add tests for it. This is important, so it doesn't break in a future release. 8. Create new Pull Request. @@ -112,6 +112,10 @@ Releasing and signing - Not needed on bundler >= 2.7.0, as reproducible builds have become the default. Git hooks and commit message helpers (exe/kettle-commit-msg) + +Start commit subjects with a Conventional Commit prefix followed by a Gitmoji, +for example `feat(core): ✨ add feature`. Emoji-first subjects remain supported. + - GIT_HOOK_BRANCH_VALIDATE: Branch name validation mode (e.g., `jira`) or `false` to disable - GIT_HOOK_FOOTER_APPEND: Append a footer to commit messages when goalie allows (true/false) - GIT_HOOK_FOOTER_SENTINEL: Required when footer append is enabled — a unique first-line sentinel to prevent duplicates diff --git a/gems/kettle-jem/spec/system/commit_msg_hook_spec.rb b/gems/kettle-jem/spec/system/commit_msg_hook_spec.rb new file mode 100644 index 000000000..93208dcf5 --- /dev/null +++ b/gems/kettle-jem/spec/system/commit_msg_hook_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "open3" +require "rbconfig" +require "tmpdir" + +RSpec.describe "generated commit-msg hook", :system do + let(:template) { File.expand_path("../../lib/kettle/jem/templates/.git-hooks/commit-msg.example", __dir__) } + + def run_hook(message) + Dir.mktmpdir do |dir| + hook = File.join(dir, "commit-msg") + message_file = File.join(dir, "message") + FileUtils.cp(template, hook) + File.write(message_file, message) + Open3.capture3({"RUBYOPT" => nil}, RbConfig.ruby, hook, message_file) + end + end + + it "accepts conventional prefixes and legacy emoji-first subjects" do + ["feat(core): ✨ add feature", "fix: 🐛 repair bug", "feat(api)!: ✨ break API", "✨ add feature"].each do |subject| + stdout, stderr, status = run_hook(subject) + expect(status.success?).to be(true), "#{subject}: #{stdout} #{stderr}" + expect(stderr).not_to include("Skipping gitmoji check") + end + end + + it "rejects missing, misplaced, and body-only emoji" do + ["feat(core): add feature", "feat: add ✨ feature", "feat: add feature\n\n✨ body", "invalid prefix: ✨ feature"].each do |subject| + stdout, _stderr, status = run_hook(subject) + expect(status.success?).to be(false), subject + expect(stdout).to include("Conventional Commit prefix") + end + end +end diff --git a/gems/kettle-jem/spec/system/install_orchestration_spec.rb b/gems/kettle-jem/spec/system/install_orchestration_spec.rb index 042ee9166..fc0aa46a8 100644 --- a/gems/kettle-jem/spec/system/install_orchestration_spec.rb +++ b/gems/kettle-jem/spec/system/install_orchestration_spec.rb @@ -540,14 +540,14 @@ status: "succeeded", commands: [ %w[git add -A -- .], - ["git", "commit", "-m", "🎨 Template bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"] + ["git", "commit", "-m", "chore(templates): 🎨 bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"] ], reason: "executed", duration_ms: be >= 0 ) expect(bootstrap_step.fetch(:command_results)).to contain_exactly( hash_including(command: %w[git add -A -- .], exitstatus: 0, duration_ms: be >= 0), - hash_including(command: ["git", "commit", "-m", "🎨 Template bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"], exitstatus: 0, duration_ms: be >= 0) + hash_including(command: ["git", "commit", "-m", "chore(templates): 🎨 bootstrap by kettle-jem v#{Kettle::Jem::Version::VERSION}"], exitstatus: 0, duration_ms: be >= 0) ) expect(git_ready.fetch(:install_steps).find { |step| step.fetch(:name) == "bootstrap_commit" }.fetch(:dirty_entries)).not_to be_empty