Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion .check.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env ruby

require "optparse"
require "yaml"

# please add or remove files on the top dir for management if necessary.
Expand All @@ -25,6 +26,12 @@ def run_and_split_nul(cmd)
IO.popen(cmd, "rb") { |pipe| pipe.read.split("\0") }
end

class Array # rubocop:disable Style/Documentation
def extract_git_branches
grep(%r{^refs/heads/}).map { |e| e.delete_prefix("refs/heads/") }
end
end

errinfo = Object.new
class << errinfo
def push(mesg)
Expand All @@ -49,10 +56,87 @@ def output
end
end

def git_get_remote_refs(repository)
env = { "GIT_TERMINAL_PROMPT" => "0" }
cmd = %W[git ls-remote -btq --refs #{repository}]

IO.popen(env, cmd, "r", err: File::NULL) do |r|
refs = r.read.split(/[\n\t]/).select.with_index { |_, i| i.odd? }
return refs unless refs.empty?
end

nil
end

def check_git_repository(gemname, tree, errinfo) # rubocop:disable Metrics/MethodLength
repo = tree["repository"].to_s
branch = tree["branch"] || "master"
refs = git_get_remote_refs(repo) unless repo.empty?

if refs.nil?
errinfo.push <<~INFO
#{gemname}: unable to access the repository or locate a valid branch name.
INFO
elsif refs.include?("refs/heads/#{branch}")
# OK
elsif refs.include?("refs/tags/#{branch}")
errinfo.push <<~INFO
#{gemname}: instead of branches, the tag "#{branch}" is used.
INFO
else
errinfo.push <<~INFO
#{gemname}: branch "#{branch}" is missing.
> candidates: #{refs.extract_git_branches.join(" ")}
INFO
end
end
Comment on lines +71 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There are a few improvements we can make to check_git_repository:

  1. Defensive Programming: If repository is missing or empty in the YAML file, repo will be nil or empty. Calling git_get_remote_refs with nil will run a failing git command. We should guard against this and return early.
  2. Idiomatic Membership Check: Use Array#include? instead of Array#any? with an argument for checking exact string matching. It is more standard and compatible.
  3. Cleaner String Manipulation: Instead of joining the array with newlines, performing a regex replacement, and then translating newlines back to spaces, we can map over the array using delete_prefix and join them with spaces directly.
def check_git_repository(gemname, tree, errinfo) # rubocop:disable Metrics/MethodLength
  repo = tree["repository"]
  return if repo.to_s.empty?

  branch = tree["branch"] || "master"
  refs = git_get_remote_refs(repo)

  if refs.nil?
    errinfo.push <<~INFO
      #{gemname}: unable to access the repository or locate a valid branch name.
    INFO
  elsif refs.include?("refs/heads/#{branch}")
    # OK
  elsif refs.include?("refs/tags/#{branch}")
    errinfo.push <<~INFO
      WARN: #{gemname}: instead of branches, the tag "#{branch}" is used.
    INFO
  else
    candidates = refs.grep(%r{\Arefs/heads/}).map { |r| r.delete_prefix("refs/heads/") }.join(" ")
    errinfo.push <<~INFO
      #{gemname}: branch "#{branch}" is missing.
      \tcandidates: #{candidates}
    INFO
  end
end


Dir.chdir(__dir__)

banner = <<~BANNER
Usage: #{$PROGRAM_NAME} [options]
#{$PROGRAM_NAME} [options] --gitdiff base-ref [head-ref]
BANNER

gitdiff = false
gitrepo = false
interval = 1.2
OptionParser.new(banner, 24, "") do |o|
o.separator ""

o.on "--gitdiff", "Verify only the files with changes between two Git refs" do
gitdiff = true
end

o.on "--gitrepo", "Verify a Git repository and a specified branch" do
gitrepo = true
end

o.on "--interval seconds", "Specify the access interval with `--gitrepo` (default: #{interval})", Float do |n|
interval = n
end

o.order!
end

entries = run_and_split_nul(%w[git ls-files -z :^*/*])

if gitdiff
case ARGV.size
when 1, 2
# do nothing
else
warn banner
exit 1
end
cmd = %w[git diff -z --name-only --diff-filter=ACMR]
cmd.concat ARGV
cmd.concat %w[-- :^*/*]
gemfiles = run_and_split_nul(cmd)
else
gemfiles = entries
end

puts "checking project management files"
unless (PROJECT_FILES - entries).empty?
errinfo.push <<~MESG
Expand All @@ -61,7 +145,7 @@ def output
MESG
end

(entries - PROJECT_FILES).each do |f|
(gemfiles - PROJECT_FILES).each_with_index do |f, i|
puts "checking #{f}"

unless f.match?(/\.gem$/)
Expand All @@ -82,6 +166,11 @@ def output
tree["dependencies"].to_a.each do |x|
errinfo.push "#{f}: invalid dependencies" unless x.is_a?(String)
end

if gitrepo
sleep interval if i.positive?
check_git_repository(f, tree, errinfo)
end
rescue StandardError => e
errinfo.push e
end
Expand Down
5 changes: 5 additions & 0 deletions .github/linters/editorconfig-checker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"Disable": {
"IndentSize": false
}
}
14 changes: 13 additions & 1 deletion .github/workflows/check-gems.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ jobs:
check-gems:
name: Check gem files
runs-on: ubuntu-latest
env:
BASE_REF: ${{ case(github.event_name == 'pull_request', github.event.pull_request.base.sha,
github.event.before == '0000000000000000000000000000000000000000', 'master',
github.event.before) }}
steps:
- name: Checkout Code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
Expand All @@ -19,4 +23,12 @@ jobs:
ruby-version: '3.4' # Not needed with a .ruby-version file
bundler: 'default'
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- run: bundle exec .check.rb
- name: Fetch base revision
run: |
git fetch --depth 1 origin "${BASE_REF}"
if [ "${BASE_REF}" = "master" ]
then
git branch master origin/master
fi
- name: Validation
run: bundle exec .check.rb --gitrepo --gitdiff "${BASE_REF}"
1 change: 1 addition & 0 deletions .github/workflows/super-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
uses: super-linter/super-linter/slim@9e863354e3ff62e0727d37183162c4a88873df41 # v8.6.0
env:
VALIDATE_EDITORCONFIG: true
EDITORCONFIG_FILE_NAME: ${{ github.workspace }}/.github/linters/editorconfig-checker.json
VALIDATE_GITHUB_ACTIONS: true
VALIDATE_GITLEAKS: true
DEFAULT_BRANCH: master
Expand Down