From e0fb1b6138088358b27bd190e3b9cb7af496df0b Mon Sep 17 00:00:00 2001 From: Murilo Dal Ri Date: Tue, 13 Aug 2024 11:22:19 +0100 Subject: [PATCH] Add govuk repo tags check Checks wether repos have been tagged correctly. This code was in the govuk-saas-config which is in the process of being retired. --- .github/workflows/verify_repo_tags.yml | 61 ++++++++++++++++++++++ Rakefile | 20 ++++++++ lib/validate_repos.rb | 46 +++++++++++++++++ spec/validate_repos_spec.rb | 71 ++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 .github/workflows/verify_repo_tags.yml create mode 100644 lib/validate_repos.rb create mode 100644 spec/validate_repos_spec.rb diff --git a/.github/workflows/verify_repo_tags.yml b/.github/workflows/verify_repo_tags.yml new file mode 100644 index 00000000..bb2c8004 --- /dev/null +++ b/.github/workflows/verify_repo_tags.yml @@ -0,0 +1,61 @@ +name: "Verify Repo Tags" + +on: + workflow_dispatch: {} + schedule: + - cron: '00 10 * * 1-5' # Runs at 10:00 UTC, Monday through Friday. + +jobs: + verify-repo-tags: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Verify Repo Tags + id: verify_repo_tags + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + EXIT_CODE=0 + output=$(bundle exec rake verify_repo_tags) || EXIT_CODE=$? + + echo "$output" + + exit $EXIT_CODE + + - name: Notify failure + uses: slackapi/slack-github-action@v1 + if: ${{ failure() }} + with: + payload: | + { + "channel": "#govuk-platform-support", + "username": "Platform Alerts", + "text": "The is out of sync with the repos tagged as 'govuk' in GitHub.", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "The is out of sync with the repos tagged as 'govuk' in GitHub." + }, + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "Check the build logs for details" + }, + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "action_id": "button-view-workflow" + } + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/Rakefile b/Rakefile index ebad37e0..fd087ccd 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +require_relative "lib/validate_repos" + begin require "rspec/core/rake_task" @@ -25,6 +27,24 @@ rescue LoadError # no rubocop available end +desc "Verify that GOVUK repos are tagged #govuk" +task :verify_repo_tags do + validator = ValidateRepos.new + + untagged_message = <<~UNTAGGED + The following repos in the repos.yml file in govuk-developer-docs do not have the govuk tag on GitHub: + UNTAGGED + + falsely_tagged_message = <<~FALSETAG + The following repos have the govuk tag on GitHub but are not in the repos.yml file in govuk-developer-docs: + FALSETAG + + puts "#{untagged_message}\n#{validator.untagged_repos}" unless validator.untagged_repos.empty? + puts "#{falsely_tagged_message}\n#{validator.falsely_tagged_repos}" unless validator.falsely_tagged_repos.empty? + + exit 1 unless validator.untagged_repos.empty? && validator.falsely_tagged_repos.empty? +end + task default: %i[ jsonlint rubocop diff --git a/lib/validate_repos.rb b/lib/validate_repos.rb new file mode 100644 index 00000000..f1464873 --- /dev/null +++ b/lib/validate_repos.rb @@ -0,0 +1,46 @@ +require "json" +require "octokit" +require "open-uri" +require "yaml" + +class ValidateRepos + def initialize + Octokit.auto_paginate = true + @client = Octokit::Client.new(access_token: ENV.fetch("GITHUB_TOKEN")) + end + + def github_repos_tagged_govuk + @github_repos_tagged_govuk ||= repos.map { |repo| repo["name"] } + end + + def govuk_repo_names + @govuk_repo_names ||= JSON.parse(Net::HTTP.get_response(URI.parse("https://docs.publishing.service.gov.uk/repos.json")).body) + .map { |repo| repo["app_name"] } + .reject { |app| ignored_apps.include?(app) } + end + + def untagged_repos + (govuk_repo_names - github_repos_tagged_govuk).join("\n") + end + + def falsely_tagged_repos + (github_repos_tagged_govuk - govuk_repo_names).join("\n") + end + + def repos + @client + .org_repos("alphagov", accept: "application/vnd.github.mercy-preview+json") + .select { |repo| repo.topics.to_a.include?("govuk") } + .reject(&:archived) + .reject { |repo| ignored_repos.include?(repo.full_name) } + .sort_by { |repo| repo[:full_name] } + end + + def ignored_repos + ["alphagov/licensify"] # Licensify consists of 3 apps in 1 repo + end + + def ignored_apps + %w[licensify-backend] + end +end diff --git a/spec/validate_repos_spec.rb b/spec/validate_repos_spec.rb new file mode 100644 index 00000000..49794b53 --- /dev/null +++ b/spec/validate_repos_spec.rb @@ -0,0 +1,71 @@ +require "spec_helper" +require_relative "../lib/validate_repos" + +RSpec.describe ValidateRepos do + before do + @repo_mock = double("Repo", topics: %w[govuk], archived: false, full_name: "this-is-a-govuk-repo") + allow(@repo_mock).to receive(:[]).with("name").and_return("this-is-a-govuk-repo") + allow(@repo_mock).to receive(:[]).with(:full_name).and_return("this-is-a-govuk-repo") + allow(ENV).to receive(:fetch).with("GITHUB_TOKEN").and_return("dummy_token") + allow_any_instance_of(Octokit::Client).to receive(:org_repos).and_return([@repo_mock]) + end + + it "should ignore any repos that exist in repos.json AND are tagged govuk in GitHub" do + repos = [{ + "app_name" => "this-is-a-govuk-repo", + }] + + stub_repos_json(repos) + + validator = ValidateRepos.new + + expect(validator.untagged_repos).to eq("") + expect(validator.falsely_tagged_repos).to eq("") + end + + it "doesn't say that a repo is missing the govuk tag if it has been added to the ignore list" do + app_name = "this-is-a-govuk-repo" + allow_any_instance_of(ValidateRepos).to receive(:ignored_repos).and_return(["alphagov/#{app_name}"]) + + repos = [{ + "app_name" => app_name, + }] + + stub_repos_json(repos) + + validator = ValidateRepos.new + + expect(validator.untagged_repos).to eq("") + expect(validator.falsely_tagged_repos).to eq("") + end + + it "should alert if it finds an untagged repo in repos.json" do + repos = [ + { "app_name" => "this-is-a-govuk-repo" }, + { "app_name" => "this-govuk-repo-is-not-tagged!" }, + ] + + stub_repos_json(repos) + + validator = ValidateRepos.new + + expect(validator.untagged_repos).to eq("this-govuk-repo-is-not-tagged!") + expect(validator.falsely_tagged_repos).to eq("") + end + + it "should alert if it finds a repo that has falsely been tagged as govuk." do + repos = [] + + stub_repos_json(repos) + + validator = ValidateRepos.new + + expect(validator.falsely_tagged_repos).to eq("this-is-a-govuk-repo") + expect(validator.untagged_repos).to eq("") + end + + def stub_repos_json(repos) + stub_request(:get, "https://docs.publishing.service.gov.uk/repos.json") + .to_return(status: 200, body: repos.to_json, headers: {}) + end +end