diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4a622e..84c5a08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,40 @@ on: - 'stl-preview-base/**' jobs: + build: + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/tilda-ruby' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: |- + github.repository == 'stainless-sdks/tilda-ruby' && + (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + steps: + - uses: actions/checkout@v6 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: false + - run: |- + bundle install + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/tilda-ruby' + id: github-oidc + uses: actions/github-script@v8 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Build and upload gem artifacts + if: github.repository == 'stainless-sdks/tilda-ruby' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + PACKAGE_NAME: tilda_ruby + run: ./scripts/utils/upload-artifact.sh lint: timeout-minutes: 10 name: lint diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..5547f83 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.1.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b892f2f..5b36364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.1.1 (2026-03-03) + +Full Changelog: [v0.1.0...v0.1.1](https://github.com/Hexlet/tilda-ruby/compare/v0.1.0...v0.1.1) + +### Bug Fixes + +* properly mock time in ruby ci tests ([137828d](https://github.com/Hexlet/tilda-ruby/commit/137828d8011b68ed9b2a5214d425d19b57188146)) + + +### Chores + +* **ci:** add build step ([99b5132](https://github.com/Hexlet/tilda-ruby/commit/99b51329fea77ff1180ca43f5bb8a68c4967c1a0)) +* **internal:** codegen related update ([84f2624](https://github.com/Hexlet/tilda-ruby/commit/84f262489527dbb97b1298cb9a8ede204dc39e0d)) + ## 0.1.0 (2026-02-25) Full Changelog: [v0.0.2...v0.1.0](https://github.com/Hexlet/tilda-ruby/compare/v0.0.2...v0.1.0) diff --git a/Gemfile.lock b/Gemfile.lock index 56e87b4..f040ee0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ GIT PATH remote: . specs: - tilda-ruby (0.1.0) + tilda-ruby (0.1.1) cgi connection_pool diff --git a/README.md b/README.md index aa7d6d1..6066170 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ To use this gem, install via Bundler by adding the following to your application ```ruby -gem "tilda-ruby", "~> 0.1.0" +gem "tilda-ruby", "~> 0.1.1" ``` diff --git a/lib/tilda_ruby/internal/util.rb b/lib/tilda_ruby/internal/util.rb index 628033b..e99eda6 100644 --- a/lib/tilda_ruby/internal/util.rb +++ b/lib/tilda_ruby/internal/util.rb @@ -490,6 +490,37 @@ def writable_enum(&blk) JSONL_CONTENT = %r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)} class << self + # @api private + # + # @param query [Hash{Symbol=>Object}] + # + # @return [Hash{Symbol=>Object}] + def encode_query_params(query) + out = {} + query.each { write_query_param_element!(out, _1, _2) } + out + end + + # @api private + # + # @param collection [Hash{Symbol=>Object}] + # @param key [String] + # @param element [Object] + # + # @return [nil] + private def write_query_param_element!(collection, key, element) + case element + in Hash + element.each do |name, value| + write_query_param_element!(collection, "#{key}[#{name}]", value) + end + in Array + collection[key] = element.map(&:to_s).join(",") + else + collection[key] = element.to_s + end + end + # @api private # # @param y [Enumerator::Yielder] diff --git a/lib/tilda_ruby/resources/export.rb b/lib/tilda_ruby/resources/export.rb index 6ad34ed..beca992 100644 --- a/lib/tilda_ruby/resources/export.rb +++ b/lib/tilda_ruby/resources/export.rb @@ -13,10 +13,11 @@ class Export # @see TildaRuby::Models::ExportRetrieveParams def retrieve(params) parsed, options = TildaRuby::ExportRetrieveParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getpageexport", - query: parsed, + query: query, model: TildaRuby::Models::ExportRetrieveResponse, options: options ) @@ -32,10 +33,11 @@ def retrieve(params) # @see TildaRuby::Models::ExportRetrieveFullParams def retrieve_full(params) parsed, options = TildaRuby::ExportRetrieveFullParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getpagefullexport", - query: parsed, + query: query, model: TildaRuby::Models::ExportRetrieveFullResponse, options: options ) diff --git a/lib/tilda_ruby/resources/pages.rb b/lib/tilda_ruby/resources/pages.rb index ab31299..884c1ec 100644 --- a/lib/tilda_ruby/resources/pages.rb +++ b/lib/tilda_ruby/resources/pages.rb @@ -13,10 +13,11 @@ class Pages # @see TildaRuby::Models::PageRetrieveParams def retrieve(params) parsed, options = TildaRuby::PageRetrieveParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getpage", - query: parsed, + query: query, model: TildaRuby::Models::PageRetrieveResponse, options: options ) @@ -32,10 +33,11 @@ def retrieve(params) # @see TildaRuby::Models::PageListParams def list(params) parsed, options = TildaRuby::PageListParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getpageslist", - query: parsed, + query: query, model: TildaRuby::Models::PageListResponse, options: options ) @@ -51,10 +53,11 @@ def list(params) # @see TildaRuby::Models::PageRetrieveFullParams def retrieve_full(params) parsed, options = TildaRuby::PageRetrieveFullParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getpagefull", - query: parsed, + query: query, model: TildaRuby::Models::PageRetrieveFullResponse, options: options ) diff --git a/lib/tilda_ruby/resources/projects.rb b/lib/tilda_ruby/resources/projects.rb index 6971e9c..abb0e6b 100644 --- a/lib/tilda_ruby/resources/projects.rb +++ b/lib/tilda_ruby/resources/projects.rb @@ -13,10 +13,11 @@ class Projects # @see TildaRuby::Models::ProjectRetrieveParams def retrieve(params) parsed, options = TildaRuby::ProjectRetrieveParams.dump_request(params) + query = TildaRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/getprojectinfo", - query: parsed, + query: query, model: TildaRuby::Models::ProjectRetrieveResponse, options: options ) diff --git a/lib/tilda_ruby/version.rb b/lib/tilda_ruby/version.rb index 9bd21fb..9a8fcdb 100644 --- a/lib/tilda_ruby/version.rb +++ b/lib/tilda_ruby/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module TildaRuby - VERSION = "0.1.0" + VERSION = "0.1.1" end diff --git a/rbi/tilda_ruby/internal/util.rbi b/rbi/tilda_ruby/internal/util.rbi index 57c1567..a06bbd0 100644 --- a/rbi/tilda_ruby/internal/util.rbi +++ b/rbi/tilda_ruby/internal/util.rbi @@ -301,6 +301,26 @@ module TildaRuby T.let(%r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)}, Regexp) class << self + # @api private + sig do + params(query: TildaRuby::Internal::AnyHash).returns( + TildaRuby::Internal::AnyHash + ) + end + def encode_query_params(query) + end + + # @api private + sig do + params( + collection: TildaRuby::Internal::AnyHash, + key: String, + element: T.anything + ).void + end + private def write_query_param_element!(collection, key, element) + end + # @api private sig do params( diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 0000000..9a9e912 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# ANSI Color Codes +GREEN='\033[32m' +RED='\033[31m' +NC='\033[0m' # No Color + +DIST_DIR="dist" + +log_error() { + local msg="$1" + local headers="$2" + local body="$3" + echo -e "${RED}${msg}${NC}" + [[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}" + echo -e "${RED}Body: ${body}${NC}" + exit 1 +} + +upload_file() { + local file_name="$1" + local tmp_headers + tmp_headers=$(mktemp) + + if [ -f "$file_name" ]; then + echo -e "${GREEN}Processing file: $file_name${NC}" + pkg_file_name="${file_name#"${DIST_DIR}/"}" + + # Get signed URL for uploading artifact file + signed_url_response=$(curl -X POST -G "$URL" \ + -sS --retry 5 \ + -D "$tmp_headers" \ + --data-urlencode "filename=$pkg_file_name" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + + # Validate JSON and extract URL + if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then + log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response" + fi + + # Set content-type based on file extension + local extension="${file_name##*.}" + local content_type + case "$extension" in + gem) content_type="application/octet-stream" ;; + gz) content_type="application/gzip" ;; + rz) content_type="application/octet-stream" ;; + html) content_type="text/html" ;; + *) content_type="application/octet-stream" ;; + esac + + # Upload file + upload_response=$(curl -v -X PUT \ + --retry 5 \ + --retry-all-errors \ + -D "$tmp_headers" \ + -H "Content-Type: $content_type" \ + --data-binary "@${file_name}" "$signed_url" 2>&1) + + if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then + log_error "Failed to upload artifact file" "$tmp_headers" "$upload_response" + fi + + # Insert small throttle to reduce rate limiting risk + sleep 0.1 + fi +} + +walk_tree() { + local current_dir="$1" + + for entry in "$current_dir"/*; do + # Check that entry is valid + [ -e "$entry" ] || [ -h "$entry" ] || continue + + if [ -d "$entry" ]; then + walk_tree "$entry" + else + upload_file "$entry" + fi + done +} + +cd "$(dirname "$0")/../.." + +echo "::group::Building gem" +VERSION_FILE="lib/${PACKAGE_NAME}/version.rb" +if [[ ! -f "$VERSION_FILE" ]]; then + echo -e "${RED}Version file not found: ${VERSION_FILE}${NC}" + exit 1 +fi +SHORT_SHA="${SHA:0:7}" +sed -i.bak -E "s/(VERSION = \"[^\"]+)\"/\1.beta.${SHORT_SHA}\"/" "$VERSION_FILE" +rm -f "${VERSION_FILE}.bak" + +gem build +mkdir -p "${DIST_DIR}/gems" +mv ./*.gem "${DIST_DIR}/gems/" +echo "::endgroup::" + +echo "::group::Generating gem index" +gem generate_index --directory "$DIST_DIR" +echo "::endgroup::" + +echo "::group::Uploading to pkg.stainless.com" +walk_tree "$DIST_DIR" +echo "::endgroup::" + +echo -e "${GREEN}Gem artifacts uploaded to Stainless storage.${NC}" +echo -e "\033[32mInstallation: bundle remove tilda-ruby && bundle add tilda-ruby --source 'https://pkg.stainless.com/s/tilda-ruby/$SHA'\033[0m" diff --git a/sig/tilda_ruby/internal/util.rbs b/sig/tilda_ruby/internal/util.rbs index 871c2e5..94a7079 100644 --- a/sig/tilda_ruby/internal/util.rbs +++ b/sig/tilda_ruby/internal/util.rbs @@ -106,6 +106,16 @@ module TildaRuby JSON_CONTENT: Regexp JSONL_CONTENT: Regexp + def encode_query_params: ( + ::Hash[Symbol, top] query + ) -> ::Hash[Symbol, top] + + private def write_query_param_element!: ( + ::Hash[Symbol, top] collection, + String key, + top element + ) -> nil + def self?.write_multipart_content: ( Enumerator::Yielder y, val: top, diff --git a/test/tilda_ruby/client_test.rb b/test/tilda_ruby/client_test.rb index 3ab94ce..b3d55b9 100644 --- a/test/tilda_ruby/client_test.rb +++ b/test/tilda_ruby/client_test.rb @@ -133,9 +133,11 @@ def test_client_retry_after_seconds end def test_client_retry_after_date + time_now = Time.now + stub_request(:get, "http://localhost/v1/getprojectslist?publickey,secretkey").to_return_json( status: 500, - headers: {"retry-after" => (Time.now + 10).httpdate}, + headers: {"retry-after" => (time_now + 10).httpdate}, body: {} ) @@ -147,11 +149,11 @@ def test_client_retry_after_date max_retries: 1 ) + Thread.current.thread_variable_set(:time_now, time_now) assert_raises(TildaRuby::Errors::InternalServerError) do - Thread.current.thread_variable_set(:time_now, Time.now) tilda.projects.list - Thread.current.thread_variable_set(:time_now, nil) end + Thread.current.thread_variable_set(:time_now, nil) assert_requested(:any, /./, times: 2) assert_in_delta(10, Thread.current.thread_variable_get(:mock_sleep).last, 1.0)