From 1a5092af9beeeacd7e58b76d7b46ed4a7e2b6859 Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 27 Feb 2025 11:40:51 -0800 Subject: [PATCH 01/16] chore: release v3.0.0 Signed-off-by: James Couball --- CHANGELOG.md | 12 ++++++++++++ lib/git/version.rb | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92821c76..59dae355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ # Change Log +## v3.0.0 (2025-02-27) + +[Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.3.3..v3.0.0) + +Changes since v2.3.3: + +* 534fcf5 chore: use ProcessExecuter.run instead of the implementing it in this gem +* 629f3b6 feat: update dependenices +* 501d135 feat: add support for Ruby 3.4 and drop support for Ruby 3.0 +* 38c0eb5 build: update the CI build to use current versions to TruffleRuby and JRuby +* d3f3a9d chore: add frozen_string_literal: true magic comment + ## v2.3.3 (2024-12-04) [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.3.2..v2.3.3) diff --git a/lib/git/version.rb b/lib/git/version.rb index b0ad1154..81e4d967 100644 --- a/lib/git/version.rb +++ b/lib/git/version.rb @@ -3,5 +3,5 @@ module Git # The current gem version # @return [String] the current gem version. - VERSION='2.3.3' + VERSION='3.0.0' end From b060e479b7eb80269c76d93b71453630b150a43d Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 27 Feb 2025 17:09:36 -0800 Subject: [PATCH 02/16] test: verify that command line envionment variables are set as expected --- lib/git/lib.rb | 2 +- tests/test_helper.rb | 10 +++- .../units/test_command_line_env_overrides.rb | 48 +++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/units/test_command_line_env_overrides.rb diff --git a/lib/git/lib.rb b/lib/git/lib.rb index a2ea79b2..0682a070 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -1547,7 +1547,7 @@ def env_overrides 'GIT_DIR' => @git_dir, 'GIT_WORK_TREE' => @git_work_dir, 'GIT_INDEX_FILE' => @git_index_file, - 'GIT_SSH' => Git::Base.config.git_ssh + 'GIT_SSH' => Git::Base.config.git_ssh, } end diff --git a/tests/test_helper.rb b/tests/test_helper.rb index c0a95174..067fa633 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -131,7 +131,7 @@ def append_file(name, contents) # # @return [void] # - def assert_command_line_eq(expected_command_line, method: :command, mocked_output: nil) + def assert_command_line_eq(expected_command_line, method: :command, mocked_output: nil, include_env: false) actual_command_line = nil command_output = '' @@ -140,7 +140,11 @@ def assert_command_line_eq(expected_command_line, method: :command, mocked_outpu git = Git.init('test_project') git.lib.define_singleton_method(method) do |*cmd, **opts, &block| - actual_command_line = [*cmd, opts] + if include_env + actual_command_line = [env_overrides, *cmd, opts] + else + actual_command_line = [*cmd, opts] + end mocked_output end @@ -149,6 +153,8 @@ def assert_command_line_eq(expected_command_line, method: :command, mocked_outpu end end + expected_command_line = expected_command_line.call if expected_command_line.is_a?(Proc) + assert_equal(expected_command_line, actual_command_line) command_output diff --git a/tests/units/test_command_line_env_overrides.rb b/tests/units/test_command_line_env_overrides.rb new file mode 100644 index 00000000..37f14bfa --- /dev/null +++ b/tests/units/test_command_line_env_overrides.rb @@ -0,0 +1,48 @@ + +# frozen_string_literal: true + +require 'test_helper' + +class TestCommandLineEnvOverrides < Test::Unit::TestCase + test 'it should set the expected environment variables' do + expected_command_line = nil + expected_command_line_proc = ->{ expected_command_line } + assert_command_line_eq(expected_command_line_proc, include_env: true) do |git| + expected_env = { + 'GIT_DIR' => git.lib.git_dir, + 'GIT_INDEX_FILE' => git.lib.git_index_file, + 'GIT_SSH' => nil, + 'GIT_WORK_TREE' => git.lib.git_work_dir + } + expected_command_line = [expected_env, 'checkout', {}] + + git.checkout + end + end + + test 'it should set the GIT_SSH environment variable from Git::Base.config.git_ssh' do + expected_command_line = nil + expected_command_line_proc = ->{ expected_command_line } + + saved_git_ssh = Git::Base.config.git_ssh + begin + Git::Base.config.git_ssh = 'ssh -i /path/to/key' + + assert_command_line_eq(expected_command_line_proc, include_env: true) do |git| + # Set the expected command line + + expected_env = { + 'GIT_DIR' => git.lib.git_dir, + 'GIT_INDEX_FILE' => git.lib.git_index_file, + 'GIT_SSH' => 'ssh -i /path/to/key', + 'GIT_WORK_TREE' => git.lib.git_work_dir + } + + expected_command_line = [expected_env, 'checkout', {}] + git.checkout + end + ensure + Git::Base.config.git_ssh = saved_git_ssh + end + end +end From f407b92d14a5deb85dd8327f61d919c1892ef4d6 Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 27 Feb 2025 17:18:16 -0800 Subject: [PATCH 03/16] feat: set the locale to en_US.UTF-8 for git commands --- lib/git/lib.rb | 1 + tests/units/test_command_line_env_overrides.rb | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 0682a070..7d9cbc3c 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -1548,6 +1548,7 @@ def env_overrides 'GIT_WORK_TREE' => @git_work_dir, 'GIT_INDEX_FILE' => @git_index_file, 'GIT_SSH' => Git::Base.config.git_ssh, + 'LC_ALL' => 'en_US.UTF-8' } end diff --git a/tests/units/test_command_line_env_overrides.rb b/tests/units/test_command_line_env_overrides.rb index 37f14bfa..a89da4d4 100644 --- a/tests/units/test_command_line_env_overrides.rb +++ b/tests/units/test_command_line_env_overrides.rb @@ -12,7 +12,8 @@ class TestCommandLineEnvOverrides < Test::Unit::TestCase 'GIT_DIR' => git.lib.git_dir, 'GIT_INDEX_FILE' => git.lib.git_index_file, 'GIT_SSH' => nil, - 'GIT_WORK_TREE' => git.lib.git_work_dir + 'GIT_WORK_TREE' => git.lib.git_work_dir, + 'LC_ALL' => 'en_US.UTF-8' } expected_command_line = [expected_env, 'checkout', {}] @@ -29,16 +30,15 @@ class TestCommandLineEnvOverrides < Test::Unit::TestCase Git::Base.config.git_ssh = 'ssh -i /path/to/key' assert_command_line_eq(expected_command_line_proc, include_env: true) do |git| - # Set the expected command line - expected_env = { 'GIT_DIR' => git.lib.git_dir, 'GIT_INDEX_FILE' => git.lib.git_index_file, 'GIT_SSH' => 'ssh -i /path/to/key', - 'GIT_WORK_TREE' => git.lib.git_work_dir + 'GIT_WORK_TREE' => git.lib.git_work_dir, + 'LC_ALL' => 'en_US.UTF-8' } - expected_command_line = [expected_env, 'checkout', {}] + git.checkout end ensure From 9d441465f4f484cf965e2c28eafa6b5259424b0c Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 27 Feb 2025 17:33:55 -0800 Subject: [PATCH 04/16] chore: update the development dependency on the minitar gem --- git.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.gemspec b/git.gemspec index a81ba60b..f8c49bdc 100644 --- a/git.gemspec +++ b/git.gemspec @@ -33,7 +33,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rchardet', '~> 1.9' s.add_development_dependency 'create_github_release', '~> 2.1' - s.add_development_dependency 'minitar', '~> 0.12' + s.add_development_dependency 'minitar', '~> 1.0' s.add_development_dependency 'mocha', '~> 2.7' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'test-unit', '~> 3.6' From b47eedc15923c39e7ffe72510fda4f245debe5ef Mon Sep 17 00:00:00 2001 From: Michal Papis Date: Wed, 14 May 2025 23:14:37 +0200 Subject: [PATCH 05/16] Improved error message of rev_parse As described by git-rev-parse: Many Git porcelainish commands take mixture of flags (i.e. parameters that begin with a dash -) and parameters meant for the underlying git rev-list command they use internally and flags and parameters for the other commands they use downstream of git rev-list. This command is used to distinguish between them. Using the `--` to separate revisions from paths is at the core of git. I do not think this behavior will ever change. The message without the extra parameters: fatal: ambiguous argument 'v3': unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git [...] -- [...]' The message with new parameters: fatal: bad revision 'NOTFOUND' I think it's way more descriptive. --- lib/git/lib.rb | 2 +- tests/units/test_lib.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 7d9cbc3c..b62d69c1 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -333,7 +333,7 @@ def full_log_commits(opts = {}) def rev_parse(revision) assert_args_are_not_options('rev', revision) - command('rev-parse', revision) + command('rev-parse', '--revs-only', '--end-of-options', revision, '--') end # For backwards compatibility with the old method name diff --git a/tests/units/test_lib.rb b/tests/units/test_lib.rb index fb319be8..af613d1f 100644 --- a/tests/units/test_lib.rb +++ b/tests/units/test_lib.rb @@ -199,7 +199,7 @@ def test_rev_parse_with_bad_revision end def test_rev_parse_with_unknown_revision - assert_raise(Git::FailedError) do + assert_raise_with_message(Git::FailedError, /exit 128, stderr: "fatal: bad revision 'NOTFOUND'"/) do @lib.rev_parse('NOTFOUND') end end From 31374263eafea4e23352494ef4f6bea3ce62c1b5 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 15:01:46 -0700 Subject: [PATCH 06/16] chore: release v3.0.1 Signed-off-by: James Couball --- CHANGELOG.md | 12 ++++++++++++ lib/git/version.rb | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59dae355..b31fed33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ # Change Log +## v3.0.1 (2025-05-14) + +[Full Changelog](https://github.com/ruby-git/ruby-git/compare/v3.0.0..v3.0.1) + +Changes since v3.0.0: + +* b47eedc Improved error message of rev_parse +* 9d44146 chore: update the development dependency on the minitar gem +* f407b92 feat: set the locale to en_US.UTF-8 for git commands +* b060e47 test: verify that command line envionment variables are set as expected +* 1a5092a chore: release v3.0.0 + ## v3.0.0 (2025-02-27) [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.3.3..v3.0.0) diff --git a/lib/git/version.rb b/lib/git/version.rb index 81e4d967..eb507c85 100644 --- a/lib/git/version.rb +++ b/lib/git/version.rb @@ -3,5 +3,5 @@ module Git # The current gem version # @return [String] the current gem version. - VERSION='3.0.0' + VERSION='3.0.1' end From 7ebe0f8626ecb2f0da023b903b82f7332d8afaf6 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 17:46:38 -0700 Subject: [PATCH 07/16] chore: enforce conventional commit messages with husky and commitlint - Add steps to bin/setup to install husky and the commitlint npm packages - Configure husky to run commitlint via the commit-msg hook - Add commitlint configuration based on my specific preferences - Add npm specific files (node_modules/, package-lock.json) to .gitignore --- .commitlintrc.yml | 38 +++++++++++++++++++++++++ .gitignore | 2 ++ .husky/commit-msg | 1 + CONTRIBUTING.md | 72 +++++++++++++++++++++++++++-------------------- bin/setup | 7 ++++- package.json | 10 +++++++ 6 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 .commitlintrc.yml create mode 100644 .husky/commit-msg create mode 100644 package.json diff --git a/.commitlintrc.yml b/.commitlintrc.yml new file mode 100644 index 00000000..3e08fa81 --- /dev/null +++ b/.commitlintrc.yml @@ -0,0 +1,38 @@ +--- +extends: '@commitlint/config-conventional' + +rules: + # See: https://commitlint.js.org/reference/rules.html + # + # Rules are made up by a name and a configuration array. The configuration + # array contains: + # + # * Severity [0..2]: 0 disable rule, 1 warning if violated, or 2 error if + # violated + # * Applicability [always|never]: never inverts the rule + # * Value: value to use for this rule (if applicable) + # + # Run `npx commitlint --print-config` to see the current setting for all + # rules. + # + header-max-length: [2, always, 100] # Header can not exceed 100 chars + + type-case: [2, always, lower-case] # Type must be lower case + type-empty: [2, never] # Type must not be empty + + # Supported conventional commit types + type-enum: [2, always, [build, ci, chore, docs, feat, fix, perf, refactor, revert, style, test]] + + scope-case: [2, always, lower-case] # Scope must be lower case + + # Error if subject is one of these cases (encourages lower-case) + subject-case: [2, never, [sentence-case, start-case, pascal-case, upper-case]] + subject-empty: [2, never] # Subject must not be empty + subject-full-stop: [2, never, "."] # Subject must not end with a period + + body-leading-blank: [2, always] # Body must have a blank line before it + body-max-line-length: [2, always, 100] # Body lines can not exceed 100 chars + + footer-leading-blank: [2, always] # Footer must have a blank line before it + footer-max-line-length: [2, always, 100] # Footer lines can not exceed 100 chars + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 611ed70c..13dcea11 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ doc pkg rdoc Gemfile.lock +node_modules +package-lock.json \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 00000000..70bd3dd2 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no-install commitlint --edit "$1" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 10793a4a..9a7a4e35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,28 +5,28 @@ # Contributing to the git gem -* [Summary](#summary) -* [How to contribute](#how-to-contribute) -* [How to report an issue or request a feature](#how-to-report-an-issue-or-request-a-feature) -* [How to submit a code or documentation change](#how-to-submit-a-code-or-documentation-change) - * [Commit your changes to a fork of `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git) - * [Create a pull request](#create-a-pull-request) - * [Get your pull request reviewed](#get-your-pull-request-reviewed) -* [Design philosophy](#design-philosophy) - * [Direct mapping to git commands](#direct-mapping-to-git-commands) - * [Parameter naming](#parameter-naming) - * [Output processing](#output-processing) -* [Coding standards](#coding-standards) - * [1 PR = 1 Commit](#1-pr--1-commit) - * [Unit tests](#unit-tests) - * [Continuous integration](#continuous-integration) - * [Documentation](#documentation) -* [Building a specific version of the Git command-line](#building-a-specific-version-of-the-git-command-line) - * [Install pre-requisites](#install-pre-requisites) - * [Obtain Git source code](#obtain-git-source-code) - * [Build git](#build-git) - * [Use the new Git version](#use-the-new-git-version) -* [Licensing](#licensing) +- [Summary](#summary) +- [How to contribute](#how-to-contribute) +- [How to report an issue or request a feature](#how-to-report-an-issue-or-request-a-feature) +- [How to submit a code or documentation change](#how-to-submit-a-code-or-documentation-change) + - [Commit your changes to a fork of `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git) + - [Create a pull request](#create-a-pull-request) + - [Get your pull request reviewed](#get-your-pull-request-reviewed) +- [Design philosophy](#design-philosophy) + - [Direct mapping to git commands](#direct-mapping-to-git-commands) + - [Parameter naming](#parameter-naming) + - [Output processing](#output-processing) +- [Coding standards](#coding-standards) + - [Commit message guidelines](#commit-message-guidelines) + - [Unit tests](#unit-tests) + - [Continuous integration](#continuous-integration) + - [Documentation](#documentation) +- [Building a specific version of the Git command-line](#building-a-specific-version-of-the-git-command-line) + - [Install pre-requisites](#install-pre-requisites) + - [Obtain Git source code](#obtain-git-source-code) + - [Build git](#build-git) + - [Use the new Git version](#use-the-new-git-version) +- [Licensing](#licensing) ## Summary @@ -153,18 +153,30 @@ behavior. To ensure high-quality contributions, all pull requests must meet the following requirements: -### 1 PR = 1 Commit +### Commit message guidelines -* All commits for a PR must be squashed into a single commit. -* To avoid an extra merge commit, the PR must be able to be merged as [a fast-forward - merge](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging). -* The easiest way to ensure a fast-forward merge is to rebase your local branch to - the `ruby-git` master branch. +All commit messages must follow the [Conventional Commits +standard](https://www.conventionalcommits.org/en/v1.0.0/). This helps us maintain a +clear and structured commit history, automate versioning, and generate changelogs +effectively. + +To ensure compliance, this project includes: + +- A git commit-msg hook that validates your commit messages before they are accepted. + + To activate the hook, you must have node installed and run `bin/setup` or + `npm install`. + +- A GitHub Actions workflow that will enforce the Conventional Commit standard as + part of the continuous integration pipeline. + + Any commit message that does not conform to the Conventional Commits standard will + cause the workflow to fail and not allow the PR to be merged. ### Unit tests -* All changes must be accompanied by new or modified unit tests. -* The entire test suite must pass when `bundle exec rake default` is run from the +- All changes must be accompanied by new or modified unit tests. +- The entire test suite must pass when `bundle exec rake default` is run from the project's local working copy. While working on specific features, you can run individual test files or a group of diff --git a/bin/setup b/bin/setup index dce67d86..f16ff654 100755 --- a/bin/setup +++ b/bin/setup @@ -5,4 +5,9 @@ set -vx bundle install -# Do any other automated setup that you need to do here +if [ -x "$(command -v npm)" ]; then + npm install +else + echo "npm is not installed" + echo "Install npm then re-run this script to enable the conventional commit git hook." +fi diff --git a/package.json b/package.json new file mode 100644 index 00000000..2924004f --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "devDependencies": { + "@commitlint/cli": "^19.8.0", + "@commitlint/config-conventional": "^19.8.0", + "husky": "^9.1.7" + }, + "scripts": { + "prepare": "husky" + } +} From 1da4c44620a3264d4e837befd3f40416c5d8f1d8 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 18:01:49 -0700 Subject: [PATCH 08/16] chore: enforce conventional commit messages with a GitHub action - Add a GitHub Actions workflow to enforce conventional commits - Add commitlint configuration based on my specific preferences --- .../enforce_conventional_commits.yml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/enforce_conventional_commits.yml diff --git a/.github/workflows/enforce_conventional_commits.yml b/.github/workflows/enforce_conventional_commits.yml new file mode 100644 index 00000000..8aaa93f8 --- /dev/null +++ b/.github/workflows/enforce_conventional_commits.yml @@ -0,0 +1,28 @@ +--- +name: Conventional Commits + +permissions: + contents: read + +on: + pull_request: + branches: + - master + +jobs: + commit-lint: + name: Verify Conventional Commits + + # Skip this job if this is a release PR + if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: { fetch-depth: 0 } + + - name: Check Commit Messages + uses: wagoid/commitlint-github-action@v6 + with: { configFile: .commitlintrc.yml } From 06480e65e2441348230ef10e05cc1c563d0e7ea8 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 20:59:31 -0700 Subject: [PATCH 09/16] build: automate continuous delivery workflow Use googleapis/release-please-action and rubygems/release-gem actions to automate releasing and publishing new gem versions to rubygems. --- .github/workflows/release.yml | 52 +++++++++++++++++++++ .release-please-manifest.json | 3 ++ .yardopts | 1 - RELEASING.md | 85 ----------------------------------- Rakefile | 7 +++ release-please-config.json | 36 +++++++++++++++ 6 files changed, 98 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .release-please-manifest.json delete mode 100644 RELEASING.md create mode 100644 release-please-config.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..607f16ce --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +--- +name: Release Gem + +description: | + This workflow creates a new release on GitHub and publishes the gem to + RubyGems.org. + + The workflow uses the `googleapis/release-please-action` to handle the + release creation process and the `rubygems/release-gem` action to publish + the gem to rubygems.org + +on: + push: + branches: ["main"] + + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + + environment: + name: RubyGems + url: https://rubygems.org/gems/git + + permissions: + contents: write + pull-requests: write + id-token: write + + steps: + - name: Checkout project + uses: actions/checkout@v4 + + - name: Create release + uses: googleapis/release-please-action@v4 + id: release + with: + token: ${{ secrets.AUTO_RELEASE_TOKEN }} + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + - name: Setup ruby + uses: ruby/setup-ruby@v1 + if: ${{ steps.release.outputs.release_created }} + with: + bundler-cache: true + ruby-version: ruby + + - name: Push to RubyGems.org + uses: rubygems/release-gem@v1 + if: ${{ steps.release.outputs.release_created }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..d6f54056 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "3.0.1" +} diff --git a/.yardopts b/.yardopts index ce1aff3c..105b79a9 100644 --- a/.yardopts +++ b/.yardopts @@ -7,5 +7,4 @@ README.md CHANGELOG.md CONTRIBUTING.md -RELEASING.md MAINTAINERS.md diff --git a/RELEASING.md b/RELEASING.md deleted file mode 100644 index ead6293a..00000000 --- a/RELEASING.md +++ /dev/null @@ -1,85 +0,0 @@ - - -# How to release a new git.gem - -Releasing a new version of the `git` gem requires these steps: - -* [Install Prerequisites](#install-prerequisites) -* [Determine the SemVer release type](#determine-the-semver-release-type) -* [Create the release](#create-the-release) -* [Review the CHANGELOG and release PR](#review-the-changelog-and-release-pr) -* [Manually merge the release PR](#manually-merge-the-release-pr) -* [Publish the git gem to RubyGems.org](#publish-the-git-gem-to-rubygemsorg) - -## Install Prerequisites - -The following tools need to be installed in order to create the release: - -* [create_githhub_release](https://github.com/main-branch/create_github_release) is used to create the release -* [git](https://git-scm.com) is used by `create-github-release` to interact with the local and remote repositories -* [gh](https://cli.github.com) is used by `create-github-release` to create the release and PR in GitHub - -On a Mac, these tools can be installed using [gem](https://guides.rubygems.org/rubygems-basics/) and [brew](https://brew.sh): - -```shell -$ gem install create_github_release -... -$ brew install git -... -$ brew install gh -... -$ -``` - -## Determine the SemVer release type - -Determine the SemVer version increment that should be applied for the new release: - -* `major`: when the release includes incompatible API or functional changes. -* `minor`: when the release adds functionality in a backward-compatible manner -* `patch`: when the release includes small user-facing changes that are - backward-compatible and do not introduce new functionality. - -## Create the release - -Create the release using the `create-github-release` command. If the release type -is `major`, the command is: - -```shell -create-github-release major -``` - -Follow the directions given by the `create-github-release` command to finish the -release. Where the instructions given by the command differ than the instructions -below, follow the instructions given by the command. - -## Review the CHANGELOG and release PR - -The `create-github-release` command will output a link to the CHANGELOG and the PR -it created for the release. Review the CHANGELOG and have someone review and approve -the release PR. - -## Manually merge the release PR - -It is important to manually merge the PR so a separate merge commit can be avoided. -Use the commands output by the `create-github-release` which will looks like this -if you are creating a 2.0.0 release: - -```shell -git checkout master -git merge --ff-only release-v2.0.0 -git push -``` - -This will automatically close the release PR. - -## Publish the git gem to RubyGems.org - -Finally, publish the git gem to RubyGems.org using the following command: - -```shell -rake release:rubygem_push -``` diff --git a/Rakefile b/Rakefile index e2d8ef2a..72b93352 100644 --- a/Rakefile +++ b/Rakefile @@ -58,3 +58,10 @@ task :'test:gem' => :install do puts 'Gem Test Succeeded' end + +# Make it so that calling `rake release` just calls `rake release:rubygem_push` to +# avoid creating and pushing a new tag. + +Rake::Task['release'].clear +desc 'Customized release task to avoid creating a new tag' +task release: 'release:rubygem_push' diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..b0c93860 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,36 @@ +{ + "bootstrap-sha": "31374263eafea4e23352494ef4f6bea3ce62c1b5", + "packages": { + ".": { + "release-type": "ruby", + "package-name": "git", + "changelog-path": "CHANGELOG.md", + "version-file": "lib/git/version.rb", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "draft": false, + "prerelease": false, + "include-component-in-tag": false, + "pull-request-title-pattern": "chore: release v${version}", + "changelog-sections": [ + { "type": "feat", "section": "Features", "hidden": false }, + { "type": "fix", "section": "Bug Fixes", "hidden": false }, + { "type": "build", "section": "Other Changes", "hidden": false }, + { "type": "chore", "section": "Other Changes", "hidden": false }, + { "type": "ci", "section": "Other Changes", "hidden": false }, + { "type": "docs", "section": "Other Changes", "hidden": false }, + { "type": "perf", "section": "Other Changes", "hidden": false }, + { "type": "refactor", "section": "Other Changes", "hidden": false }, + { "type": "revert", "section": "Other Changes", "hidden": false }, + { "type": "style", "section": "Other Changes", "hidden": false }, + { "type": "test", "section": "Other Changes", "hidden": false } + ] + } + }, + "plugins": [ + { + "type": "sentence-case" + } + ], + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} From c8611f1e68e73825fd16bd475752a40b0088d4ae Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 21:09:07 -0700 Subject: [PATCH 10/16] fix: trigger the release workflow on a change to 'master' insetad of 'main' --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 607f16ce..eaea43f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ description: | on: push: - branches: ["main"] + branches: ["master"] workflow_dispatch: From 880d38e4d36e598b47c7d487d49b56c6541ebf66 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 14 May 2025 21:31:07 -0700 Subject: [PATCH 11/16] chore: release v3.0.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ lib/git/version.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d6f54056..e28eff59 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.0.1" + ".": "3.0.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index b31fed33..0fec2948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ # Change Log +## [3.0.2](https://github.com/ruby-git/ruby-git/compare/v3.0.1...v3.0.2) (2025-05-15) + + +### Bug Fixes + +* Trigger the release workflow on a change to 'master' insetad of 'main' ([c8611f1](https://github.com/ruby-git/ruby-git/commit/c8611f1e68e73825fd16bd475752a40b0088d4ae)) + + +### Other Changes + +* Automate continuous delivery workflow ([06480e6](https://github.com/ruby-git/ruby-git/commit/06480e65e2441348230ef10e05cc1c563d0e7ea8)) +* Enforce conventional commit messages with a GitHub action ([1da4c44](https://github.com/ruby-git/ruby-git/commit/1da4c44620a3264d4e837befd3f40416c5d8f1d8)) +* Enforce conventional commit messages with husky and commitlint ([7ebe0f8](https://github.com/ruby-git/ruby-git/commit/7ebe0f8626ecb2f0da023b903b82f7332d8afaf6)) + ## v3.0.1 (2025-05-14) [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v3.0.0..v3.0.1) diff --git a/lib/git/version.rb b/lib/git/version.rb index eb507c85..6831d2c1 100644 --- a/lib/git/version.rb +++ b/lib/git/version.rb @@ -3,5 +3,5 @@ module Git # The current gem version # @return [String] the current gem version. - VERSION='3.0.1' + VERSION='3.0.2' end From a832259314aa9c8bdd7719e50d425917df1df831 Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 15 May 2025 09:48:44 -0700 Subject: [PATCH 12/16] docs: announce and document guidelines for using Conventional Commits --- CONTRIBUTING.md | 87 +++++++++++++++++++++++++++++++++++++++++-------- README.md | 64 ++++++++++++++++-------------------- 2 files changed, 102 insertions(+), 49 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a7a4e35..653290f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,8 @@ - [Output processing](#output-processing) - [Coding standards](#coding-standards) - [Commit message guidelines](#commit-message-guidelines) + - [What does this mean for contributors?](#what-does-this-mean-for-contributors) + - [What to know about Conventional Commits](#what-to-know-about-conventional-commits) - [Unit tests](#unit-tests) - [Continuous integration](#continuous-integration) - [Documentation](#documentation) @@ -63,7 +65,8 @@ thoroughly as possible to describe the issue or feature request. There is a three-step process for submitting code or documentation changes: 1. [Commit your changes to a fork of - `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git) + `ruby-git`](#commit-your-changes-to-a-fork-of-ruby-git) using [Conventional + Commits](#commit-message-guidelines) 2. [Create a pull request](#create-a-pull-request) 3. [Get your pull request reviewed](#get-your-pull-request-reviewed) @@ -155,23 +158,81 @@ requirements: ### Commit message guidelines -All commit messages must follow the [Conventional Commits -standard](https://www.conventionalcommits.org/en/v1.0.0/). This helps us maintain a -clear and structured commit history, automate versioning, and generate changelogs -effectively. +To enhance our development workflow, enable automated changelog generation, and pave +the way for Continuous Delivery, the `ruby-git` project has adopted the [Conventional +Commits standard](https://www.conventionalcommits.org/en/v1.0.0/) for all commit +messages. -To ensure compliance, this project includes: +This structured approach to commit messages allows us to: -- A git commit-msg hook that validates your commit messages before they are accepted. +- **Automate versioning and releases:** Tools can now automatically determine the + semantic version bump (patch, minor, major) based on the types of commits merged. +- **Generate accurate changelogs:** We can automatically create and update a + `CHANGELOG.md` file, providing a clear history of changes for users and + contributors. +- **Improve commit history readability:** A standardized format makes it easier for + everyone to understand the nature of changes at a glance. - To activate the hook, you must have node installed and run `bin/setup` or - `npm install`. +#### What does this mean for contributors? -- A GitHub Actions workflow that will enforce the Conventional Commit standard as - part of the continuous integration pipeline. +Going forward, all commits to this repository **MUST** adhere to the [Conventional +Commits standard](https://www.conventionalcommits.org/en/v1.0.0/). Commits not +adhering to this standard will cause the CI build to fail. PRs will not be merged if +they include non-conventional commits. - Any commit message that does not conform to the Conventional Commits standard will - cause the workflow to fail and not allow the PR to be merged. +A git pre-commit hook may be installed to validate your conventional commit messages +before pushing them to GitHub by running `bin/setup` in the project root. + +#### What to know about Conventional Commits + +The simplist conventional commit is in the form `type: description` where `type` +indicates the type of change and `description` is your usual commit message (with +some limitations). + +- Types include: `feat`, `fix`, `docs`, `test`, `refactor`, and `chore`. See the full + list of types supported in [.commitlintrc.yml](.commitlintrc.yml). +- The description must (1) not start with an upper case letter, (2) be no more than + 100 characters, and (3) not end with punctuation. + +Examples of valid commits: + +- `feat: add the --merges option to Git::Lib.log` +- `fix: exception thrown by Git::Lib.log when repo has no commits` +- `docs: add conventional commit announcement to README.md` + +Commits that include breaking changes must include an exclaimation mark before the +colon: + +- `feat!: removed Git::Base.commit_force` + +The commit messages will drive how the version is incremented for each release: + +- a release containing a **breaking change** will do a **major** version increment +- a release containing a **new feature** will do a **minor** increment +- a release containing **neither a breaking change nor a new feature** will do a + **patch** version increment + +The full conventional commit format is: + +```text +[optional scope][!]: + +[optional body] + +[optional footer(s)] +``` + +- `optional body` may include multiple lines of descriptive text limited to 100 chars + each +- `optional footers` only uses `BREAKING CHANGE: ` where description + should describe the nature of the backward incompatibility. + +Use of the `BREAKING CHANGE:` footer flags a backward incompatible change even if it +is not flagged with an exclaimation mark after the `type`. Other footers are allowed +by not acted upon. + +See [the Conventional Commits +specification](https://www.conventionalcommits.org/en/v1.0.0/) for more details. ### Unit tests diff --git a/README.md b/README.md index c3f788ca..74e6ad4c 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,34 @@ [![Documentation](https://img.shields.io/badge/Documentation-Latest-green)](https://rubydoc.info/gems/git/) [![Change Log](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://rubydoc.info/gems/git/file/CHANGELOG.md) [![Build Status](https://github.com/ruby-git/ruby-git/workflows/CI/badge.svg?branch=master)](https://github.com/ruby-git/ruby-git/actions?query=workflow%3ACI) -[![Code Climate](https://codeclimate.com/github/ruby-git/ruby-git.png)](https://codeclimate.com/github/ruby-git/ruby-git) - -* [Summary](#summary) -* [v2.x Release](#v2x-release) -* [Install](#install) -* [Major Objects](#major-objects) -* [Errors Raised By This Gem](#errors-raised-by-this-gem) -* [Specifying And Handling Timeouts](#specifying-and-handling-timeouts) -* [Examples](#examples) -* [Ruby version support policy](#ruby-version-support-policy) -* [License](#license) +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) + +- [📢 We've Switched to Conventional Commits 📢](#-weve-switched-to-conventional-commits-) +- [Summary](#summary) +- [Install](#install) +- [Major Objects](#major-objects) +- [Errors Raised By This Gem](#errors-raised-by-this-gem) +- [Specifying And Handling Timeouts](#specifying-and-handling-timeouts) +- [Examples](#examples) +- [Ruby version support policy](#ruby-version-support-policy) +- [License](#license) + +## 📢 We've Switched to Conventional Commits 📢 + +To enhance our development workflow, enable automated changelog generation, and pave +the way for Continuous Delivery, the `ruby-git` project has adopted the [Conventional +Commits standard](https://www.conventionalcommits.org/en/v1.0.0/) for all commit +messages. + +Going forward, all commits to this repository **MUST** adhere to the Conventional +Commits standard. Commits not adhering to this standard will cause the CI build to +fail. PRs will not be merged if they include non-conventional commits. + +A git pre-commit hook may be installed to validate your conventional commit messages +before pushing them to GitHub by running `bin/setup` in the project root. + +Read more about this change in the [Commit Message Guidelines section of +CONTRIBUTING.md](CONTRIBUTING.md#commit-message-guidelines) ## Summary @@ -34,31 +51,6 @@ Get started by obtaining a repository object by: Methods that can be called on a repository object are documented in [Git::Base](https://rubydoc.info/gems/git/Git/Base) -## v2.x Release - -git 2.0.0 has recently been released. Please give it a try. - -**If you have problems with the 2.x release, open an issue and use the 1.x version -instead.** We will do our best to fix your issues in a timely fashion. - -**JRuby on Windows is not yet supported by the 2.x release line. Users running JRuby -on Windows should continue to use the 1.x release line.** - -The changes in this major release include: - -* Added a dependency on the activesupport gem to use the deprecation functionality -* Create a policy of supported Ruby versions to support only non-EOL Ruby versions -* Create a policy of supported Git CLI versions (released 2020-12-25) -* Update the required Ruby version to at least 3.0 (released 2020-07-27) -* Update the required Git command line version to at least 2.28 -* Update how CLI commands are called to use the [process_executer](https://github.com/main-branch/process_executer) - gem which is built on top of [Kernel.spawn](https://ruby-doc.org/3.3.0/Kernel.html#method-i-spawn). - See [PR #684](https://github.com/ruby-git/ruby-git/pull/684) for more details - on the motivation for this implementation. - -The `master` branch will be used for `2.x` development. If needed, fixes for `1.x` -version will be done on the `v1` branch. - ## Install Install the gem and add to the application's Gemfile by executing: From df3b07d0f14d79c6c77edc04550c1ad0207c920a Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 15 May 2025 10:48:16 -0700 Subject: [PATCH 13/16] feat: make Git::Log support the git log --merges option --- lib/git/lib.rb | 2 ++ lib/git/log.rb | 9 +++++++-- tests/test_helper.rb | 2 +- tests/units/test_log.rb | 5 +++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/git/lib.rb b/lib/git/lib.rb index b62d69c1..692ceef9 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -294,6 +294,7 @@ def log_commits(opts = {}) # * 'tree' [String] the tree sha # * 'author' [String] the author of the commit and timestamp of when the changes were created # * 'committer' [String] the committer of the commit and timestamp of when the commit was applied + # * 'merges' [Boolean] if truthy, only include merge commits (aka commits with 2 or more parents) # # @raise [ArgumentError] if the revision range (specified with :between or :object) is a string starting with a hyphen # @@ -305,6 +306,7 @@ def full_log_commits(opts = {}) arr_opts << '--pretty=raw' arr_opts << "--skip=#{opts[:skip]}" if opts[:skip] + arr_opts << '--merges' if opts[:merges] arr_opts += log_path_options(opts) diff --git a/lib/git/log.rb b/lib/git/log.rb index dad2c2cd..7ac31622 100644 --- a/lib/git/log.rb +++ b/lib/git/log.rb @@ -133,11 +133,16 @@ def cherry return self end + def merges + dirty_log + @merges = true + return self + end + def to_s self.map { |c| c.to_s }.join("\n") end - # forces git log to run def size @@ -184,7 +189,7 @@ def run_log log = @base.lib.full_log_commits( count: @max_count, all: @all, object: @object, path_limiter: @path, since: @since, author: @author, grep: @grep, skip: @skip, until: @until, between: @between, - cherry: @cherry + cherry: @cherry, merges: @merges ) @commits = log.map { |c| Git::Object::Commit.new(@base, c['sha'], c) } end diff --git a/tests/test_helper.rb b/tests/test_helper.rb index 067fa633..f35a0fcd 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -131,7 +131,7 @@ def append_file(name, contents) # # @return [void] # - def assert_command_line_eq(expected_command_line, method: :command, mocked_output: nil, include_env: false) + def assert_command_line_eq(expected_command_line, method: :command, mocked_output: '', include_env: false) actual_command_line = nil command_output = '' diff --git a/tests/units/test_log.rb b/tests/units/test_log.rb index 1cab1a32..f18fabf2 100644 --- a/tests/units/test_log.rb +++ b/tests/units/test_log.rb @@ -128,4 +128,9 @@ def test_log_cherry l = @git.log.between( 'master', 'cherry').cherry assert_equal( 1, l.size ) end + + def test_log_merges + expected_command_line = ['log', '--max-count=30', '--no-color', '--pretty=raw', '--merges', {:chdir=>nil}] + assert_command_line_eq(expected_command_line) { |git| git.log.merges.size } + end end From f647a18c8a3ae78f49c8cd485db4660aa10a92fc Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 15 May 2025 11:11:16 -0700 Subject: [PATCH 14/16] build: skip continuous integration workflow for release PRs --- .github/workflows/continuous_integration.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 5bc83dd3..e54df88c 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -10,6 +10,11 @@ on: jobs: build: name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }} + + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) + runs-on: ${{ matrix.operating-system }} continue-on-error: ${{ matrix.experimental == 'Yes' }} env: { JAVA_OPTS: -Djdk.io.File.enableADS=true } From 3dab0b34e41393a43437c53a53b96895fd3d2cc5 Mon Sep 17 00:00:00 2001 From: James Couball Date: Thu, 15 May 2025 11:56:02 -0700 Subject: [PATCH 15/16] build: skip the experiemental build workflow if a release commit is pushed to master --- .github/workflows/continuous_integration.yml | 5 ++--- .../workflows/experimental_continuous_integration.yml | 9 ++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index e54df88c..c21e97cd 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -1,16 +1,15 @@ name: CI on: - push: - branches: [master,v1] pull_request: - branches: [master,v1] + branches: [master] workflow_dispatch: jobs: build: name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }} + # Skip this job if triggered by a release PR if: >- github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) diff --git a/.github/workflows/experimental_continuous_integration.yml b/.github/workflows/experimental_continuous_integration.yml index 44dc7889..488ab797 100644 --- a/.github/workflows/experimental_continuous_integration.yml +++ b/.github/workflows/experimental_continuous_integration.yml @@ -2,12 +2,19 @@ name: CI Experimental on: push: - branches: [master,v1] + branches: [master] + workflow_dispatch: jobs: build: name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }} + + # Skip this job if triggered by pushing a release commit + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'push' && !startsWith(github.event.head_commit.message, 'chore: release ')) + runs-on: ${{ matrix.operating-system }} continue-on-error: true env: { JAVA_OPTS: -Djdk.io.File.enableADS=true } From b7da131cd2946af9159d515667df4af33016a6ae Mon Sep 17 00:00:00 2001 From: James Couball Date: Sun, 18 May 2025 14:02:33 -0700 Subject: [PATCH 16/16] chore: release v3.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ lib/git/version.rb | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e28eff59..ada7355e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.0.2" + ".": "3.1.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fec2948..5602c70e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ # Change Log +## [3.1.0](https://github.com/ruby-git/ruby-git/compare/v3.0.2...v3.1.0) (2025-05-18) + + +### Features + +* Make Git::Log support the git log --merges option ([df3b07d](https://github.com/ruby-git/ruby-git/commit/df3b07d0f14d79c6c77edc04550c1ad0207c920a)) + + +### Other Changes + +* Announce and document guidelines for using Conventional Commits ([a832259](https://github.com/ruby-git/ruby-git/commit/a832259314aa9c8bdd7719e50d425917df1df831)) +* Skip continuous integration workflow for release PRs ([f647a18](https://github.com/ruby-git/ruby-git/commit/f647a18c8a3ae78f49c8cd485db4660aa10a92fc)) +* Skip the experiemental build workflow if a release commit is pushed to master ([3dab0b3](https://github.com/ruby-git/ruby-git/commit/3dab0b34e41393a43437c53a53b96895fd3d2cc5)) + ## [3.0.2](https://github.com/ruby-git/ruby-git/compare/v3.0.1...v3.0.2) (2025-05-15) diff --git a/lib/git/version.rb b/lib/git/version.rb index 6831d2c1..0a293cc1 100644 --- a/lib/git/version.rb +++ b/lib/git/version.rb @@ -3,5 +3,5 @@ module Git # The current gem version # @return [String] the current gem version. - VERSION='3.0.2' + VERSION='3.1.0' end pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy