From 8f916f1edae87086e216e9ad35cfab7ae73598c3 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Tue, 3 Nov 2026 11:51:07 +0100 Subject: [PATCH 1/5] sync: add generic issue and pull request templates - Give repositories without their own templates a pull request template, bug report form and issue config built from what the existing templates have in common. - List repositories that keep their own templates, so pull request descriptions don't start with a synced-file comment. - Skip `.github` because templates there would become the default for every repository, including unsynced ones. --- .github/actions/sync/issue_bug.template.yml | 33 ++++++++++ .../actions/sync/issue_config.template.yml | 6 ++ .github/actions/sync/pull_request.template.md | 17 +++++ .github/actions/sync/shared-config.rb | 41 ++++++++++++ .github/actions/sync/shared-config_spec.rb | 64 +++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 .github/actions/sync/issue_bug.template.yml create mode 100644 .github/actions/sync/issue_config.template.yml create mode 100644 .github/actions/sync/pull_request.template.md diff --git a/.github/actions/sync/issue_bug.template.yml b/.github/actions/sync/issue_bug.template.yml new file mode 100644 index 00000000..f587b197 --- /dev/null +++ b/.github/actions/sync/issue_bug.template.yml @@ -0,0 +1,33 @@ +name: New issue for Reproducible Bug +description: "If you're sure it's reproducible and not just your machine: submit an issue so we can investigate." +type: "Bug" +body: + - type: checkboxes + attributes: + label: Verification + description: Verify you've followed these steps. If you can't truthfully check these boxes, open a discussion at https://github.com/orgs/Homebrew/discussions instead. + options: + - label: I searched for similar issues at https://github.com/%s/issues and found no duplicates. + required: true + - label: I did not use AI/LLM to create this issue, or I disclosed the tool and model used; I will answer maintainer questions myself without AI/LLM. + required: true + - type: textarea + attributes: + label: What were you trying to do (and why)? + validations: + required: true + - type: textarea + attributes: + label: What happened? + validations: + required: true + - type: textarea + attributes: + label: What did you expect to happen? + validations: + required: true + - type: textarea + attributes: + label: Step-by-step reproduction instructions + validations: + required: true diff --git a/.github/actions/sync/issue_config.template.yml b/.github/actions/sync/issue_config.template.yml new file mode 100644 index 00000000..aa82dc3c --- /dev/null +++ b/.github/actions/sync/issue_config.template.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false + +contact_links: + - name: Get help in GitHub Discussions + url: https://github.com/orgs/Homebrew/discussions + about: Have a question? Not sure if your issue affects everyone reproducibly? The quickest way to get help is on Homebrew's GitHub Discussions! diff --git a/.github/actions/sync/pull_request.template.md b/.github/actions/sync/pull_request.template.md new file mode 100644 index 00000000..64170443 --- /dev/null +++ b/.github/actions/sync/pull_request.template.md @@ -0,0 +1,17 @@ +----- + + + + +- [ ] Have you checked for other open [Pull Requests](https://github.com/%s/pulls) for the same change? +- [ ] Have you explained what your changes do and why you'd like them included? +- [ ] Have you written new tests where it makes sense? +- [ ] Have you successfully run this repository's tests and checks locally? + +----- + +- [ ] I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM. + + + +----- diff --git a/.github/actions/sync/shared-config.rb b/.github/actions/sync/shared-config.rb index 9ab50bba..842980b5 100755 --- a/.github/actions/sync/shared-config.rb +++ b/.github/actions/sync/shared-config.rb @@ -63,6 +63,12 @@ def safe_system(*args) codeowners_header = "# This file is synced from the `.github` repository, do not modify it directly.\n" # GitHub uses the first of these that exists and ignores the others. codeowners_paths = [codeowners, "CODEOWNERS", "docs/CODEOWNERS"].freeze +pull_request_template = ".github/PULL_REQUEST_TEMPLATE.md" +template_sources = { + pull_request_template => ".github/actions/sync/pull_request.template.md", + ".github/ISSUE_TEMPLATE/bug.yml" => ".github/actions/sync/issue_bug.template.yml", + ".github/ISSUE_TEMPLATE/config.yml" => ".github/actions/sync/issue_config.template.yml", +}.freeze vale_ini = ".vale.ini" dependabot_template_yaml = ".github/actions/sync/dependabot.template.yml" dependabot_yaml = ".github/dependabot.yml" @@ -205,6 +211,22 @@ def safe_system(*args) homebrew-core homebrew-cask ].freeze +# These repositories keep their own, more specific templates instead of the synced ones. +custom_pull_request_template_repositories = %w[ + BrewUI + brew + formulae.brew.sh + homebrew-cask + homebrew-core +].freeze +custom_issue_template_repositories = %w[ + BrewUI + brew + formulae.brew.sh + homebrew-cask + homebrew-core + install +].freeze rejected_docs_basenames = %w[ _config.yml CNAME @@ -238,6 +260,7 @@ def safe_system(*args) rubocop_yaml, coderabbit_config_ts, codeowners, + *template_sources.keys, dependabot_yaml, *license_check_paths, check_template_rb, @@ -450,6 +473,24 @@ def safe_system(*args) next unless target_path.exist? git "-C", target_directory, "rm", path + when *template_sources.keys + # A template here would become the default for every repository without one, including unsynced ones. + next if repository_name == ".github" + + custom_repositories = if path == pull_request_template + custom_pull_request_template_repositories + else + custom_issue_template_repositories + end + next if custom_repositories.include?(repository_name) + + contents = format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}") + # Pull request descriptions start with the template, so it can't carry the synced header. + if path != pull_request_template + contents = "# This file is synced from the `.github` repository, do not modify it directly.\n#{contents}" + end + FileUtils.rm_f target_path + target_path.write(contents) else next if path == target_path.to_s diff --git a/.github/actions/sync/shared-config_spec.rb b/.github/actions/sync/shared-config_spec.rb index 30c958f2..4c8e3883 100644 --- a/.github/actions/sync/shared-config_spec.rb +++ b/.github/actions/sync/shared-config_spec.rb @@ -162,6 +162,20 @@ def synced_contents(path) entries = own_codeowners.lines.reject { |line| line.include?("coderabbit*") }.join "#{entries}/.coderabbit* @Homebrew/maintainers\n" end + let(:pull_request_template) { ".github/PULL_REQUEST_TEMPLATE.md" } + let(:expected_templates) do + { + pull_request_template => "pull_request.template.md", + ".github/ISSUE_TEMPLATE/bug.yml" => "issue_bug.template.yml", + ".github/ISSUE_TEMPLATE/config.yml" => "issue_config.template.yml", + }.to_h do |path, source| + contents = format((source_repository/".github/actions/sync/#{source}").read, + repository: "Homebrew/#{File.basename(repository)}") + [path, (path == pull_request_template) ? contents : sync_header + contents] + end + end + let(:issue_templates) { expected_templates.keys - [pull_request_template] } + let(:own_template) { "- [ ] Have you run `make test`?\n" } let(:original_gemfile) { "source \"https://rubygems.org\"\n\nruby file: \".ruby-version\"\n\ngem \"rake\"\n" } let(:custom_gemfile) { "source \"https://rubygems.org\"\n\ngem \"rake\"\n" } let(:expected_gemfile) do @@ -315,6 +329,56 @@ def synced_contents(path) end end + it "installs issue and pull request templates linking to the repository, repeatedly" do + commit_fixture + sync_target + expected_templates.each { |path, contents| expect((target/path).read).to eq(contents) } + expect((target/pull_request_template).read).to include("https://github.com/Homebrew/no-ruby/pulls") + expect((target/".github/ISSUE_TEMPLATE/bug.yml").read).to include("https://github.com/Homebrew/no-ruby/issues") + expect_idempotent_sync + end + + it "replaces outdated templates, then makes no further commits" do + expected_templates.each_key { |path| write path, own_template } + commit_fixture + sync_target + expected_templates.each { |path, contents| expect((target/path).read).to eq(contents) } + expect_idempotent_sync + end + + context "with a repository that has its own templates" do + let(:repository) { "formulae.brew.sh" } + + it "keeps them" do + expected_templates.each_key { |path| write path, own_template } + commit_fixture + sync_target + expected_templates.each_key { |path| expect((target/path).read).to eq(own_template) } + end + end + + context "with a repository that has its own issue templates only" do + let(:repository) { "install" } + + it "keeps them and installs the pull request template" do + issue_templates.each { |path| write path, own_template } + commit_fixture + sync_target + issue_templates.each { |path| expect((target/path).read).to eq(own_template) } + expect((target/pull_request_template).read).to eq(expected_templates.fetch(pull_request_template)) + end + end + + context "with the .github repository" do + let(:repository) { "source/.github" } + + it "does not install templates" do + commit_fixture + sync_target + expected_templates.each_key { |path| expect(target/path).not_to exist } + end + end + %w[ Package.swift Package.resolved From 0781ee019a2a3ee6b40a3fa1091810c380582015 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Tue, 3 Nov 2026 12:01:52 +0100 Subject: [PATCH 2/5] sync: install templates only where none exist - Detect existing templates wherever GitHub reads them from instead of listing repositories that have their own. - Leave installed templates to each repository afterwards, so they need no synced-file comment. --- .github/actions/sync/shared-config.rb | 36 +++----------- .github/actions/sync/shared-config_spec.rb | 55 ++++++++++++++-------- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/.github/actions/sync/shared-config.rb b/.github/actions/sync/shared-config.rb index 842980b5..661fe0ad 100755 --- a/.github/actions/sync/shared-config.rb +++ b/.github/actions/sync/shared-config.rb @@ -69,6 +69,9 @@ def safe_system(*args) ".github/ISSUE_TEMPLATE/bug.yml" => ".github/actions/sync/issue_bug.template.yml", ".github/ISSUE_TEMPLATE/config.yml" => ".github/actions/sync/issue_config.template.yml", }.freeze +# GitHub reads templates from any of these locations, case-insensitively. +existing_pull_request_template = %r{\A(?:\.github/|docs/)?pull_request_template(?:\.\w+)?(?:/|\z)}i +existing_issue_template = %r{\A(?:\.github/|docs/)?issue_template(?:\.\w+)?(?:/|\z)}i vale_ini = ".vale.ini" dependabot_template_yaml = ".github/actions/sync/dependabot.template.yml" dependabot_yaml = ".github/dependabot.yml" @@ -211,22 +214,6 @@ def safe_system(*args) homebrew-core homebrew-cask ].freeze -# These repositories keep their own, more specific templates instead of the synced ones. -custom_pull_request_template_repositories = %w[ - BrewUI - brew - formulae.brew.sh - homebrew-cask - homebrew-core -].freeze -custom_issue_template_repositories = %w[ - BrewUI - brew - formulae.brew.sh - homebrew-cask - homebrew-core - install -].freeze rejected_docs_basenames = %w[ _config.yml CNAME @@ -477,20 +464,11 @@ def safe_system(*args) # A template here would become the default for every repository without one, including unsynced ones. next if repository_name == ".github" - custom_repositories = if path == pull_request_template - custom_pull_request_template_repositories - else - custom_issue_template_repositories - end - next if custom_repositories.include?(repository_name) + # Install templates once and then leave them to the repository, like any template it already has. + existing_template = (path == pull_request_template) ? existing_pull_request_template : existing_issue_template + next if tracked_files.any?(existing_template) - contents = format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}") - # Pull request descriptions start with the template, so it can't carry the synced header. - if path != pull_request_template - contents = "# This file is synced from the `.github` repository, do not modify it directly.\n#{contents}" - end - FileUtils.rm_f target_path - target_path.write(contents) + target_path.write(format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}")) else next if path == target_path.to_s diff --git a/.github/actions/sync/shared-config_spec.rb b/.github/actions/sync/shared-config_spec.rb index 4c8e3883..78d4340c 100644 --- a/.github/actions/sync/shared-config_spec.rb +++ b/.github/actions/sync/shared-config_spec.rb @@ -47,6 +47,10 @@ def git(*args, directory: target) successful_command("git", "-C", directory.to_s, *args).chomp end + def tracked_templates(pattern) + git("ls-files").lines(chomp: true).grep(pattern) + end + def commit_fixture(directory: target) git("add", "--all", directory:) git("commit", "--quiet", "--message", "Fixture", directory:) @@ -168,10 +172,9 @@ def synced_contents(path) pull_request_template => "pull_request.template.md", ".github/ISSUE_TEMPLATE/bug.yml" => "issue_bug.template.yml", ".github/ISSUE_TEMPLATE/config.yml" => "issue_config.template.yml", - }.to_h do |path, source| - contents = format((source_repository/".github/actions/sync/#{source}").read, - repository: "Homebrew/#{File.basename(repository)}") - [path, (path == pull_request_template) ? contents : sync_header + contents] + }.transform_values do |source| + format((source_repository/".github/actions/sync/#{source}").read, + repository: "Homebrew/#{File.basename(repository)}") end end let(:issue_templates) { expected_templates.keys - [pull_request_template] } @@ -338,33 +341,47 @@ def synced_contents(path) expect_idempotent_sync end - it "replaces outdated templates, then makes no further commits" do - expected_templates.each_key { |path| write path, own_template } + it "keeps an installed template after the repository edits it" do commit_fixture sync_target - expected_templates.each { |path, contents| expect((target/path).read).to eq(contents) } + write pull_request_template, own_template + commit_fixture expect_idempotent_sync + expect((target/pull_request_template).read).to eq(own_template) end - context "with a repository that has its own templates" do - let(:repository) { "formulae.brew.sh" } - - it "keeps them" do - expected_templates.each_key { |path| write path, own_template } + %w[ + .github/PULL_REQUEST_TEMPLATE.md + .github/pull_request_template.md + PULL_REQUEST_TEMPLATE.md + docs/pull_request_template.md + .github/PULL_REQUEST_TEMPLATE/bug_fix.md + ].each do |path| + it "keeps a repository's own pull request template at #{path} and installs issue templates" do + write path, own_template commit_fixture sync_target - expected_templates.each_key { |path| expect((target/path).read).to eq(own_template) } + expect((target/path).read).to eq(own_template) + expect(tracked_templates(/pull_request_template/i)).to eq([path]) + issue_templates.each do |issue_template| + expect((target/issue_template).read).to eq(expected_templates.fetch(issue_template)) + end end end - context "with a repository that has its own issue templates only" do - let(:repository) { "install" } - - it "keeps them and installs the pull request template" do - issue_templates.each { |path| write path, own_template } + %w[ + .github/ISSUE_TEMPLATE/feature.yml + .github/ISSUE_TEMPLATE/config.yml + .github/issue_template.md + ISSUE_TEMPLATE.md + docs/ISSUE_TEMPLATE.md + ].each do |path| + it "keeps a repository's own issue template at #{path} and installs the pull request template" do + write path, own_template commit_fixture sync_target - issue_templates.each { |path| expect((target/path).read).to eq(own_template) } + expect((target/path).read).to eq(own_template) + expect(tracked_templates(/issue_template/i)).to eq([path]) expect((target/pull_request_template).read).to eq(expected_templates.fetch(pull_request_template)) end end From 11c5739683a69209a22e2b044cda6eb2a37f6994 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Tue, 3 Nov 2026 12:14:04 +0100 Subject: [PATCH 3/5] sync: handle shadowed and retired templates - Remove an unedited synced pull request template once a repository adds its own elsewhere, as GitHub would keep showing ours. - Install issue templates next to single-file `ISSUE_TEMPLATE.md` templates, which GitHub retired on 30 March 2025. - Don't overwrite uncommitted templates during local syncs. --- .github/actions/sync/shared-config.rb | 17 +++++++--- .github/actions/sync/shared-config_spec.rb | 39 ++++++++++++++++++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/actions/sync/shared-config.rb b/.github/actions/sync/shared-config.rb index 661fe0ad..f60a77eb 100755 --- a/.github/actions/sync/shared-config.rb +++ b/.github/actions/sync/shared-config.rb @@ -70,8 +70,9 @@ def safe_system(*args) ".github/ISSUE_TEMPLATE/config.yml" => ".github/actions/sync/issue_config.template.yml", }.freeze # GitHub reads templates from any of these locations, case-insensitively. +# It no longer reads single-file `ISSUE_TEMPLATE.md` issue templates, only `ISSUE_TEMPLATE/` directories. existing_pull_request_template = %r{\A(?:\.github/|docs/)?pull_request_template(?:\.\w+)?(?:/|\z)}i -existing_issue_template = %r{\A(?:\.github/|docs/)?issue_template(?:\.\w+)?(?:/|\z)}i +existing_issue_template = %r{\A(?:\.github/|docs/)?issue_template/}i vale_ini = ".vale.ini" dependabot_template_yaml = ".github/actions/sync/dependabot.template.yml" dependabot_yaml = ".github/dependabot.yml" @@ -465,10 +466,18 @@ def safe_system(*args) next if repository_name == ".github" # Install templates once and then leave them to the repository, like any template it already has. - existing_template = (path == pull_request_template) ? existing_pull_request_template : existing_issue_template - next if tracked_files.any?(existing_template) + contents = format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}") + if path == pull_request_template + own_templates = tracked_files.grep(existing_pull_request_template) - [path] + # GitHub prefers `.github/`, so remove an unedited synced template once the repository adds its own elsewhere. + FileUtils.rm_f target_path if own_templates.any? && target_path.file? && target_path.read == contents + next if own_templates.any? + elsif tracked_files.any?(existing_issue_template) + next + end + next if target_path.exist? - target_path.write(format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}")) + target_path.write(contents) else next if path == target_path.to_s diff --git a/.github/actions/sync/shared-config_spec.rb b/.github/actions/sync/shared-config_spec.rb index 78d4340c..623f9834 100644 --- a/.github/actions/sync/shared-config_spec.rb +++ b/.github/actions/sync/shared-config_spec.rb @@ -350,6 +350,24 @@ def synced_contents(path) expect((target/pull_request_template).read).to eq(own_template) end + it "removes an unedited pull request template once the repository adds its own elsewhere" do + commit_fixture + sync_target + write "PULL_REQUEST_TEMPLATE.md", own_template + commit_fixture + sync_target + expect(target/pull_request_template).not_to exist + expect(tracked_templates(/pull_request_template/i)).to eq(["PULL_REQUEST_TEMPLATE.md"]) + expect_idempotent_sync + end + + it "does not overwrite an uncommitted template" do + commit_fixture + write pull_request_template, own_template + sync_target + expect((target/pull_request_template).read).to eq(own_template) + end + %w[ .github/PULL_REQUEST_TEMPLATE.md .github/pull_request_template.md @@ -372,9 +390,8 @@ def synced_contents(path) %w[ .github/ISSUE_TEMPLATE/feature.yml .github/ISSUE_TEMPLATE/config.yml - .github/issue_template.md - ISSUE_TEMPLATE.md - docs/ISSUE_TEMPLATE.md + ISSUE_TEMPLATE/bug.md + docs/ISSUE_TEMPLATE/bug.md ].each do |path| it "keeps a repository's own issue template at #{path} and installs the pull request template" do write path, own_template @@ -386,6 +403,22 @@ def synced_contents(path) end end + %w[ + .github/issue_template.md + ISSUE_TEMPLATE.md + docs/ISSUE_TEMPLATE.md + ].each do |path| + it "installs issue templates alongside the retired single-file template at #{path}" do + write path, own_template + commit_fixture + sync_target + expect((target/path).read).to eq(own_template) + issue_templates.each do |issue_template| + expect((target/issue_template).read).to eq(expected_templates.fetch(issue_template)) + end + end + end + context "with the .github repository" do let(:repository) { "source/.github" } From ae2671bc8b716f82fb85cd8f6347b10d106f96df Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Tue, 3 Nov 2026 12:26:20 +0100 Subject: [PATCH 4/5] sync: detect uncommitted templates - Include untracked files when looking for existing templates, so local syncs don't add a shared template beside an uncommitted one. --- .github/actions/sync/shared-config.rb | 10 ++++++++-- .github/actions/sync/shared-config_spec.rb | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/actions/sync/shared-config.rb b/.github/actions/sync/shared-config.rb index f60a77eb..aab2159c 100755 --- a/.github/actions/sync/shared-config.rb +++ b/.github/actions/sync/shared-config.rb @@ -229,6 +229,12 @@ def safe_system(*args) tracked_files = out.split("\0") +out, err, status = Open3.capture3("git", "-C", target_directory, "ls-files", "-z", "--others", "--exclude-standard") +raise err unless status.success? + +# Local syncs can run on checkouts with uncommitted templates, which shouldn't get a shared one beside them. +working_tree_files = tracked_files + out.split("\0") + swift_manifests = ["Package.swift", "Package.resolved"].freeze licenses_enabled ||= tracked_files.any? { |path| swift_manifests.include?(File.basename(path)) } @@ -468,11 +474,11 @@ def safe_system(*args) # Install templates once and then leave them to the repository, like any template it already has. contents = format(Pathname(template_sources.fetch(path)).read, repository: "Homebrew/#{repository_name}") if path == pull_request_template - own_templates = tracked_files.grep(existing_pull_request_template) - [path] + own_templates = working_tree_files.grep(existing_pull_request_template) - [path] # GitHub prefers `.github/`, so remove an unedited synced template once the repository adds its own elsewhere. FileUtils.rm_f target_path if own_templates.any? && target_path.file? && target_path.read == contents next if own_templates.any? - elsif tracked_files.any?(existing_issue_template) + elsif working_tree_files.any?(existing_issue_template) next end next if target_path.exist? diff --git a/.github/actions/sync/shared-config_spec.rb b/.github/actions/sync/shared-config_spec.rb index 623f9834..74c5a515 100644 --- a/.github/actions/sync/shared-config_spec.rb +++ b/.github/actions/sync/shared-config_spec.rb @@ -361,11 +361,18 @@ def synced_contents(path) expect_idempotent_sync end - it "does not overwrite an uncommitted template" do - commit_fixture - write pull_request_template, own_template - sync_target - expect((target/pull_request_template).read).to eq(own_template) + { + ".github/PULL_REQUEST_TEMPLATE.md" => /pull_request_template/i, + "PULL_REQUEST_TEMPLATE.md" => /pull_request_template/i, + ".github/ISSUE_TEMPLATE/feature.yml" => /issue_template/i, + }.each do |path, pattern| + it "keeps an uncommitted #{path} without adding a shared template beside it" do + commit_fixture + write path, own_template + sync_target + expect((target/path).read).to eq(own_template) + expect(tracked_templates(pattern)).to eq([path]) + end end %w[ From a2762191313894b63f6a6be170bb99f6ecb0c709 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Tue, 3 Nov 2026 12:36:12 +0100 Subject: [PATCH 5/5] sync: ignore deleted templates - Only count template paths that still exist, so local syncs treat templates deleted but not yet committed as gone. --- .github/actions/sync/shared-config.rb | 4 ++-- .github/actions/sync/shared-config_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/actions/sync/shared-config.rb b/.github/actions/sync/shared-config.rb index aab2159c..bc78f1d9 100755 --- a/.github/actions/sync/shared-config.rb +++ b/.github/actions/sync/shared-config.rb @@ -232,8 +232,8 @@ def safe_system(*args) out, err, status = Open3.capture3("git", "-C", target_directory, "ls-files", "-z", "--others", "--exclude-standard") raise err unless status.success? -# Local syncs can run on checkouts with uncommitted templates, which shouldn't get a shared one beside them. -working_tree_files = tracked_files + out.split("\0") +# Local syncs can run on checkouts with uncommitted template changes, so look at the files that are actually there. +working_tree_files = (tracked_files + out.split("\0")).select { |path| (target_directory_path/path).exist? } swift_manifests = ["Package.swift", "Package.resolved"].freeze licenses_enabled ||= tracked_files.any? { |path| swift_manifests.include?(File.basename(path)) } diff --git a/.github/actions/sync/shared-config_spec.rb b/.github/actions/sync/shared-config_spec.rb index 74c5a515..4bbcf434 100644 --- a/.github/actions/sync/shared-config_spec.rb +++ b/.github/actions/sync/shared-config_spec.rb @@ -361,6 +361,26 @@ def synced_contents(path) expect_idempotent_sync end + it "keeps the pull request template when the repository's own one is deleted but not committed" do + commit_fixture + sync_target + write "PULL_REQUEST_TEMPLATE.md", own_template + commit_fixture + FileUtils.rm target/"PULL_REQUEST_TEMPLATE.md" + sync_target + expect((target/pull_request_template).read).to eq(expected_templates.fetch(pull_request_template)) + end + + it "installs issue templates when the last one is deleted but not committed" do + write ".github/ISSUE_TEMPLATE/feature.yml", own_template + commit_fixture + FileUtils.rm target/".github/ISSUE_TEMPLATE/feature.yml" + sync_target + issue_templates.each do |issue_template| + expect((target/issue_template).read).to eq(expected_templates.fetch(issue_template)) + end + end + { ".github/PULL_REQUEST_TEMPLATE.md" => /pull_request_template/i, "PULL_REQUEST_TEMPLATE.md" => /pull_request_template/i,