diff --git a/.check.rb b/.check.rb index 3c6ba05..a6b7e85 100755 --- a/.check.rb +++ b/.check.rb @@ -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. @@ -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) @@ -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 + 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 @@ -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$/) @@ -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 diff --git a/.github/linters/editorconfig-checker.json b/.github/linters/editorconfig-checker.json new file mode 100644 index 0000000..1adc63b --- /dev/null +++ b/.github/linters/editorconfig-checker.json @@ -0,0 +1,5 @@ +{ + "Disable": { + "IndentSize": false + } +} diff --git a/.github/workflows/check-gems.yml b/.github/workflows/check-gems.yml index 29d6019..690efed 100644 --- a/.github/workflows/check-gems.yml +++ b/.github/workflows/check-gems.yml @@ -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 @@ -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}" diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 293579a..28537ff 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -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