From 30db3ea92a7226324bef86ec61675f7284949735 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:22:03 +0000 Subject: [PATCH 1/8] Added a public repo filter --- .../audit/github/generators/exec.rb | 12 +++-- .../audit/github/generators/exec_test.rb | 52 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/way_of_working/audit/github/generators/exec.rb b/lib/way_of_working/audit/github/generators/exec.rb index 816e6f5..a6cd245 100644 --- a/lib/way_of_working/audit/github/generators/exec.rb +++ b/lib/way_of_working/audit/github/generators/exec.rb @@ -17,12 +17,15 @@ class Exec < Thor::Group class_option :all_repos, type: :boolean, default: false, desc: 'Audit all repositories in the organisation (not just this repo)' - class_option :topic, type: :string, default: nil, - desc: 'Filter repositories by topic (e.g., way-of-working)' - class_option :fix, type: :boolean, default: false, desc: 'Attempt to automatically fix issues where possible' + class_option :public, type: :boolean, default: false, + desc: 'Filter to only public repositories' + + class_option :topic, type: :string, default: nil, + desc: 'Filter repositories by topic (e.g., way-of-working)' + desc 'This runs the github audit on this project' def check_for_github_token_environment_variables @@ -67,6 +70,9 @@ def prep_audit repo.topics.include?(options[:topic]) end end + + # Filter by visibility if specified + @repositories = @repositories.select(&:public?) if options[:public] rescue Octokit::Unauthorized abort(Rainbow("\nGITHUB_TOKEN has expired or does not have sufficient permission").red) end diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index d939aa1..af80031 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -41,6 +41,12 @@ class ExecTest < Rails::Generators::TestCase assert_nil generator_class.class_options[:topic].default end + test 'generator has public option' do + assert generator_class.class_options.key?(:public) + assert_equal :boolean, generator_class.class_options[:public].type + assert_equal false, generator_class.class_options[:public].default + end + test 'generator has fix option' do assert generator_class.class_options.key?(:fix) assert_equal :boolean, generator_class.class_options[:fix].type @@ -142,6 +148,52 @@ class ExecTest < Rails::Generators::TestCase assert_includes repositories.map(&:name), 'test_repo' assert_includes repositories.map(&:name), 'third_repo' end + + test 'prep_audit filters repositories to only public when public is true' do + # Mock the auditor + mock_repo1 = stub(name: 'public_repo', archived?: false, public?: true) + mock_repo2 = stub(name: 'private_repo', archived?: false, public?: false) + mock_repo3 = stub(name: 'another_public_repo', archived?: false, public?: true) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with public filter + generator = generator_class.new([], { all_repos: true, public: true }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + generator.prep_audit + + repositories = generator.instance_variable_get(:@repositories) + # Should only include public repos + assert_equal 2, repositories.length + assert_includes repositories.map(&:name), 'public_repo' + assert_includes repositories.map(&:name), 'another_public_repo' + end + + test 'prep_audit combines topic and public filters when both are specified' do + # Mock the auditor + mock_repo1 = stub(name: 'public_with_topic', archived?: false, public?: true, topics: ['way-of-working']) + mock_repo2 = stub(name: 'private_with_topic', archived?: false, public?: false, topics: ['way-of-working']) + mock_repo3 = stub(name: 'public_without_topic', archived?: false, public?: true, topics: ['other']) + mock_repo4 = stub(name: 'private_without_topic', archived?: false, public?: false, topics: ['other']) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3, mock_repo4]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with both topic and public filters + generator = generator_class.new([], { all_repos: true, topic: 'way-of-working', public: true }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + generator.prep_audit + + repositories = generator.instance_variable_get(:@repositories) + # Should only include public repos with 'way-of-working' topic + assert_equal 1, repositories.length + assert_equal 'public_with_topic', repositories.first.name + end end end end From 9c5f92bdcabaa2c704846087b0e7e005502022f1 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:36:47 +0000 Subject: [PATCH 2/8] Not private --- lib/way_of_working/audit/github/generators/exec.rb | 2 +- .../audit/github/generators/exec_test.rb | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/way_of_working/audit/github/generators/exec.rb b/lib/way_of_working/audit/github/generators/exec.rb index a6cd245..87edce2 100644 --- a/lib/way_of_working/audit/github/generators/exec.rb +++ b/lib/way_of_working/audit/github/generators/exec.rb @@ -72,7 +72,7 @@ def prep_audit end # Filter by visibility if specified - @repositories = @repositories.select(&:public?) if options[:public] + @repositories = @repositories.reject(&:private?) if options[:public] rescue Octokit::Unauthorized abort(Rainbow("\nGITHUB_TOKEN has expired or does not have sufficient permission").red) end diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index af80031..b093012 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -151,9 +151,9 @@ class ExecTest < Rails::Generators::TestCase test 'prep_audit filters repositories to only public when public is true' do # Mock the auditor - mock_repo1 = stub(name: 'public_repo', archived?: false, public?: true) - mock_repo2 = stub(name: 'private_repo', archived?: false, public?: false) - mock_repo3 = stub(name: 'another_public_repo', archived?: false, public?: true) + mock_repo1 = stub(name: 'public_repo', archived?: false, private?: false) + mock_repo2 = stub(name: 'private_repo', archived?: false, private?: true) + mock_repo3 = stub(name: 'another_public_repo', archived?: false, private?: false) mock_auditor = mock mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3]) @@ -174,10 +174,10 @@ class ExecTest < Rails::Generators::TestCase test 'prep_audit combines topic and public filters when both are specified' do # Mock the auditor - mock_repo1 = stub(name: 'public_with_topic', archived?: false, public?: true, topics: ['way-of-working']) - mock_repo2 = stub(name: 'private_with_topic', archived?: false, public?: false, topics: ['way-of-working']) - mock_repo3 = stub(name: 'public_without_topic', archived?: false, public?: true, topics: ['other']) - mock_repo4 = stub(name: 'private_without_topic', archived?: false, public?: false, topics: ['other']) + mock_repo1 = stub(name: 'public_with_topic', archived?: false, private?: false, topics: ['way-of-working']) + mock_repo2 = stub(name: 'private_with_topic', archived?: false, private?: true, topics: ['way-of-working']) + mock_repo3 = stub(name: 'public_without_topic', archived?: false, private?: false, topics: ['other']) + mock_repo4 = stub(name: 'private_without_topic', archived?: false, private?: true, topics: ['other']) mock_auditor = mock mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3, mock_repo4]) From 17dc9a6d4b102bc344f96e89cf3425a5168294c2 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:44:21 +0000 Subject: [PATCH 3/8] Updated all switch --- CHANGELOG.md | 3 ++- README.md | 17 +++++++++---- .../audit/github/generators/exec.rb | 8 +++---- .../audit/github/generators/exec_test.rb | 24 +++++++++---------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4d8c29..4fd793d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `--all-repos` option to audit all repositories in the organisation (not just the current repo) +- `--all` option to audit all repositories in the organisation (not just the current repo) - `--topic` option to filter repositories by GitHub topics (e.g., way-of-working) +- `--public` option to filter to only public repositories - `--fix` option to automatically fix issues where possible (passed to individual rules) ## [1.0.1] - 2025-01-24 diff --git a/README.md b/README.md index 6676a5e..1b54577 100644 --- a/README.md +++ b/README.md @@ -38,20 +38,27 @@ Then to run the GitHub audit for your project, use: way_of_working exec audit_github ``` -By default, the audit runs only against repositories that are configured as git remotes in your current project. To audit all repositories in your organisation, use the `--all-repos` flag: +By default, the audit runs only against repositories that are configured as git remotes in your current project. To audit all repositories in your organisation, use the `--all` flag: ```bash -way_of_working exec audit_github --all-repos +way_of_working exec audit_github --all ``` You can filter repositories by topic using the `--topic` flag. This accepts a single topic and will only audit repositories that have that topic: ```bash # Audit all repos with the 'way-of-working' topic -way_of_working exec audit_github --all-repos --topic way-of-working +way_of_working exec audit_github --all --topic way-of-working # Audit all repos with the 'indoor-mapping' topic -way_of_working exec audit_github --all-repos --topic indoor-mapping +way_of_working exec audit_github --all --topic indoor-mapping +``` + +You can filter to only public repositories using the `--public` flag: + +```bash +# Audit all public repos +way_of_working exec audit_github --all --public ``` To automatically fix issues where possible, use the `--fix` flag: @@ -61,7 +68,7 @@ To automatically fix issues where possible, use the `--fix` flag: way_of_working exec audit_github --fix # Audit and fix issues in all repos with a specific topic -way_of_working exec audit_github --all-repos --topic way-of-working --fix +way_of_working exec audit_github --all --topic way-of-working --fix ``` Note: The `--fix` flag is passed to individual rules, which may implement automatic fixes for their specific checks. Not all rules support automatic fixing. diff --git a/lib/way_of_working/audit/github/generators/exec.rb b/lib/way_of_working/audit/github/generators/exec.rb index 87edce2..f350fc0 100644 --- a/lib/way_of_working/audit/github/generators/exec.rb +++ b/lib/way_of_working/audit/github/generators/exec.rb @@ -12,10 +12,8 @@ module Github module Generators # This generator runs the github audit class Exec < Thor::Group - # argument :all_repos, type: :string, required: false, desc: 'Optional repo to test' - - class_option :all_repos, type: :boolean, default: false, - desc: 'Audit all repositories in the organisation (not just this repo)' + class_option :all, type: :boolean, default: false, + desc: 'Audit all repositories in the organisation (not just this repo)' class_option :fix, type: :boolean, default: false, desc: 'Attempt to automatically fix issues where possible' @@ -58,7 +56,7 @@ def prep_audit # Loop though all the repos @repositories = @auditor.repositories - unless options[:all_repos] + unless options[:all] @repositories = @repositories.select do |repo| github_organisation_remotes.include?(repo.name) end diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index b093012..36b83c8 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -29,10 +29,10 @@ class ExecTest < Rails::Generators::TestCase ENV.delete('GITHUB_ORGANISATION') end - test 'generator has all_repos option' do - assert generator_class.class_options.key?(:all_repos) - assert_equal :boolean, generator_class.class_options[:all_repos].type - assert_equal false, generator_class.class_options[:all_repos].default + test 'generator has all option' do + assert generator_class.class_options.key?(:all) + assert_equal :boolean, generator_class.class_options[:all].type + assert_equal false, generator_class.class_options[:all].default end test 'generator has topic option' do @@ -83,7 +83,7 @@ class ExecTest < Rails::Generators::TestCase generator.prep_audit end - test 'prep_audit filters repositories when all_repos is false' do + test 'prep_audit filters repositories when all is false' do # Mock the auditor mock_repo1 = stub(name: 'test_repo', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -92,7 +92,7 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) - # Run generator with all_repos=false (default) + # Run generator with all=false (default) generator = generator_class.new([], {}, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') @@ -106,7 +106,7 @@ class ExecTest < Rails::Generators::TestCase assert_equal 'test_repo', repositories.first.name end - test 'prep_audit does not filter repositories when all_repos is true' do + test 'prep_audit does not filter repositories when all is true' do # Mock the auditor mock_repo1 = stub(name: 'test_repo', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -115,8 +115,8 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) - # Run generator with all_repos=true - generator = generator_class.new([], { all_repos: true }, {}) + # Run generator with all=true + generator = generator_class.new([], { all: true }, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit @@ -137,7 +137,7 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) # Run generator with topic filter - generator = generator_class.new([], { all_repos: true, topic: 'way-of-working' }, {}) + generator = generator_class.new([], { all: true, topic: 'way-of-working' }, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit @@ -160,7 +160,7 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) # Run generator with public filter - generator = generator_class.new([], { all_repos: true, public: true }, {}) + generator = generator_class.new([], { all: true, public: true }, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit @@ -184,7 +184,7 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) # Run generator with both topic and public filters - generator = generator_class.new([], { all_repos: true, topic: 'way-of-working', public: true }, {}) + generator = generator_class.new([], { all: true, topic: 'way-of-working', public: true }, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit From 4711187f463f0d6cb450cfdf9a13720aed37e294 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:51:53 +0000 Subject: [PATCH 4/8] name filter and thorification --- CHANGELOG.md | 1 + README.md | 10 +++ .../audit/github/generators/exec.rb | 46 +++++++---- .../audit/github/generators/exec_test.rb | 77 +++++++++++++++++++ 4 files changed, 120 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd793d..e26b4a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--all` option to audit all repositories in the organisation (not just the current repo) - `--topic` option to filter repositories by GitHub topics (e.g., way-of-working) +- `--name` option to filter repositories by name (supports multiple names) - `--public` option to filter to only public repositories - `--fix` option to automatically fix issues where possible (passed to individual rules) diff --git a/README.md b/README.md index 1b54577..5e8df2e 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,16 @@ way_of_working exec audit_github --all --topic way-of-working way_of_working exec audit_github --all --topic indoor-mapping ``` +You can filter repositories by name using the `--name` flag. This accepts one or more repository names: + +```bash +# Audit a single repository by name +way_of_working exec audit_github --all --name structured_store + +# Audit multiple repositories by name +way_of_working exec audit_github --all --name structured_store other_repo +``` + You can filter to only public repositories using the `--public` flag: ```bash diff --git a/lib/way_of_working/audit/github/generators/exec.rb b/lib/way_of_working/audit/github/generators/exec.rb index f350fc0..70617ac 100644 --- a/lib/way_of_working/audit/github/generators/exec.rb +++ b/lib/way_of_working/audit/github/generators/exec.rb @@ -13,11 +13,14 @@ module Generators # This generator runs the github audit class Exec < Thor::Group class_option :all, type: :boolean, default: false, - desc: 'Audit all repositories in the organisation (not just this repo)' + desc: 'Audit all repositories in the organisation (not just this repo)' class_option :fix, type: :boolean, default: false, desc: 'Attempt to automatically fix issues where possible' + class_option :name, type: :array, default: nil, + desc: 'Filter repositories by name (e.g., structured_store)' + class_option :public, type: :boolean, default: false, desc: 'Filter to only public repositories' @@ -56,23 +59,38 @@ def prep_audit # Loop though all the repos @repositories = @auditor.repositories - unless options[:all] - @repositories = @repositories.select do |repo| - github_organisation_remotes.include?(repo.name) - end + rescue Octokit::Unauthorized + abort(Rainbow("\nGITHUB_TOKEN has expired or does not have sufficient permission").red) + end + + def filter_all_if_specified + return if options[:all] + + @repositories = @repositories.select do |repo| + github_organisation_remotes.include?(repo.name) end + end - # Filter by topic if specified - if options[:topic] - @repositories = @repositories.select do |repo| - repo.topics.include?(options[:topic]) - end + def filter_by_name_array_if_specified + return unless options[:name] + + @repositories = @repositories.select do |repo| + options[:name].include?(repo.name) end + end - # Filter by visibility if specified - @repositories = @repositories.reject(&:private?) if options[:public] - rescue Octokit::Unauthorized - abort(Rainbow("\nGITHUB_TOKEN has expired or does not have sufficient permission").red) + def filter_by_topic_if_specified + return unless options[:topic] + + @repositories = @repositories.select do |repo| + repo.topics.include?(options[:topic]) + end + end + + def filter_by_visibility_if_specified + return unless options[:public] + + @repositories = @repositories.reject(&:private?) end def run_audit diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index 36b83c8..0b2a172 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -53,6 +53,12 @@ class ExecTest < Rails::Generators::TestCase assert_equal false, generator_class.class_options[:fix].default end + test 'generator has name option' do + assert generator_class.class_options.key?(:name) + assert_equal :array, generator_class.class_options[:name].type + assert_nil generator_class.class_options[:name].default + end + test 'prep_audit passes fix option to auditor when false' do # Mock the auditor mock_auditor = mock @@ -172,6 +178,51 @@ class ExecTest < Rails::Generators::TestCase assert_includes repositories.map(&:name), 'another_public_repo' end + test 'prep_audit filters repositories by name when name is specified' do + # Mock the auditor + mock_repo1 = stub(name: 'structured_store', archived?: false) + mock_repo2 = stub(name: 'other_repo', archived?: false) + mock_repo3 = stub(name: 'another_repo', archived?: false) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with name filter + generator = generator_class.new([], { all: true, name: ['structured_store'] }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + generator.prep_audit + + repositories = generator.instance_variable_get(:@repositories) + # Should only include structured_store + assert_equal 1, repositories.length + assert_equal 'structured_store', repositories.first.name + end + + test 'prep_audit filters repositories by multiple names when multiple names are specified' do + # Mock the auditor + mock_repo1 = stub(name: 'structured_store', archived?: false) + mock_repo2 = stub(name: 'other_repo', archived?: false) + mock_repo3 = stub(name: 'another_repo', archived?: false) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with multiple name filters + generator = generator_class.new([], { all: true, name: ['structured_store', 'another_repo'] }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + generator.prep_audit + + repositories = generator.instance_variable_get(:@repositories) + # Should only include structured_store and another_repo + assert_equal 2, repositories.length + assert_includes repositories.map(&:name), 'structured_store' + assert_includes repositories.map(&:name), 'another_repo' + end + test 'prep_audit combines topic and public filters when both are specified' do # Mock the auditor mock_repo1 = stub(name: 'public_with_topic', archived?: false, private?: false, topics: ['way-of-working']) @@ -194,6 +245,32 @@ class ExecTest < Rails::Generators::TestCase assert_equal 1, repositories.length assert_equal 'public_with_topic', repositories.first.name end + + test 'prep_audit combines name, topic and public filters when all are specified' do + # Mock the auditor + mock_repo1 = stub(name: 'structured_store', archived?: false, private?: false, + topics: ['way-of-working']) + mock_repo2 = stub(name: 'other_store', archived?: false, private?: true, topics: ['way-of-working']) + mock_repo3 = stub(name: 'structured_store', archived?: false, private?: false, topics: ['other']) + mock_repo4 = stub(name: 'public_with_topic', archived?: false, private?: false, + topics: ['way-of-working']) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3, mock_repo4]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with name, topic and public filters + generator = generator_class.new([], { all: true, name: ['structured_store'], topic: 'way-of-working', + public: true }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + generator.prep_audit + + repositories = generator.instance_variable_get(:@repositories) + # Should only include public structured_store with 'way-of-working' topic + assert_equal 1, repositories.length + assert_equal 'structured_store', repositories.first.name + end end end end From 9af9484475d824effcbc96bf92e0a23e2d809e84 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:53:51 +0000 Subject: [PATCH 5/8] Test fix --- .../audit/github/generators/exec_test.rb | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index 0b2a172..01cbb74 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -89,7 +89,7 @@ class ExecTest < Rails::Generators::TestCase generator.prep_audit end - test 'prep_audit filters repositories when all is false' do + test 'filter_all_if_specified filters repositories when all is false' do # Mock the auditor mock_repo1 = stub(name: 'test_repo', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -105,6 +105,7 @@ class ExecTest < Rails::Generators::TestCase # Stub the github_organisation_remotes method to return test_repo generator.stubs(:github_organisation_remotes).returns(['test_repo']) generator.prep_audit + generator.filter_all_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include test_repo (from git remotes) @@ -112,7 +113,7 @@ class ExecTest < Rails::Generators::TestCase assert_equal 'test_repo', repositories.first.name end - test 'prep_audit does not filter repositories when all is true' do + test 'filter_all_if_specified does not filter repositories when all is true' do # Mock the auditor mock_repo1 = stub(name: 'test_repo', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -126,13 +127,14 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_all_if_specified repositories = generator.instance_variable_get(:@repositories) # Should include all repos assert_equal 2, repositories.length end - test 'prep_audit filters repositories by topic when topic is specified' do + test 'filter_by_topic_if_specified filters repositories by topic when topic is specified' do # Mock the auditor mock_repo1 = stub(name: 'test_repo', archived?: false, topics: ['way-of-working', 'ruby']) mock_repo2 = stub(name: 'other_repo', archived?: false, topics: ['python']) @@ -147,6 +149,7 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_topic_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include repos with 'way-of-working' topic @@ -155,7 +158,7 @@ class ExecTest < Rails::Generators::TestCase assert_includes repositories.map(&:name), 'third_repo' end - test 'prep_audit filters repositories to only public when public is true' do + test 'filter_by_visibility_if_specified filters repositories to only public when public is true' do # Mock the auditor mock_repo1 = stub(name: 'public_repo', archived?: false, private?: false) mock_repo2 = stub(name: 'private_repo', archived?: false, private?: true) @@ -170,6 +173,7 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_visibility_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include public repos @@ -178,7 +182,7 @@ class ExecTest < Rails::Generators::TestCase assert_includes repositories.map(&:name), 'another_public_repo' end - test 'prep_audit filters repositories by name when name is specified' do + test 'filter_by_name_array_if_specified filters repositories by name when name is specified' do # Mock the auditor mock_repo1 = stub(name: 'structured_store', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -193,6 +197,7 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_name_array_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include structured_store @@ -200,7 +205,7 @@ class ExecTest < Rails::Generators::TestCase assert_equal 'structured_store', repositories.first.name end - test 'prep_audit filters repositories by multiple names when multiple names are specified' do + test 'filter_by_name_array_if_specified filters repositories by multiple names when multiple names are specified' do # Mock the auditor mock_repo1 = stub(name: 'structured_store', archived?: false) mock_repo2 = stub(name: 'other_repo', archived?: false) @@ -215,6 +220,7 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_name_array_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include structured_store and another_repo @@ -223,7 +229,7 @@ class ExecTest < Rails::Generators::TestCase assert_includes repositories.map(&:name), 'another_repo' end - test 'prep_audit combines topic and public filters when both are specified' do + test 'combines topic and public filters when both are specified' do # Mock the auditor mock_repo1 = stub(name: 'public_with_topic', archived?: false, private?: false, topics: ['way-of-working']) mock_repo2 = stub(name: 'private_with_topic', archived?: false, private?: true, topics: ['way-of-working']) @@ -239,6 +245,8 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_topic_if_specified + generator.filter_by_visibility_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include public repos with 'way-of-working' topic @@ -246,7 +254,7 @@ class ExecTest < Rails::Generators::TestCase assert_equal 'public_with_topic', repositories.first.name end - test 'prep_audit combines name, topic and public filters when all are specified' do + test 'combines name, topic and public filters when all are specified' do # Mock the auditor mock_repo1 = stub(name: 'structured_store', archived?: false, private?: false, topics: ['way-of-working']) @@ -265,6 +273,9 @@ class ExecTest < Rails::Generators::TestCase generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit + generator.filter_by_name_array_if_specified + generator.filter_by_topic_if_specified + generator.filter_by_visibility_if_specified repositories = generator.instance_variable_get(:@repositories) # Should only include public structured_store with 'way-of-working' topic From 3b12d997fbbbad1522bd2534b12af9f48baf9ab3 Mon Sep 17 00:00:00 2001 From: Tim Gentry <52189+timgentry@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:04:04 +0000 Subject: [PATCH 6/8] More logical use of name switch --- README.md | 6 ++--- .../audit/github/generators/exec.rb | 2 +- .../audit/github/generators/exec_test.rb | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5e8df2e..d349762 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,14 @@ way_of_working exec audit_github --all --topic way-of-working way_of_working exec audit_github --all --topic indoor-mapping ``` -You can filter repositories by name using the `--name` flag. This accepts one or more repository names: +You can filter repositories by name using the `--name` flag. This accepts one or more repository names and automatically audits all repositories in the organisation (you don't need to specify `--all`): ```bash # Audit a single repository by name -way_of_working exec audit_github --all --name structured_store +way_of_working exec audit_github --name structured_store # Audit multiple repositories by name -way_of_working exec audit_github --all --name structured_store other_repo +way_of_working exec audit_github --name structured_store other_repo ``` You can filter to only public repositories using the `--public` flag: diff --git a/lib/way_of_working/audit/github/generators/exec.rb b/lib/way_of_working/audit/github/generators/exec.rb index 70617ac..aa98bfe 100644 --- a/lib/way_of_working/audit/github/generators/exec.rb +++ b/lib/way_of_working/audit/github/generators/exec.rb @@ -64,7 +64,7 @@ def prep_audit end def filter_all_if_specified - return if options[:all] + return if options[:all] || options[:name] @repositories = @repositories.select do |repo| github_organisation_remotes.include?(repo.name) diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index 01cbb74..752eb4c 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -205,6 +205,30 @@ class ExecTest < Rails::Generators::TestCase assert_equal 'structured_store', repositories.first.name end + test 'filter_all_if_specified does not filter when name is specified without all' do + # Mock the auditor + mock_repo1 = stub(name: 'structured_store', archived?: false) + mock_repo2 = stub(name: 'other_repo', archived?: false) + mock_repo3 = stub(name: 'test_repo', archived?: false) + mock_auditor = mock + mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3]) + + Auditor.stubs(:new).returns(mock_auditor) + + # Run generator with name filter only (no --all flag) + generator = generator_class.new([], { name: ['structured_store'] }, {}) + generator.instance_variable_set(:@github_token, 'test_token') + generator.instance_variable_set(:@github_organisation, 'test_org') + # Stub the github_organisation_remotes method + generator.stubs(:github_organisation_remotes).returns(['test_repo']) + generator.prep_audit + generator.filter_all_if_specified + + repositories = generator.instance_variable_get(:@repositories) + # Should include all repos because --name implies --all + assert_equal 3, repositories.length + end + test 'filter_by_name_array_if_specified filters repositories by multiple names when multiple names are specified' do # Mock the auditor mock_repo1 = stub(name: 'structured_store', archived?: false) From c7a0f598acec8dc238ca8f766f485c3923f38039 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 21:53:57 +0100 Subject: [PATCH 7/8] linting --- .../audit/github/generators/exec_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/way_of_working/audit/github/generators/exec_test.rb b/test/way_of_working/audit/github/generators/exec_test.rb index 752eb4c..51c065e 100644 --- a/test/way_of_working/audit/github/generators/exec_test.rb +++ b/test/way_of_working/audit/github/generators/exec_test.rb @@ -18,8 +18,8 @@ class ExecTest < Rails::Generators::TestCase # Mock Git operations mock_git = mock mock_git.stubs(:remotes).returns([ - stub(url: 'https://github.com/test_org/test_repo.git') - ]) + stub(url: 'https://github.com/test_org/test_repo.git') + ]) Git.stubs(:open).returns(mock_git) end @@ -136,7 +136,7 @@ class ExecTest < Rails::Generators::TestCase test 'filter_by_topic_if_specified filters repositories by topic when topic is specified' do # Mock the auditor - mock_repo1 = stub(name: 'test_repo', archived?: false, topics: ['way-of-working', 'ruby']) + mock_repo1 = stub(name: 'test_repo', archived?: false, topics: %w[way-of-working ruby]) mock_repo2 = stub(name: 'other_repo', archived?: false, topics: ['python']) mock_repo3 = stub(name: 'third_repo', archived?: false, topics: ['way-of-working']) mock_auditor = mock @@ -240,7 +240,7 @@ class ExecTest < Rails::Generators::TestCase Auditor.stubs(:new).returns(mock_auditor) # Run generator with multiple name filters - generator = generator_class.new([], { all: true, name: ['structured_store', 'another_repo'] }, {}) + generator = generator_class.new([], { all: true, name: %w[structured_store another_repo] }, {}) generator.instance_variable_set(:@github_token, 'test_token') generator.instance_variable_set(:@github_organisation, 'test_org') generator.prep_audit @@ -281,11 +281,11 @@ class ExecTest < Rails::Generators::TestCase test 'combines name, topic and public filters when all are specified' do # Mock the auditor mock_repo1 = stub(name: 'structured_store', archived?: false, private?: false, - topics: ['way-of-working']) + topics: ['way-of-working']) mock_repo2 = stub(name: 'other_store', archived?: false, private?: true, topics: ['way-of-working']) mock_repo3 = stub(name: 'structured_store', archived?: false, private?: false, topics: ['other']) mock_repo4 = stub(name: 'public_with_topic', archived?: false, private?: false, - topics: ['way-of-working']) + topics: ['way-of-working']) mock_auditor = mock mock_auditor.stubs(:repositories).returns([mock_repo1, mock_repo2, mock_repo3, mock_repo4]) From a68cb45a23e0e92844b3a4f62e0c37805cdab6cc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 22:53:07 +0100 Subject: [PATCH 8/8] Reword CHANGELOG to satisfy inclusive language check alex flagged "just" as potentially insensitive; replace with "only". Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e26b4a8..4119975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `--all` option to audit all repositories in the organisation (not just the current repo) +- `--all` option to audit all repositories in the organisation (not only the current repo) - `--topic` option to filter repositories by GitHub topics (e.g., way-of-working) - `--name` option to filter repositories by name (supports multiple names) - `--public` option to filter to only public repositories