From e8dd80a845c62613eacf1fc0b464d43aa28a4f8a Mon Sep 17 00:00:00 2001 From: Eddie Li Date: Thu, 24 Sep 2026 12:46:05 +0800 Subject: [PATCH 1/5] =?UTF-8?q?Support=20Ruby=203.3=E2=80=933.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add required_ruby_version >= 2.7 and ostruct dependency - Require ostruct for OpenStruct on Ruby 3.3+/3.4 - Fix Rakefile default; add GHA matrix including 3.3.12/3.4.11 --- .github/workflows/test.yml | 23 ++++++++++++++++ Gemfile.lock | 55 +++++++++++++++++++++----------------- Rakefile | 2 +- lib/xdelivery.rb | 1 + xdelivery-ruby.gemspec | 8 ++++-- 5 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7bae488 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Tests + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: ["3.1.7", "3.3.12", "3.4.11"] + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rake t diff --git a/Gemfile.lock b/Gemfile.lock index 678d8b5..83d3b5d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,52 +2,57 @@ PATH remote: . specs: xdelivery (2.1.0) + ostruct rest-client (>= 2.0) GEM remote: https://rubygems.org/ specs: - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) - crack (0.4.3) - safe_yaml (~> 1.0.0) - domain_name (0.5.20180417) - unf (>= 0.0.5, < 1.0.0) - hashdiff (0.3.7) - http-cookie (1.0.3) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + bigdecimal (4.1.3) + crack (1.0.1) + bigdecimal + rexml + domain_name (0.6.20260907) + hashdiff (1.2.1) + http-accept (1.7.0) + http-cookie (1.1.6) domain_name (~> 0.5) - mime-types (3.2.2) - mime-types-data (~> 3.2015) - mime-types-data (3.2018.0812) - minitest (5.11.3) + logger (1.7.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2026.0922) + minitest (5.27.0) minitest-stub-const (0.6) netrc (0.11.0) - public_suffix (3.0.3) - rake (10.5.0) - rest-client (2.0.2) + ostruct (0.6.3) + public_suffix (7.0.5) + rake (13.4.2) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - safe_yaml (1.0.4) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.5) - webmock (3.4.2) - addressable (>= 2.3.6) + rexml (3.4.4) + webmock (3.26.4) + addressable (>= 2.8.0) crack (>= 0.3.2) - hashdiff + hashdiff (>= 0.4.0, < 2.0.0) PLATFORMS ruby + x86_64-linux DEPENDENCIES - bundler (~> 2.3.14) + bundler (>= 2.3) minitest (~> 5.0) minitest-stub-const - rake (~> 10.0) + rake (>= 10.0) rest-client webmock xdelivery! BUNDLED WITH - 2.3.14 + 2.7.2 diff --git a/Rakefile b/Rakefile index 5175168..c70afb7 100644 --- a/Rakefile +++ b/Rakefile @@ -7,4 +7,4 @@ Rake::TestTask.new(:t) do |t| t.test_files = FileList["spec/**/*_spec.rb"] end -task :default => :test +task :default => :t diff --git a/lib/xdelivery.rb b/lib/xdelivery.rb index 852c845..65328b1 100644 --- a/lib/xdelivery.rb +++ b/lib/xdelivery.rb @@ -1,4 +1,5 @@ require 'json' +require 'ostruct' require 'rest-client' require "xdelivery/version" require "xdelivery/callback" diff --git a/xdelivery-ruby.gemspec b/xdelivery-ruby.gemspec index 87ad65c..489c001 100644 --- a/xdelivery-ruby.gemspec +++ b/xdelivery-ruby.gemspec @@ -14,6 +14,8 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/superlanding/xdelivery-ruby" spec.license = "MIT" + spec.required_ruby_version = ">= 2.7" + # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do @@ -23,11 +25,13 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 2.3.14" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "bundler", ">= 2.3" + spec.add_development_dependency "rake", ">= 10.0" spec.add_development_dependency "minitest", "~> 5.0" spec.add_development_dependency "minitest-stub-const" # lib/xdelivery/api/base.rb 用到 RestClient::Exceptions::Timeout,2.0 才有這個常數 spec.add_dependency "rest-client", ">= 2.0" + # OpenStruct extracted from default gems (needed on Ruby 3.3+/3.4) + spec.add_dependency "ostruct" end From 8e8a53bf1498cc4200a6f64f915f11740a9f3894 Mon Sep 17 00:00:00 2001 From: Eddie Date: Thu, 24 Sep 2026 13:29:07 +0800 Subject: [PATCH 2/5] Fix CI: use Bundler 2.5.23 so Ruby 3.1 matrix job can install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gemfile.lock BUNDLED WITH 2.7.2 requires Ruby >= 3.2, which broke the 3.1.7 CI cell. Pin BUNDLED WITH to 2.5.23 (supports 3.1–3.4). --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 83d3b5d..1c729c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -55,4 +55,4 @@ DEPENDENCIES xdelivery! BUNDLED WITH - 2.7.2 + 2.5.23 From 5840e81bc36140c5cbb52c39a644d90e812aa1ef Mon Sep 17 00:00:00 2001 From: "Eddie Li (ADZ)" Date: Thu, 24 Sep 2026 13:30:43 +0800 Subject: [PATCH 3/5] Fix CI: pin public_suffix < 7 for Ruby 3.1 After Bundler 2.5.23, bundle install still failed on 3.1 because public_suffix 7.0.5 requires Ruby >= 3.2. Pin < 7 and re-lock. --- Gemfile | 5 ++++- Gemfile.lock | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 58e86fb..e032604 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,7 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gemspec gem 'rest-client' -gem 'webmock' \ No newline at end of file +gem 'webmock' + +# public_suffix 7.x needs Ruby >= 3.2; keep 3.1 CI green +gem 'public_suffix', '>= 2.0.2', '< 7' diff --git a/Gemfile.lock b/Gemfile.lock index 1c729c8..f181765 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -28,7 +28,7 @@ GEM minitest-stub-const (0.6) netrc (0.11.0) ostruct (0.6.3) - public_suffix (7.0.5) + public_suffix (6.0.2) rake (13.4.2) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -49,6 +49,7 @@ DEPENDENCIES bundler (>= 2.3) minitest (~> 5.0) minitest-stub-const + public_suffix (>= 2.0.2, < 7) rake (>= 10.0) rest-client webmock From 7ea1aac85a06d3ae7c4ba074cba28557fd167172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=99=E6=B0=B4=E5=BF=8D?= <335298362+sensui624@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:28:42 +0800 Subject: [PATCH 4/5] ci: add RubyGems trusted publishing workflow --- .github/workflows/publish.yml | 120 ++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1792016 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,120 @@ +name: Publish gem + +# master 上 gemspec 版本號尚未發佈到 rubygems.org 時,自動以 Trusted Publishing (OIDC) 發佈。 +# 版本已存在則跳過;不使用任何儲存的 RubyGems API key。 +on: + push: + branches: + - master + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: publish-gem-${{ github.repository }} + cancel-in-progress: false + +env: + # 與 test.yml 矩陣中最新的 Ruby 3.4.11 一致(使用根目錄 Gemfile / Gemfile.lock) + RUBY_VERSION: '3.4.11' + GEM_NAME: xdelivery + GEMSPEC: xdelivery-ruby.gemspec + +jobs: + check: + name: Check version on rubygems.org + runs-on: ubuntu-latest + outputs: + should_publish: ${{ steps.check.outputs.should_publish }} + gem_name: ${{ steps.spec.outputs.name }} + gem_version: ${{ steps.spec.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0 + with: + ruby-version: ${{ env.RUBY_VERSION }} + + - name: Read gem name and version from gemspec + id: spec + run: | + ruby -e 'spec = Gem::Specification.load(ENV.fetch("GEMSPEC")) or abort("cannot load #{ENV["GEMSPEC"]}"); abort("gemspec name #{spec.name} != #{ENV["GEM_NAME"]}") unless spec.name == ENV["GEM_NAME"]; puts "name=#{spec.name}"; puts "version=#{spec.version}"' >> "$GITHUB_OUTPUT" + cat "$GITHUB_OUTPUT" + + - name: Check whether this version already exists on rubygems.org + id: check + env: + GEM_NAME: ${{ steps.spec.outputs.name }} + GEM_VERSION: ${{ steps.spec.outputs.version }} + run: | + set -euo pipefail + status=$(curl -sS -o versions.json -w '%{http_code}' "https://rubygems.org/api/v1/versions/${GEM_NAME}.json") + if [ "$status" = "404" ]; then + echo "Gem ${GEM_NAME} not found on rubygems.org; will publish." + echo "should_publish=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + if [ "$status" != "200" ]; then + echo "::error::rubygems.org API returned HTTP ${status}" + exit 1 + fi + if jq -e --arg v "$GEM_VERSION" 'any(.[]; .number == $v)' versions.json > /dev/null; then + echo "${GEM_NAME} ${GEM_VERSION} already on rubygems.org; skip publish." + echo "should_publish=false" >> "$GITHUB_OUTPUT" + else + echo "${GEM_NAME} ${GEM_VERSION} not on rubygems.org yet; will publish." + echo "should_publish=true" >> "$GITHUB_OUTPUT" + fi + + test: + name: Test (Ruby 3.4) + needs: check + if: needs.check.outputs.should_publish == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + + - run: bundle exec rake t + + publish: + name: Publish ${{ needs.check.outputs.gem_name }} ${{ needs.check.outputs.gem_version }} + needs: [check, test] + if: needs.check.outputs.should_publish == 'true' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + environment: gem-publish + permissions: + id-token: write # Trusted Publishing (OIDC) + contents: write # rake release 推送 vX.Y.Z tag + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + + # setup-ruby 的 bundler-cache 會把 BUNDLE_PATH 等設定寫進 .bundle/config, + # 若該檔被 git 追蹤,rake release 的 guard_clean 會判定工作目錄不乾淨。 + # 目前此 repo 沒有追蹤任何 .bundle/config;這一步對「有追蹤才標記」做容錯處理, + # 日後若加入 gemfiles/.bundle/config 或 .bundle/config 也不會讓發佈失敗。 + - name: Ignore CI-only bundler config change + run: | + git status --porcelain + git ls-files -z '*.bundle/config' | xargs -0 -r git update-index --assume-unchanged + git status --porcelain + + # 透過 rubygems/configure-rubygems-credentials 取得短效 OIDC token, + # 再執行 bundle exec rake release(build、建立並推送 vX.Y.Z tag、push gem) + - uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1 From 0f1becc8b446817e46b3183fa61a90f0a0183d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=99=E6=B0=B4=E5=BF=8D?= <335298362+sensui624@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:25:21 +0800 Subject: [PATCH 5/5] docs: add CLAUDE.md version bump rule --- CLAUDE.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1d9b7dd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,9 @@ +# CLAUDE.md + +## 發版規則(Ruby gem) + +- 這個 gem(`xdelivery`,gemspec:`xdelivery.gemspec`)在 master 收到「新版本號」時,由 GitHub Actions(`.github/workflows/publish.yml`)自動發佈到 rubygems.org(Trusted Publishing,不使用 API key)。 +- **每一個 PR 都必須跳版號**(改 `lib/xdelivery/version.rb`),除非這個 PR 只改文件、CI 或 CLAUDE.md 且明確不需要發版。沒跳版號的 PR 合進 master 後不會發佈。 +- 跳版規則(Semantic Versioning):修 bug 跳 patch(x.y.Z);新增相容功能跳 minor(x.Y.0);不相容變更跳 major(X.0.0)。 +- 版號只改在 PR 裡。AI 帳號只能開 branch 與 PR,不得合併到 master;只有真人帳號可以合併。合併就等於批准發佈。 +- 不得為了讓測試通過而改變程式原本的行為。