kk-git 0.2.6 → 0.2.9
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/exe/kk-git +1 -1
- data/lib/kk/git/auto_format.rb +11 -2
- data/lib/kk/git/commit_message.rb +2 -2
- data/lib/kk/git/git_ops.rb +47 -7
- data/lib/kk/git/rake_tasks.rb +14 -0
- data/lib/kk/git/release.rb +97 -2
- data/lib/kk/git/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf707ff92a149936ea729095e66789b1af70a80a5519a1b90f262ac9f9bfeaf5
|
|
4
|
+
data.tar.gz: 16527f8e79675fde491dfad46bc3bf853f43755c837364c905bf7e1839fef542
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47fe99e7a65c3e9eed0f3e6d2fb56ec47dfe2ab77daf775a8706c598e7ed20b7857adaa6256a122c28924fb677954cbf60d3eb6311f319456c914b93d1f69ce2
|
|
7
|
+
data.tar.gz: fb76a7efc708d33ec57b9309db3b79a278b146a47df37159f4acdb39ff7f3920fa14996c9c70b24ed7a68e238d6170953ccd9886b5085b0c5d6cef522cc7d01e
|
data/exe/kk-git
CHANGED
|
@@ -165,7 +165,7 @@ def main_help
|
|
|
165
165
|
KK_GIT_SKIP_PUSH=1 skip push step
|
|
166
166
|
KK_GIT_AMEND=1 amend last commit instead of creating new one
|
|
167
167
|
KK_GIT_CONFIRM=1 print message and require KK_GIT_YES=1
|
|
168
|
-
|
|
168
|
+
KK_GIT_BLOCK_SENSITIVE=1 block committing .env/credentials paths (off by default)
|
|
169
169
|
KK_GIT_DEFAULT_TYPE default type for code edits (default: chore)
|
|
170
170
|
KK_GIT_PUSH_URL push to explicit URL (bypass SSH remote)
|
|
171
171
|
KK_GIT_PULL_URL pull from explicit URL (bypass SSH remote)
|
data/lib/kk/git/auto_format.rb
CHANGED
|
@@ -52,9 +52,18 @@ module KKGit
|
|
|
52
52
|
]
|
|
53
53
|
),
|
|
54
54
|
Language.new(
|
|
55
|
-
id: :
|
|
55
|
+
id: :js,
|
|
56
56
|
label: 'JS/TS/Node',
|
|
57
|
-
extensions: %w[.js .jsx .mjs .cjs .ts .tsx .json .
|
|
57
|
+
extensions: %w[.js .jsx .mjs .cjs .ts .tsx .json .jsonc .css],
|
|
58
|
+
tools: [
|
|
59
|
+
Tool.new(name: 'biome', bin: 'biome', argv: ['format', '--write', '%{files}']),
|
|
60
|
+
Tool.new(name: 'prettier', bin: 'prettier', argv: ['--write', '%{files}'])
|
|
61
|
+
]
|
|
62
|
+
),
|
|
63
|
+
Language.new(
|
|
64
|
+
id: :prettier_docs,
|
|
65
|
+
label: 'Prettier (markup/docs)',
|
|
66
|
+
extensions: %w[.scss .less .html .md .mdx .yaml .yml],
|
|
58
67
|
tools: [
|
|
59
68
|
Tool.new(name: 'prettier', bin: 'prettier', argv: ['--write', '%{files}'])
|
|
60
69
|
]
|
|
@@ -315,8 +315,8 @@ module KKGit
|
|
|
315
315
|
return false if path.nil?
|
|
316
316
|
path.start_with?('openspec/') ||
|
|
317
317
|
path.match?(/\AREADME(\..+)?\z/i) ||
|
|
318
|
-
path.match?(/\.(md|mdx|txt)\z/i)
|
|
319
|
-
|
|
318
|
+
path.match?(/\.(md|mdx|txt|csv)\z/i)
|
|
319
|
+
end
|
|
320
320
|
|
|
321
321
|
def self.ci_path?(path)
|
|
322
322
|
return false if path.nil?
|
data/lib/kk/git/git_ops.rb
CHANGED
|
@@ -22,6 +22,12 @@ module KKGit
|
|
|
22
22
|
|
|
23
23
|
SAFE_ENV_TEMPLATE_SUFFIXES = %w[example sample template dist].freeze
|
|
24
24
|
|
|
25
|
+
# Application source that manages credentials in-repo (not secret payloads).
|
|
26
|
+
SAFE_CREDENTIALS_CODE_PATTERNS = [
|
|
27
|
+
%r{(?:^|/)[a-z0-9_]+_credentials\.go\z}i,
|
|
28
|
+
%r{(?:^|/)[a-z0-9_]+_credentials\.(ts|tsx|js|jsx)\z}i
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
25
31
|
# 仓库同步状态快照
|
|
26
32
|
Status = Struct.new(
|
|
27
33
|
:branch, :remote, :clean, :ahead, :behind,
|
|
@@ -261,10 +267,15 @@ module KKGit
|
|
|
261
267
|
|
|
262
268
|
def sensitive_path?(path)
|
|
263
269
|
return false if safe_env_template?(path)
|
|
270
|
+
return false if safe_credentials_code?(path)
|
|
264
271
|
|
|
265
272
|
SENSITIVE_PATH_PATTERNS.any? { |pattern| path.match?(pattern) }
|
|
266
273
|
end
|
|
267
274
|
|
|
275
|
+
def safe_credentials_code?(path)
|
|
276
|
+
SAFE_CREDENTIALS_CODE_PATTERNS.any? { |pattern| path.match?(pattern) }
|
|
277
|
+
end
|
|
278
|
+
|
|
268
279
|
def safe_env_template?(path)
|
|
269
280
|
SAFE_ENV_TEMPLATE_SUFFIXES.any? do |suffix|
|
|
270
281
|
path.match?(/\A\.env\.#{suffix}\z/i)
|
|
@@ -279,16 +290,18 @@ module KKGit
|
|
|
279
290
|
end
|
|
280
291
|
|
|
281
292
|
def ensure_no_sensitive_staged!(repo_dir: '.')
|
|
293
|
+
return unless sensitive_check_enabled?
|
|
294
|
+
|
|
282
295
|
paths = sensitive_staged_paths(repo_dir: repo_dir)
|
|
283
296
|
return if paths.empty?
|
|
284
297
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
298
|
+
raise Error,
|
|
299
|
+
"Refusing to commit sensitive paths: #{paths.join(', ')}. " \
|
|
300
|
+
'Unset KK_GIT_BLOCK_SENSITIVE or unstage these files.'
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def sensitive_check_enabled?
|
|
304
|
+
ENV['KK_GIT_BLOCK_SENSITIVE'] == '1'
|
|
292
305
|
end
|
|
293
306
|
|
|
294
307
|
def confirm_commit!(message)
|
|
@@ -299,6 +312,18 @@ module KKGit
|
|
|
299
312
|
raise Error, 'Set KK_GIT_YES=1 to confirm this commit'
|
|
300
313
|
end
|
|
301
314
|
|
|
315
|
+
def fallback_commit_message(repo_dir: '.')
|
|
316
|
+
inferred = KKGit::CommitMessage.generate(repo_dir: repo_dir, mode: :all).to_s.strip
|
|
317
|
+
return inferred unless inferred.empty?
|
|
318
|
+
|
|
319
|
+
out, _err, ok = run_cmd('git', 'diff', '--cached', '--name-only', chdir: repo_dir)
|
|
320
|
+
names = ok ? out.split("\n").reject(&:empty?) : []
|
|
321
|
+
return 'chore(general): Update project' if names.empty?
|
|
322
|
+
|
|
323
|
+
label = File.basename(names.first)
|
|
324
|
+
names.length == 1 ? "chore(general): Update #{label}" : "chore(general): Update #{names.length} files"
|
|
325
|
+
end
|
|
326
|
+
|
|
302
327
|
def commit_with_message!(message, repo_dir: '.')
|
|
303
328
|
commit_args = amend? ? %w[commit --amend -F] : %w[commit -F]
|
|
304
329
|
|
|
@@ -339,6 +364,17 @@ module KKGit
|
|
|
339
364
|
add_all!(repo_dir: repo_dir)
|
|
340
365
|
ensure_no_sensitive_staged!(repo_dir: repo_dir)
|
|
341
366
|
|
|
367
|
+
# add 后可能已干净:假脏(换行归一化等)或 KK_GIT_ADD_PATHS 未覆盖到变更
|
|
368
|
+
if working_tree_clean?(repo_dir: repo_dir)
|
|
369
|
+
if status(remote: remote_name, branch: branch_name, repo_dir: repo_dir).needs_sync?
|
|
370
|
+
sync_with_remote!(remote_name, branch_name, repo_dir: repo_dir)
|
|
371
|
+
return :synced
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
puts 'No changes to commit or push'
|
|
375
|
+
return :noop
|
|
376
|
+
end
|
|
377
|
+
|
|
342
378
|
message =
|
|
343
379
|
if commit_message_generator
|
|
344
380
|
commit_message_generator.call
|
|
@@ -346,6 +382,10 @@ module KKGit
|
|
|
346
382
|
KKGit::CommitMessage.generate(repo_dir: repo_dir, mode: :all)
|
|
347
383
|
end
|
|
348
384
|
message = message.to_s.strip
|
|
385
|
+
if message.empty?
|
|
386
|
+
message = fallback_commit_message(repo_dir: repo_dir)
|
|
387
|
+
warn "Commit message inference empty; using fallback: #{message}"
|
|
388
|
+
end
|
|
349
389
|
raise Error, 'Could not generate commit message from staged changes' if message.empty?
|
|
350
390
|
|
|
351
391
|
confirm_commit!(message)
|
data/lib/kk/git/rake_tasks.rb
CHANGED
|
@@ -71,6 +71,20 @@ module KKGit
|
|
|
71
71
|
warn e.message
|
|
72
72
|
exit 1
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
desc 'Auto commit/push, then semver tag (SKIP_TAG=1 or KK_GIT_AUTO_TAG=0 to skip)'
|
|
76
|
+
task :auto_commit_push_tag do
|
|
77
|
+
generator = lambda do
|
|
78
|
+
Rake::Task['git:auto_commit'].reenable
|
|
79
|
+
Rake::Task['git:auto_commit'].invoke
|
|
80
|
+
ENV['KK_GIT_COMMIT_MESSAGE'].to_s
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
KKGit::Release.auto_commit_push_and_tag!(commit_message_generator: generator)
|
|
84
|
+
rescue KKGit::GitOps::Error, ArgumentError => e
|
|
85
|
+
warn e.message
|
|
86
|
+
exit 1
|
|
87
|
+
end
|
|
74
88
|
end
|
|
75
89
|
end
|
|
76
90
|
end
|
data/lib/kk/git/release.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module KKGit
|
|
4
|
-
# Semver bump and git tag helpers (maintainer release flow).
|
|
4
|
+
# Semver bump and git tag helpers (maintainer release flow + consumer auto-tag).
|
|
5
5
|
module Release
|
|
6
6
|
VERSION_PATH = File.join(__dir__, 'version.rb').freeze
|
|
7
7
|
|
|
@@ -48,7 +48,7 @@ module KKGit
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def semver_max(*versions)
|
|
51
|
-
versions.compact.
|
|
51
|
+
versions.compact.max
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def tag_exists?(tag, repo_dir: '.')
|
|
@@ -74,6 +74,42 @@ module KKGit
|
|
|
74
74
|
true
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
# Strict repo tags only: vMAJOR.MINOR.PATCH with MAJOR < max_major (skips date tags like v2026.09.23).
|
|
78
|
+
# @return [Array<Array(Integer)>]
|
|
79
|
+
def list_repo_semver_versions(prefix: 'v', repo_dir: '.', max_major: 1999)
|
|
80
|
+
stdout, _stderr, ok = GitOps.run_cmd('git', 'tag', '--list', "#{prefix}*", chdir: repo_dir)
|
|
81
|
+
return [] unless ok
|
|
82
|
+
|
|
83
|
+
stdout.to_s.split("\n").filter_map do |t|
|
|
84
|
+
t = t.strip
|
|
85
|
+
next unless t.start_with?(prefix)
|
|
86
|
+
|
|
87
|
+
ver = parse_semver(t.delete_prefix(prefix))
|
|
88
|
+
next unless ver
|
|
89
|
+
next if ver[0] > max_major
|
|
90
|
+
|
|
91
|
+
ver
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Next tag from git history only (no version.rb / gem VERSION). For product repos.
|
|
96
|
+
# @return [String] e.g. "v1.0.1"
|
|
97
|
+
def next_repo_semver_tag(prefix: nil, bump: nil, repo_dir: '.', default_base: [1, 0, 0], max_major: 1999)
|
|
98
|
+
prefix ||= ENV.fetch('KK_GIT_TAG_PREFIX', 'v')
|
|
99
|
+
bump ||= ENV.fetch('KK_GIT_BUMP', 'patch')
|
|
100
|
+
raise ArgumentError, "bump must be major|minor|patch (got #{bump})" unless %w[major minor patch].include?(bump)
|
|
101
|
+
|
|
102
|
+
base = list_repo_semver_versions(prefix: prefix, repo_dir: repo_dir, max_major: max_major).max || default_base
|
|
103
|
+
version = base
|
|
104
|
+
50.times do
|
|
105
|
+
version = bump_semver(version, bump)
|
|
106
|
+
candidate = "#{prefix}#{semver_to_s(version)}"
|
|
107
|
+
return candidate unless tag_exists?(candidate, repo_dir: repo_dir)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
raise 'Failed to generate next repo semver tag: too many attempts'
|
|
111
|
+
end
|
|
112
|
+
|
|
77
113
|
# @return [Array(String, String)] tag, version
|
|
78
114
|
def next_tag_and_version(prefix: nil, bump: nil, repo_dir: '.')
|
|
79
115
|
prefix ||= ENV.fetch('KK_GIT_TAG_PREFIX', 'v')
|
|
@@ -81,6 +117,7 @@ module KKGit
|
|
|
81
117
|
|
|
82
118
|
latest_tag = latest_semver_tag(prefix: prefix, repo_dir: repo_dir)
|
|
83
119
|
tag_version = latest_tag ? parse_semver(latest_tag.delete_prefix(prefix)) : nil
|
|
120
|
+
# Prefer strict repo tags when gem/file versions would pollute consumer repos.
|
|
84
121
|
file_version = current_version_in_file || parse_semver(KKGit::VERSION)
|
|
85
122
|
|
|
86
123
|
# version.rb already ahead of tags and tag not yet created → release that version
|
|
@@ -109,5 +146,63 @@ module KKGit
|
|
|
109
146
|
out, err, ok = GitOps.run_cmd('git', 'push', push_target, tag, chdir: repo_dir)
|
|
110
147
|
GitOps.ensure_ok!(ok, 'git push tag', stdout: out, stderr: err, repo_dir: repo_dir)
|
|
111
148
|
end
|
|
149
|
+
|
|
150
|
+
def auto_tag_enabled?
|
|
151
|
+
return false if ENV['SKIP_TAG'] == '1'
|
|
152
|
+
|
|
153
|
+
ENV.fetch('KK_GIT_AUTO_TAG', '1') != '0'
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Commit + push, then create/push a semver tag when a new commit was made.
|
|
157
|
+
#
|
|
158
|
+
# - Product repos: leave bump_gem_version false (uses next_repo_semver_tag).
|
|
159
|
+
# - kk-git itself: bump_gem_version true (updates version.rb before commit).
|
|
160
|
+
#
|
|
161
|
+
# Env: KK_GIT_AUTO_TAG (default 1), SKIP_TAG=1, KK_GIT_BUMP, KK_GIT_TAG_PREFIX
|
|
162
|
+
#
|
|
163
|
+
# @return [Hash] keys :result (Symbol), :tag (String or nil)
|
|
164
|
+
def auto_commit_push_and_tag!(commit_message_generator: nil, repo_dir: '.', bump_gem_version: false)
|
|
165
|
+
GitOps.ensure_in_repo!(repo_dir: repo_dir)
|
|
166
|
+
|
|
167
|
+
head_before, _err, ok = GitOps.run_cmd('git', 'rev-parse', 'HEAD', chdir: repo_dir)
|
|
168
|
+
head_before = ok ? head_before.to_s.strip : ''
|
|
169
|
+
|
|
170
|
+
prepared_tag = nil
|
|
171
|
+
if bump_gem_version && auto_tag_enabled? && !GitOps.working_tree_clean?(repo_dir: repo_dir)
|
|
172
|
+
prepared_tag, new_version = next_tag_and_version(repo_dir: repo_dir)
|
|
173
|
+
bumped = update_version!(new_version)
|
|
174
|
+
puts((bumped ? "Version bumped to #{new_version}" : "Version already #{new_version}") +
|
|
175
|
+
", preparing tag #{prepared_tag}")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
result = GitOps.auto_commit_push!(
|
|
179
|
+
commit_message_generator: commit_message_generator,
|
|
180
|
+
repo_dir: repo_dir
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
head_after, _err2, ok2 = GitOps.run_cmd('git', 'rev-parse', 'HEAD', chdir: repo_dir)
|
|
184
|
+
head_after = ok2 ? head_after.to_s.strip : ''
|
|
185
|
+
new_commit = result == :committed_and_synced || (head_before != head_after && !head_after.empty?)
|
|
186
|
+
|
|
187
|
+
unless auto_tag_enabled?
|
|
188
|
+
puts 'SKIP_TAG/KK_GIT_AUTO_TAG=0 — not tagging' if ENV['SKIP_TAG'] == '1' || ENV['KK_GIT_AUTO_TAG'] == '0'
|
|
189
|
+
return { result: result, tag: nil }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
unless new_commit
|
|
193
|
+
puts "No new commit (#{result}) — skip tag"
|
|
194
|
+
return { result: result, tag: nil }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
tag = prepared_tag || next_repo_semver_tag(repo_dir: repo_dir)
|
|
198
|
+
if tag_exists?(tag, repo_dir: repo_dir)
|
|
199
|
+
puts "Tag #{tag} already exists — skip"
|
|
200
|
+
return { result: result, tag: nil }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
create_and_push_tag!(tag, repo_dir: repo_dir)
|
|
204
|
+
puts "Tagged and pushed #{tag}"
|
|
205
|
+
{ result: result, tag: tag }
|
|
206
|
+
end
|
|
112
207
|
end
|
|
113
208
|
end
|
data/lib/kk/git/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kk-git
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kk
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Generate Conventional Commits commit messages from current git changes
|
|
14
14
|
(staged/worktree), designed for Rake/script usage.
|