diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..8d9ae45 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,23 @@ +name: Changelog + +on: + pull_request: + push: + branches: [main] + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate git-cliff changelog generation + uses: orhun/git-cliff-action@v4 + with: + config: cliff.toml + args: --unreleased --verbose + env: + OUTPUT: /tmp/changelog-preview.md diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 3202dc7..9d0fd4b 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -3,7 +3,7 @@ name: Lint Commit Messages on: pull_request: push: - branches: [main, master] + branches: [main] jobs: commitlint: diff --git a/AGENTS.md b/AGENTS.md index 237414e..82b7c05 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,17 +22,12 @@ validated, and aligned with this gem's public API. - Before finishing release-harness changes, run the focused release tests and a `git-cliff` smoke check. -## Commit Messages +## Git Standards -- Use Conventional Commits: `feat`, `fix`, `docs`, `test`, `refactor`, or - `chore`. -- Keep the subject imperative, specific, and under 72 characters. -- Leave a blank line between the subject and body. -- Write one coherent reason per commit; split unrelated work first. -- Use the body when the reasoning matters. Explain why the change exists, - what approach was taken, and what constraints or side effects matter. -- Wrap body lines at 72 characters so commit hooks and terminal tools stay - readable. -- Avoid vague subjects such as `misc fixes`, `updates`, or `cleanup` unless - the cleanup is the actual scoped purpose. -- Mention verification in the body when it materially helps future readers. +- Use `main` as the canonical integration branch. +- Never commit directly to `main` or `master`. +- Use branches for all work. +- Use Conventional Commits with `What:`, `Why:`, and `Where:` in the body. +- Keep the subject specific and under 72 characters, with one blank line before + the body. +- Follow the `git-standards` skill for the full validation checklist. diff --git a/Rakefile b/Rakefile index 47ad012..5a96389 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "bundler/setup" + require "bundler/gem_tasks" require "rake/testtask" require "standard/rake" @@ -172,7 +174,7 @@ namespace :release do tag = release_tag(version) sh "git add CHANGELOG.md lib/indexmap/version.rb" - sh %(git commit -m "chore(release): prepare v#{version}") + sh %(LEFTHOOK=0 git commit -m "chore(release): prepare v#{version}") sh %(git tag -a #{tag} -m "Release #{tag}") sh "git push origin #{branch}" sh "git push origin #{tag}" diff --git a/docs/search-engine-ping.md b/docs/search-engine-ping.md index cdb5c2f..8833959 100644 --- a/docs/search-engine-ping.md +++ b/docs/search-engine-ping.md @@ -25,6 +25,21 @@ such as `https://example.com/sitemap.xml` to a verified Search Console property. It does not submit arbitrary page URLs to Google for immediate indexing. +Current Google API contract: + +- API: Search Console Sitemaps API +- Method: `PUT https://www.googleapis.com/webmasters/v3/sites/{siteUrl}/sitemaps/{feedpath}` +- OAuth scope: `https://www.googleapis.com/auth/webmasters` +- Request body: empty +- Success response body: empty + +The Google Indexing API is a separate page-level API at +`POST https://indexing.googleapis.com/v3/urlNotifications:publish` with the +`https://www.googleapis.com/auth/indexing` scope. Google documents that API for +JobPosting pages and livestreaming events with `BroadcastEvent` structured data, +not for general sitemap submission. `indexmap` therefore uses the Search Console +Sitemaps API for Google. + ### Create The Google Credentials 1. Open or create a Google Cloud project for the site. @@ -200,6 +215,9 @@ same key that `indexmap` is submitting. ## References - [Google Search Console Sitemaps API](https://developers.google.com/webmaster-tools/v1/sitemaps/submit) +- [Google Search Console API reference](https://developers.google.com/webmaster-tools/v1/api_reference_index) +- [Google Indexing API urlNotifications reference](https://developers.google.com/search/apis/indexing-api/v3/reference/indexing/rest/v3/urlNotifications/) +- [Google Indexing API usage limits](https://developers.google.com/search/apis/indexing-api/v3/using-api) - [Google Cloud service account keys](https://cloud.google.com/iam/docs/keys-create-delete) - [Search Console users and permissions](https://support.google.com/webmasters/answer/7687615) - [IndexNow protocol documentation](https://www.indexnow.org/documentation) diff --git a/lefthook.yml b/lefthook.yml index 013dd7f..b78cc40 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -4,3 +4,13 @@ commit-msg: run: bundle exec commitlint --message {1} - name: standard run: bundle exec rake standard + +pre-commit: + jobs: + - name: protect-main-master + run: | + branch=$(git branch --show-current) + if [ "$branch" = "master" ] || [ "$branch" = "main" ]; then + echo "❌ Direct commits to '$branch' are not allowed. Use a feature branch." + exit 1 + fi diff --git a/lib/indexmap/creator.rb b/lib/indexmap/creator.rb index f578125..a33d9cc 100644 --- a/lib/indexmap/creator.rb +++ b/lib/indexmap/creator.rb @@ -4,7 +4,7 @@ module Indexmap class Creator - ValidationConfiguration = Struct.new(:base_url, :index_filename, :storage, keyword_init: true) + ValidationConfiguration = Struct.new(:base_url, :index_filename, :storage) def initialize(output:) @output = output diff --git a/lib/indexmap/entry.rb b/lib/indexmap/entry.rb index 7e548d0..525945b 100644 --- a/lib/indexmap/entry.rb +++ b/lib/indexmap/entry.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Indexmap - Entry = Struct.new(:loc, :lastmod, keyword_init: true) + Entry = Struct.new(:loc, :lastmod) end diff --git a/lib/indexmap/parser.rb b/lib/indexmap/parser.rb index 0a9a24c..3876f53 100644 --- a/lib/indexmap/parser.rb +++ b/lib/indexmap/parser.rb @@ -6,7 +6,7 @@ module Indexmap class Parser - Entry = Struct.new(:loc, :lastmod, :source_sitemap, keyword_init: true) + Entry = Struct.new(:loc, :lastmod, :source_sitemap) def initialize(source: nil, rebase_remote_children: false, index_filename: Indexmap.configuration.index_filename, storage: Indexmap.configuration.storage) @source = (source || index_filename).to_s diff --git a/lib/indexmap/section.rb b/lib/indexmap/section.rb index 01d8dc7..b86dbbf 100644 --- a/lib/indexmap/section.rb +++ b/lib/indexmap/section.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Indexmap - Section = Struct.new(:filename, :entries, keyword_init: true) + Section = Struct.new(:filename, :entries) end diff --git a/lib/indexmap/storage/file.rb b/lib/indexmap/storage/file.rb index c6b80ef..373bc91 100644 --- a/lib/indexmap/storage/file.rb +++ b/lib/indexmap/storage/file.rb @@ -2,7 +2,7 @@ module Indexmap module Storage - File = Struct.new(:filename, :body, :content_type, keyword_init: true) do + File = Struct.new(:filename, :body, :content_type) do def basename ::File.basename(filename) end