diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 8b8f4c83..8b137891 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -1,10 +1 @@ -assign_prs: - - dazuma - - akerekes - - nifflets -assign_issues: - - dazuma - - akerekes - - nifflets - diff --git a/.github/workflows/buildpack-integration-test.yml b/.github/workflows/buildpack-integration-test.yml index 744e729a..fb820f9f 100644 --- a/.github/workflows/buildpack-integration-test.yml +++ b/.github/workflows/buildpack-integration-test.yml @@ -23,5 +23,5 @@ jobs: cloudevent-builder-source: 'test/conformance' cloudevent-builder-target: 'cloudevent_func' prerun: ${{format('test/conformance/prerun.sh {0} testdata/conformance/function', github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha )}} - builder-runtime: 'ruby30' - builder-runtime-version: '3.0' + builder-runtime: 'ruby31' + builder-runtime-version: '3.1' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index da818dd6..2ee9d94a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -52,11 +52,11 @@ jobs: uploads.github.com:443 - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -79,6 +79,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 75195f58..a3118116 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] + ruby: ['3.1', '3.2', '3.3', '3.4'] steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -29,46 +29,47 @@ jobs: index.rubygems.org:443 objects.githubusercontent.com:443 proxy.golang.org:443 + release-assets.githubusercontent.com:443 rubygems.org:443 storage.googleapis.com:443 - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: - go-version: '1.21' + go-version: '1.24' check-latest: true - name: Setup Ruby - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # v1.247.0 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run HTTP conformance tests - uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main + uses: GoogleCloudPlatform/functions-framework-conformance/action@a95b422b1e7ef3c73d2b7605db428e6387b106fc # main with: functionType: 'http' useBuildpacks: false cmd: "'bundle exec functions-framework-ruby --source test/conformance/app.rb --target http_func --signature-type http'" - name: Run Typed conformance tests - uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main + uses: GoogleCloudPlatform/functions-framework-conformance/action@a95b422b1e7ef3c73d2b7605db428e6387b106fc # main with: functionType: 'http' declarativeType: 'typed' useBuildpacks: false cmd: "'bundle exec functions-framework-ruby --source test/conformance/app.rb --target typed_func --signature-type http'" - name: Run CloudEvent conformance tests - uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main + uses: GoogleCloudPlatform/functions-framework-conformance/action@a95b422b1e7ef3c73d2b7605db428e6387b106fc # main with: functionType: 'cloudevent' useBuildpacks: false validateMapping: true cmd: "'bundle exec functions-framework-ruby --source test/conformance/app.rb --target cloudevent_func --signature-type cloudevent'" - name: Run HTTP concurrency tests - uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main + uses: GoogleCloudPlatform/functions-framework-conformance/action@a95b422b1e7ef3c73d2b7605db428e6387b106fc # main with: functionType: 'http' useBuildpacks: false validateConcurrency: true cmd: "'bundle exec functions-framework-ruby --source test/conformance/app.rb --target concurrent_http_func --signature-type http'" - \ No newline at end of file + diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index fa98b209..ac7a0a0c 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -26,6 +26,6 @@ jobs: github.com:443 - name: 'Checkout Repository' - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 \ No newline at end of file + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 215d1b10..0c30a2f7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -24,14 +24,15 @@ jobs: github.com:443 index.rubygems.org:443 objects.githubusercontent.com:443 + release-assets.githubusercontent.com:443 rubygems.org:443 - name: Checkout repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: Install Ruby 3.0 - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install Ruby 3.1 + uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # v1.247.0 with: - ruby-version: "3.0" + ruby-version: "3.1" bundler-cache: true - name: Install toys run: gem install --no-document toys diff --git a/.github/workflows/push-gh-pages.yml b/.github/workflows/push-gh-pages.yml index 931da11d..f2771f3f 100644 --- a/.github/workflows/push-gh-pages.yml +++ b/.github/workflows/push-gh-pages.yml @@ -10,7 +10,7 @@ permissions: read-all jobs: push-gh-pages: env: - ruby_version: "3.0" + ruby_version: "3.1" runs-on: ubuntu-latest permissions: @@ -18,13 +18,13 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Checkout repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Ruby ${{ env.ruby_version }} - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # v1.247.0 with: ruby-version: ${{ env.ruby_version }} bundler-cache: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 210accf1..62004ed1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -44,12 +44,12 @@ jobs: www.bestpractices.dev:443 - name: "Checkout code" - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 with: results_file: results.sarif results_format: sarif @@ -61,6 +61,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: sarif_file: results.sarif diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 2ee91fde..db1e9ed4 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] + ruby: ['3.1', '3.2', '3.3', '3.4'] flags: ["--only --test-unit"] include: - os: ubuntu-latest @@ -26,19 +26,19 @@ jobs: ruby: truffleruby flags: "--only --test-unit" - os: macos-latest - ruby: "3.0" + ruby: "3.1" flags: "--only --test-unit" - os: windows-latest - ruby: "3.0" + ruby: "3.2" # 3.1 fails similar to https://github.com/rubygems/rubygems/issues/4338 flags: "--only --test-unit" - os: ubuntu-latest - ruby: "3.0" + ruby: "3.1" flags: "--only --test-yardoc --test-build --test-examples" fail-fast: false runs-on: ${{ matrix.os }} steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: disable-sudo: true egress-policy: block @@ -46,12 +46,13 @@ jobs: github.com:443 index.rubygems.org:443 objects.githubusercontent.com:443 + release-assets.githubusercontent.com:443 rubygems.org:443 - name: Checkout repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Ruby ${{ matrix.ruby }} - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # v1.247.0 with: ruby-version: "${{ matrix.ruby }}" bundler-cache: true diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh index aab0ec38..de6c0db5 100644 --- a/.kokoro/populate-secrets.sh +++ b/.kokoro/populate-secrets.sh @@ -23,6 +23,10 @@ function msg { println "$*" >&2 ;} function println { printf '%s\n' "$(now) $*" ;} # Populates requested secrets set in SECRET_MANAGER_KEYS +if [[ -z "${SECRET_MANAGER_PROJECT_ID-}" ]]; then + msg "SECRET_MANAGER_PROJECT_ID is not set in environment variables, using default" + SECRET_MANAGER_PROJECT_ID="cloud-devrel-kokoro-resources" +fi # In Kokoro CI builds, we use the service account attached to the # Kokoro VM. This means we need to setup auth on other CI systems. @@ -64,7 +68,7 @@ do msg "Retrieving secret ${key}" "${GCLOUD_COMMANDS[@]}" \ secrets versions access latest \ - --project cloud-devrel-kokoro-resources \ + --project "${SECRET_MANAGER_PROJECT_ID}" \ --secret $key > \ "$SECRET_LOCATION/$key" if [[ $? == 0 ]]; then diff --git a/.kokoro/release.cfg b/.kokoro/release.cfg index fa48e651..decc9818 100644 --- a/.kokoro/release.cfg +++ b/.kokoro/release.cfg @@ -7,19 +7,13 @@ action { } } -# Download resources for system tests (service account key, etc.) -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-ruby" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - # Use the trampoline script to run in docker. build_file: "functions-framework-ruby/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/yoshi-ruby/release" + value: "us-central1-docker.pkg.dev/cloud-sdk-release-custom-pool/release-images/ruby-release" } env_vars: { @@ -27,16 +21,32 @@ env_vars: { value: ".kokoro/release.sh" } +env_vars: { + key: "SECRET_MANAGER_PROJECT_ID" + value: "cloud-sdk-release-custom-pool" +} + env_vars: { key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googlecloudplatform-installation,releasetool-publish-reporter-pem,docuploader_service_account" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googlecloudplatform-installation,releasetool-publish-reporter-pem" +} + +# Pick up Rubygems key from internal keystore +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "rubygems-publish-key" + backend: "blade:keystore-fastconfigpush" + } + } } # Store the packages uploaded to rubygems.org, which # we can later use to generate SBOMs and attestations. action { - define_artifacts { - regex: "github/functions-framework-ruby/pkg/*.gem" - strip_prefix: "github" - } + define_artifacts { + regex: "github/functions-framework-ruby/pkg/*.gem" + strip_prefix: "github" + } } diff --git a/.kokoro/release.sh b/.kokoro/release.sh index 4adfa232..185ebfac 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -7,7 +7,4 @@ set -eo pipefail export GEM_HOME=$HOME/.gem export PATH=$GEM_HOME/bin:$PATH -gem install --no-document toys -toys release install-python-tools -v -python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script -toys release perform -v --enable-docs < /dev/null +toys release perform -v --reporter-org=googlecloudplatform < /dev/null diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index ef6972b4..11918a26 100644 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -138,18 +138,26 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then RUNNING_IN_CI="true" TRAMPOLINE_CI="kokoro" if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then - if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then - log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting." - exit 1 - fi - # This service account will be activated later. - TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" + if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then + log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting." + exit 1 + fi + # This service account will be activated later. + TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" else - if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then - gcloud auth list - fi - log_yellow "Configuring Container Registry access" - gcloud auth configure-docker --quiet + if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then + gcloud auth list + fi + log_yellow "Configuring Container Registry access" + TRAMPOLINE_HOST=$(echo "${TRAMPOLINE_IMAGE}" | cut -d/ -f1) + if [[ ! "${TRAMPOLINE_HOST}" =~ "gcr.io" ]]; then + # If you need to specificy a host other than gcr.io, you have to run on an update version of gcloud. + echo "TRAMPOLINE_HOST: ${TRAMPOLINE_HOST}" + gcloud components update + gcloud auth configure-docker "${TRAMPOLINE_HOST}" + else + gcloud auth configure-docker --quiet + fi fi pass_down_envvars+=( # KOKORO dynamic variables. diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4918b25e..0d1bebe1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.4.1" + ".": "1.6.0" } diff --git a/.rubocop.yml b/.rubocop.yml index 076b34c2..556b6511 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,3 +31,5 @@ Naming/FileName: Style/BlockDelimiters: Exclude: - "test/**/test_*.rb" +Style/DoubleNegation: + Enabled: false diff --git a/.trampolinerc b/.trampolinerc index d4c5febe..493e8d70 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -18,7 +18,12 @@ required_envvars+=( # Add env vars which are passed down into the container here. pass_down_envvars+=( - "AUTORELEASE_PR" "RELEASE_DRY_RUN" "RELEASE_PACKAGE" "KOKORO_GIT_COMMIT" "RUBY_VERSIONS" "EXTRA_CI_ARGS" + "AUTORELEASE_PR" + "EXTRA_CI_ARGS" + "KOKORO_GIT_COMMIT" + "RELEASE_DRY_RUN" + "RELEASE_PACKAGE" + "RUBY_VERSIONS" ) # Prevent unintentional override on the default image. diff --git a/CHANGELOG.md b/CHANGELOG.md index 23620862..dac7993b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +### 1.6.0 (2025-02-07) + +#### Features + +* drop support for ruby <3.0 ([#210](https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues/210)) + +### 1.5.1 (2025-02-06) + +### Miscellaneous Chores + +* release 1.5.1 ([#211](https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues/211)) + +### 1.5.0 (2025-01-03) + +#### Features + +* Support pidfile in CLI & Server (defaults to puma.pid) ([#178](https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues/178)) + +### 1.4.2 (2024-09-18) + +#### Bug Fixes + +* typo in cli help ([#196](https://github.com/GoogleCloudPlatform/functions-framework-ruby/issues/196)) +#### Documentation + +* fix a broken link to the rack SPEC + ### 1.4.1 (2023-06-27) #### Bug Fixes diff --git a/Gemfile b/Gemfile index 918e4a2d..c495c220 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,8 @@ source "https://rubygems.org" gemspec -gem "google-style", "~> 1.26.3" +gem "base64" +gem "google-style", "~> 1.31.0" gem "minitest", "~> 5.16" gem "minitest-focus", "~> 1.2" gem "minitest-rg", "~> 5.2" diff --git a/README.md b/README.md index 0b0736ce..0be12ae3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ An open source framework for writing lightweight, portable Ruby functions that run in a serverless environment. Functions written to this Framework will run in many different environments, including: - * [Google Cloud Functions](https://cloud.google.com/functions) - * [Google Cloud Run](https://cloud.google.com/run) + * [Google Cloud Run functions](https://cloud.google.com/functions) * Any other [Knative](https://github.com/knative)-based environment * Your local development machine @@ -42,7 +41,7 @@ requiring an HTTP server or complicated request handling logic. ## Supported Ruby versions -This library is supported on Ruby 2.6+. +This library is supported on Ruby 3.0+. Google provides official support for Ruby versions that are actively supported by Ruby Core—that is, Ruby versions that are either in normal maintenance or @@ -113,8 +112,7 @@ These guides provide additional getting-started information. functions server. * **[Deploying Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.deploying-functions.html)** : How to deploy functions to - [Google Cloud Functions](https://cloud.google.com/functions) or - [Google Cloud Run](https://cloud.google.com/run). + [Google Cloud Run functions](https://cloud.google.com/functions) The library reference documentation can be found at: https://googlecloudplatform.github.io/functions-framework-ruby diff --git a/docs/deploying-functions.md b/docs/deploying-functions.md index 0f4c6cf6..c59b437c 100644 --- a/docs/deploying-functions.md +++ b/docs/deploying-functions.md @@ -30,9 +30,9 @@ Google Cloud Functions is Google's scalable pay-as-you-go Functions-as-a-Service Functions Framework is designed especially for functions that can be hosted on Cloud Functions. -You can run Ruby functions on Google Cloud Functions by selecting the `ruby26` -runtime or `ruby27` runtime to use a recent release of Ruby 2.6 or Ruby 2.7. -Support for Ruby 3.0 is forthcoming. +You can run Ruby functions on Google Cloud Functions by selecting the `ruby32` +runtime or `ruby33` runtime to use a recent release of Ruby 3.2 or Ruby 3.3. +Support for Ruby 3.4 is forthcoming. ### Deploying and updating your function @@ -60,7 +60,7 @@ Then, issue the gcloud command to deploy: ```sh gcloud functions deploy $YOUR_FUNCTION_NAME \ --project=$YOUR_PROJECT_ID \ - --runtime=ruby27 \ + --runtime=ruby33 \ --trigger-http \ --entry-point=$YOUR_FUNCTION_TARGET ``` @@ -86,7 +86,7 @@ and above, set `FUNCTION_LOGGING_LEVEL` to `WARN` when deploying: ```sh gcloud functions deploy $YOUR_FUNCTION_NAME --project=$YOUR_PROJECT_ID \ - --runtime=ruby27 --trigger-http --source=$YOUR_FUNCTION_SOURCE \ + --runtime=ruby33 --trigger-http --source=$YOUR_FUNCTION_SOURCE \ --entry-point=$YOUR_FUNCTION_TARGET \ --set-env-vars=FUNCTION_LOGGING_LEVEL=WARN ``` @@ -121,7 +121,7 @@ Dockerfile that you can use as a starting point. Feel free to adjust it to the needs of your project: ``` -FROM ruby:2.7 +FROM ruby:3.3 WORKDIR /app COPY . . RUN gem install --no-document bundler \ @@ -147,8 +147,8 @@ command may ask you for permission to enable the Cloud Build API for the project if it isn't already enabled. Because you provide your own Docker image when deploying to Cloud Run, you can -use any version of Ruby supported by the Functions Framework, from 2.6 through -3.1. +use any version of Ruby supported by the Functions Framework, from 3.0 through +3.3. ### Deploying an image to Cloud Run diff --git a/docs/overview.md b/docs/overview.md index 3029a468..76430ca6 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -46,7 +46,7 @@ requiring an HTTP server or complicated request handling logic. ## Supported Ruby versions -This library is supported on Ruby 2.6+. +This library is supported on Ruby 3.0+. Google provides official support for Ruby versions that are actively supported by Ruby Core—that is, Ruby versions that are either in normal maintenance or diff --git a/functions_framework.gemspec b/functions_framework.gemspec index 1143d03a..6ccd3d85 100644 --- a/functions_framework.gemspec +++ b/functions_framework.gemspec @@ -44,7 +44,7 @@ version = ::FunctionsFramework::VERSION spec.bindir = "bin" spec.executables = ["functions-framework", "functions-framework-ruby"] - spec.required_ruby_version = ">= 2.6.0" + spec.required_ruby_version = ">= 3.1.0" spec.add_dependency "cloud_events", ">= 0.7.0", "< 2.a" spec.add_dependency "puma", ">= 4.3.0", "< 7.a" spec.add_dependency "rack", ">= 2.1", "< 4.a" diff --git a/lib/functions_framework.rb b/lib/functions_framework.rb index c2308e7f..9a886834 100644 --- a/lib/functions_framework.rb +++ b/lib/functions_framework.rb @@ -134,11 +134,10 @@ class << self # end # # @param name [String] The function name. Defaults to {DEFAULT_TARGET}. - # @param block [Proc] The function code as a proc. # @return [self] # - def http name = DEFAULT_TARGET, &block - global_registry.add_http name, &block + def http(name = DEFAULT_TARGET, &) + global_registry.add_http(name, &) self end @@ -164,11 +163,10 @@ def http name = DEFAULT_TARGET, &block # @param name [String] The function name. Defaults to {DEFAULT_TARGET} # @param request_class [#decode_json] An optional class which will be used to # decode the request if it implements a `decode_json` static method. - # @param block [Proc] The function code as a proc @return [self] # @return [self] # - def typed name = DEFAULT_TARGET, request_class: nil, &block - global_registry.add_typed name, request_class: request_class, &block + def typed(name = DEFAULT_TARGET, request_class: nil, &) + global_registry.add_typed(name, request_class: request_class, &) self end @@ -187,16 +185,15 @@ def typed name = DEFAULT_TARGET, request_class: nil, &block # end # # @param name [String] The function name. Defaults to {DEFAULT_TARGET}. - # @param block [Proc] The function code as a proc. # @return [self] # - def cloud_event name = DEFAULT_TARGET, &block - global_registry.add_cloud_event name, &block + def cloud_event(name = DEFAULT_TARGET, &) + global_registry.add_cloud_event(name, &) self end ## - # Define a server startup task. This is useful for initializing shared + # Define a server startup task as a block. This is useful for initializing shared # resources that should be accessible across all function invocations in # this Ruby VM. # @@ -208,11 +205,10 @@ def cloud_event name = DEFAULT_TARGET, &block # Startup tasks are passed the {FunctionsFramework::Function} identifying # the function to execute, and have no return value. # - # @param block [Proc] The startup task # @return [self] # - def on_startup &block - global_registry.add_startup_task(&block) + def on_startup(&) + global_registry.add_startup_task(&) self end @@ -227,7 +223,7 @@ def on_startup &block # manipulated to configure the server. # @return [FunctionsFramework::Server] # - def start target, &block + def start(target, &) require "functions_framework/server" if target.is_a? ::FunctionsFramework::Function function = target @@ -236,7 +232,7 @@ def start target, &block raise ::ArgumentError, "Undefined function: #{target.inspect}" if function.nil? end globals = function.populate_globals - server = Server.new function, globals, &block + server = Server.new(function, globals, &) global_registry.startup_tasks.each do |task| task.call function, globals: globals, logger: server.config.logger end @@ -255,8 +251,8 @@ def start target, &block # manipulated to configure the server. # @return [self] # - def run target, &block - server = start target, &block + def run(target, &) + server = start(target, &) server.wait_until_stopped self end diff --git a/lib/functions_framework/cli.rb b/lib/functions_framework/cli.rb index f6f00d71..6623dc12 100644 --- a/lib/functions_framework/cli.rb +++ b/lib/functions_framework/cli.rb @@ -36,6 +36,7 @@ def initialize @source = ::ENV["FUNCTION_SOURCE"] || ::FunctionsFramework::DEFAULT_SOURCE @env = nil @port = nil + @pidfile = nil @bind = nil @min_threads = nil @max_threads = nil @@ -67,6 +68,12 @@ def error? # attr_reader :error_message + ## + # @return [String] The pidfile. + # @return [nil] if not running. + # + attr_reader :pidfile + ## # Parse the given command line arguments. # Exits if argument parsing failed. @@ -89,6 +96,9 @@ def parse_args argv # rubocop:disable Metrics/MethodLength,Metrics/AbcSize "Supported values are 'http' and 'cloudevent'." do |val| @signature_type = val end + op.on "-P", "--pidfile PIDFILE", "Set the pidfile for the server (defaults to puma.pid)" do |val| + @pidfile = val + end op.on "-p", "--port PORT", "Set the port to listen to (defaults to 8080)" do |val| @port = val.to_i end @@ -218,6 +228,7 @@ def start_server ::FunctionsFramework.start function do |config| config.rack_env = @env config.port = @port + config.pidfile = @pidfile config.bind_addr = @bind config.show_error_details = @detailed_errors config.min_threads = @min_threads diff --git a/lib/functions_framework/legacy_event_converter.rb b/lib/functions_framework/legacy_event_converter.rb index ff3f75d9..acaae6c0 100644 --- a/lib/functions_framework/legacy_event_converter.rb +++ b/lib/functions_framework/legacy_event_converter.rb @@ -185,6 +185,7 @@ def convert_data context, data %r{^providers/google\.firebase\.analytics/} => "firebase.googleapis.com", %r{^providers/google\.firebase\.database/} => "firebasedatabase.googleapis.com" }.freeze + private_constant :LEGACY_TYPE_TO_SERVICE LEGACY_TYPE_TO_CE_TYPE = { "google.pubsub.topic.publish" => "google.cloud.pubsub.topic.v1.messagePublished", @@ -206,6 +207,7 @@ def convert_data context, data "providers/google.firebase.database/eventTypes/ref.delete" => "google.firebase.database.ref.v1.deleted", "providers/cloud.storage/eventTypes/object.change" => "google.cloud.storage.object.v1.finalized" }.freeze + private_constant :LEGACY_TYPE_TO_CE_TYPE CE_SERVICE_TO_RESOURCE_RE = { "firebase.googleapis.com" => %r{^(projects/[^/]+)/(events/[^/]+)$}, @@ -213,11 +215,13 @@ def convert_data context, data "firestore.googleapis.com" => %r{^(projects/[^/]+/databases/\(default\))/(documents/.+)$}, "storage.googleapis.com" => %r{^(projects/[^/]+/buckets/[^/]+)/([^#]+)(?:#.*)?$} }.freeze + private_constant :CE_SERVICE_TO_RESOURCE_RE # Map Firebase Auth legacy event metadata field names to their equivalent CloudEvent field names. FIREBASE_AUTH_METADATA_LEGACY_TO_CE = { "createdAt" => "createTime", "lastSignedInAt" => "lastSignInTime" }.freeze + private_constant :FIREBASE_AUTH_METADATA_LEGACY_TO_CE end end diff --git a/lib/functions_framework/server.rb b/lib/functions_framework/server.rb index 074ebccd..4f86d79e 100644 --- a/lib/functions_framework/server.rb +++ b/lib/functions_framework/server.rb @@ -152,6 +152,24 @@ def running? @server&.thread&.alive? end + ## + # Returns pidfile if server is currently running + # + # @return [String, nil] + # + def pidfile + @config.pidfile if running? + end + + ## + # Returns whether pidfile is present. + # + # @return [Boolean] + # + def pidfile? + !!@config.pidfile && running? + end + ## # Cause this server to respond to SIGTERM, SIGINT, and SIGHUP by shutting # down gracefully. @@ -214,6 +232,7 @@ def initialize self.rack_env = nil self.bind_addr = nil self.port = nil + self.pidfile = nil self.min_threads = nil self.max_threads = nil self.show_error_details = nil @@ -245,6 +264,14 @@ def port= port @port = (port || ::ENV["PORT"] || 8080).to_i end + ## + # Set the pidfile string, or `nil` to use the default. + # @param path [String,nil] + # + def pidfile= path + @pidfile = (path || ::ENV["PIDFILE"] || "puma.pid").to_s + end + ## # Set the minimum number of worker threads, or `nil` to use the default. # @param min_threads [Integer,nil] @@ -270,7 +297,7 @@ def show_error_details= show_error_details if show_error_details.nil? !::ENV["FUNCTION_DETAILED_ERRORS"].to_s.empty? else - show_error_details ? true : false + !!show_error_details end end @@ -306,6 +333,14 @@ def port @port end + ## + # Returns the current pidfile string. + # @return [String] + # + def pidfile + @pidfile + end + ## # Returns the minimum number of worker threads in the thread pool. # @return [Integer] diff --git a/lib/functions_framework/testing.rb b/lib/functions_framework/testing.rb index a78bc640..9c18652c 100644 --- a/lib/functions_framework/testing.rb +++ b/lib/functions_framework/testing.rb @@ -70,9 +70,9 @@ module Testing # # @param path [String] File path to load # - def load_temporary path, &block + def load_temporary(path, &) path = ::File.expand_path path - Testing.load_for_testing path, &block + Testing.load_for_testing(path, &) end ## diff --git a/lib/functions_framework/version.rb b/lib/functions_framework/version.rb index 771c3433..650e0e79 100644 --- a/lib/functions_framework/version.rb +++ b/lib/functions_framework/version.rb @@ -17,5 +17,5 @@ module FunctionsFramework # Version of the Ruby Functions Framework # @return [String] # - VERSION = "1.4.1".freeze + VERSION = "1.6.0".freeze end diff --git a/test/test_cli.rb b/test/test_cli.rb index 4c0d9fff..5e32f649 100644 --- a/test/test_cli.rb +++ b/test/test_cli.rb @@ -27,6 +27,7 @@ let(:retry_count) { 10 } let(:retry_interval) { 0.5 } let(:port) { "8066" } + let(:pidfile) { "server.pid" } let(:timeout) { 10 } def run_with_retry cli @@ -104,6 +105,21 @@ def run_in_timeout cli assert_equal "I received a request: GET http://127.0.0.1:#{port}/", response.body end + it "runs an http server with a pidfile" do + args = [ + "--source", http_source, + "--target", "simple_http", + "--port", port, + "--pidfile", pidfile, + "-q" + ] + cli = FunctionsFramework::CLI.new.parse_args args + _response = run_with_retry cli do + Net::HTTP.get_response URI("http://127.0.0.1:#{port}/") + end + assert_equal pidfile, cli.pidfile + end + it "runs an http server with a function that includes a return" do args = [ "--source", http_return_source, diff --git a/test/test_server.rb b/test/test_server.rb index 1bd678dc..78eb8aee 100644 --- a/test/test_server.rb +++ b/test/test_server.rb @@ -38,6 +38,7 @@ end } let(:port) { 8077 } + let(:pidfile) { "server.pid" } let(:server_url) { "http://127.0.0.1:#{port}" } let(:quiet_logger) { logger = ::Logger.new $stderr @@ -56,6 +57,7 @@ def make_basic_server function, show_error_details: true config.min_threads = 1 config.max_threads = 1 config.port = port + config.pidfile = pidfile config.bind_addr = "127.0.0.1" config.rack_env = "development" config.logger = quiet_logger @@ -94,6 +96,19 @@ def query_server_with_retry server http_server.stop.wait_until_stopped timeout: 10 end + it "uses a pidfile" do + refute http_server.pidfile? + refute http_server.pidfile + http_server.start + assert http_server.pidfile? + assert http_server.pidfile + http_server.stop.wait_until_stopped timeout: 10 + refute http_server.running? + refute http_server.pidfile? + ensure + http_server.stop.wait_until_stopped timeout: 10 + end + it "handles post requests" do response = query_server_with_retry http_server do ::Net::HTTP.post URI("#{server_url}/"), "Hello, world!", "Content-Type" => "text/plain" @@ -188,7 +203,7 @@ def query_server_with_retry server end assert_equal "400", response.code - assert_equal "unexpected token at 'not json'", response.body + assert_match(/unexpected token/, response.body) assert_equal "text/plain; charset=utf-8", response["Content-Type"] end