diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d97c5d8..8613d52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' @@ -22,7 +24,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/amocrm-ruby' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: |- github.repository == 'stainless-sdks/amocrm-ruby' && - (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 - name: Set up Ruby diff --git a/.gitignore b/.gitignore index 3d26cee..fc9eb28 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .idea/ .ignore .prism.log +.stdy.log .ruby-lsp/ .yardoc/ bin/tapioca diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6bc1697..8d0a116 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.5.3" + ".": "0.5.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4833f36..d51e571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 0.5.4 (2026-04-09) + +Full Changelog: [v0.5.3...v0.5.4](https://github.com/Hexlet/amocrm-ruby/compare/v0.5.3...v0.5.4) + +### Bug Fixes + +* align path encoding with RFC 3986 section 3.3 ([e553b8e](https://github.com/Hexlet/amocrm-ruby/commit/e553b8e0b21dfb819d67c93a63a71009b0f728a8)) +* **internal:** correct multipart form field name encoding ([4975893](https://github.com/Hexlet/amocrm-ruby/commit/4975893b8eb2d4f427e7d40c07a3b7c1413168fb)) +* multipart encoding for file arrays ([6e86c5d](https://github.com/Hexlet/amocrm-ruby/commit/6e86c5d69289bc86c02b5a8f0d441fa5c7bb2ffc)) +* variable name typo ([e8f5466](https://github.com/Hexlet/amocrm-ruby/commit/e8f546639d827bb38a89df2e42465f9c6d019ea4)) + + +### Chores + +* **ci:** skip lint on metadata-only changes ([dad96e5](https://github.com/Hexlet/amocrm-ruby/commit/dad96e5408642a9781334f2bb4dbc32cd73872d9)) +* **ci:** support opting out of skipping builds on metadata-only commits ([34c8c04](https://github.com/Hexlet/amocrm-ruby/commit/34c8c04b937d0249fe49073193997e9311228d8f)) +* **internal:** tweak CI branches ([9a3e737](https://github.com/Hexlet/amocrm-ruby/commit/9a3e7375b6cca91433c8732e6fa060ecea85519c)) +* **internal:** update gitignore ([9fb96c7](https://github.com/Hexlet/amocrm-ruby/commit/9fb96c78308d30c29ead727cc05a101cdd1b97d1)) + ## 0.5.3 (2026-03-08) Full Changelog: [v0.5.2...v0.5.3](https://github.com/Hexlet/amocrm-ruby/compare/v0.5.2...v0.5.3) diff --git a/Gemfile.lock b/Gemfile.lock index 78c8ad1..1e7bee6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ GIT PATH remote: . specs: - amocrm (0.5.3) + amocrm (0.5.4) cgi connection_pool diff --git a/README.md b/README.md index fd3e241..e338635 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application ```ruby -gem "amocrm", "~> 0.5.3" +gem "amocrm", "~> 0.5.4" ``` diff --git a/lib/amocrm/internal/util.rb b/lib/amocrm/internal/util.rb index 0d9e3b6..a50e85e 100644 --- a/lib/amocrm/internal/util.rb +++ b/lib/amocrm/internal/util.rb @@ -157,7 +157,7 @@ def coerce_hash!(input) in Hash | nil => coerced coerced else - message = "Expected a #{Hash} or #{Amocrm::Internal::Type::BaseModel}, got #{data.inspect}" + message = "Expected a #{Hash} or #{Amocrm::Internal::Type::BaseModel}, got #{input.inspect}" raise ArgumentError.new(message) end end @@ -237,6 +237,11 @@ def dig(data, pick, &blk) end end + # @type [Regexp] + # + # https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3 + RFC_3986_NOT_PCHARS = /[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/ + class << self # @api private # @@ -247,6 +252,15 @@ def uri_origin(uri) "#{uri.scheme}://#{uri.host}#{":#{uri.port}" unless uri.port == uri.default_port}" end + # @api private + # + # @param path [String, Integer] + # + # @return [String] + def encode_path(path) + path.to_s.gsub(Amocrm::Internal::Util::RFC_3986_NOT_PCHARS) { ERB::Util.url_encode(_1) } + end + # @api private # # @param path [String, Array] @@ -259,7 +273,7 @@ def interpolate_path(path) in [] "" in [String => p, *interpolations] - encoded = interpolations.map { ERB::Util.url_encode(_1) } + encoded = interpolations.map { encode_path(_1) } format(p, *encoded) end end @@ -573,16 +587,15 @@ def encode_query_params(query) y << "Content-Disposition: form-data" unless key.nil? - name = ERB::Util.url_encode(key.to_s) - y << "; name=\"#{name}\"" + y << "; name=\"#{key}\"" end case val in Amocrm::FilePart unless val.filename.nil? - filename = ERB::Util.url_encode(val.filename) + filename = encode_path(val.filename) y << "; filename=\"#{filename}\"" in Pathname | IO - filename = ERB::Util.url_encode(::File.basename(val.to_path)) + filename = encode_path(::File.basename(val.to_path)) y << "; filename=\"#{filename}\"" else end @@ -599,6 +612,7 @@ def encode_query_params(query) # # @return [Array(String, Enumerable)] private def encode_multipart_streaming(body) + # rubocop:disable Style/CaseEquality # RFC 1521 Section 7.2.1 says we should have 70 char maximum for boundary length boundary = SecureRandom.urlsafe_base64(46) @@ -608,7 +622,7 @@ def encode_query_params(query) in Hash body.each do |key, val| case val - in Array if val.all? { primitive?(_1) } + in Array if val.all? { primitive?(_1) || Amocrm::Internal::Type::FileInput === _1 } val.each do |v| write_multipart_chunk(y, boundary: boundary, key: key, val: v, closing: closing) end @@ -624,6 +638,7 @@ def encode_query_params(query) fused_io = fused_enum(strio) { closing.each(&:call) } [boundary, fused_io] + # rubocop:enable Style/CaseEquality end # @api private diff --git a/lib/amocrm/version.rb b/lib/amocrm/version.rb index dfb8f0f..b7833c7 100644 --- a/lib/amocrm/version.rb +++ b/lib/amocrm/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Amocrm - VERSION = "0.5.3" + VERSION = "0.5.4" end diff --git a/rbi/amocrm/internal/util.rbi b/rbi/amocrm/internal/util.rbi index d126d74..cda8601 100644 --- a/rbi/amocrm/internal/util.rbi +++ b/rbi/amocrm/internal/util.rbi @@ -148,12 +148,20 @@ module Amocrm end end + # https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3 + RFC_3986_NOT_PCHARS = T.let(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/, Regexp) + class << self # @api private sig { params(uri: URI::Generic).returns(String) } def uri_origin(uri) end + # @api private + sig { params(path: T.any(String, Integer)).returns(String) } + def encode_path(path) + end + # @api private sig { params(path: T.any(String, T::Array[String])).returns(String) } def interpolate_path(path) diff --git a/sig/amocrm/internal/util.rbs b/sig/amocrm/internal/util.rbs index a185d31..a88cc9b 100644 --- a/sig/amocrm/internal/util.rbs +++ b/sig/amocrm/internal/util.rbs @@ -45,8 +45,12 @@ module Amocrm -> top? } -> top? + RFC_3986_NOT_PCHARS: Regexp + def self?.uri_origin: (URI::Generic uri) -> String + def self?.encode_path: (String | Integer path) -> String + def self?.interpolate_path: (String | ::Array[String] path) -> String def self?.decode_query: (String? query) -> ::Hash[String, ::Array[String]]