Repository navigation
chore(stack): bump storage to v1.89.3-r0 (#7474) #12614
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: | |
| - develop | |
| - main | |
| - next | |
| # workflow_dispatch is the manual re-cut path. Use it when: | |
| # - a previous release published stale bytes under a version that | |
| # semantic-release keeps re-computing (cut a fresh version forward), or | |
| # - a downstream step (GH release, brew, scoop) failed after npm published | |
| # and you need to rerun the whole pipeline against an explicit version. | |
| # An empty version lets semantic-release compute it from the dispatched ref. `maintenance` | |
| # releases a v<N>.x line and must be dispatched on that ref. | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: Release channel | |
| required: true | |
| type: choice | |
| options: | |
| - beta | |
| - stable | |
| - next | |
| - maintenance | |
| version: | |
| description: npm package version to publish (must be unique on npm). Leave empty to let semantic-release compute it from the dispatched ref. | |
| required: false | |
| type: string | |
| default: "" | |
| # Defaults to `true` so a stray "Run workflow" click can't accidentally publish; operators | |
| # must consciously untick this when dispatching. | |
| dry_run: | |
| description: Dry run (skip actual publishing) | |
| required: false | |
| type: boolean | |
| default: true | |
| # The release pipeline mutates external registries, git tags, GitHub Releases, Homebrew, and | |
| # Scoop, so keep one release per ref active at a time to avoid duplicate push events racing. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| # semantic-release reads channel notes per commit (semantic-release/semantic-release#4073), so | |
| # releasing `next` on a commit develop already tagged misreads both tags. | |
| next-ahead: | |
| name: Check next has its own commits | |
| if: github.ref_name == 'next' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| outputs: | |
| ahead: ${{ steps.compare.outputs.ahead }} | |
| steps: | |
| - id: compare | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| ahead_by="$(gh api "repos/${REPO}/compare/develop...${SHA}" --jq .ahead_by)" | |
| if [[ "$ahead_by" -gt 0 ]]; then | |
| echo "ahead=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ahead=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| test-next: | |
| name: Test next | |
| needs: next-ahead | |
| if: github.event_name == 'push' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead == 'true' | |
| permissions: | |
| contents: read | |
| actions: read | |
| uses: ./.github/workflows/test.yml | |
| with: | |
| force: true | |
| secrets: | |
| DF_FIREWALL_TOKEN: ${{ secrets.DF_FIREWALL_TOKEN }} | |
| plan: | |
| name: Plan release | |
| needs: [next-ahead, test-next] | |
| # Skipped next-ahead and test-next jobs (every non-next trigger) make the implicit success() | |
| # false, so their results are checked explicitly. A next push with no commits beyond develop | |
| # plans nothing. | |
| if: >- | |
| ${{ !cancelled() && | |
| contains(fromJSON('["success","skipped"]'), needs.next-ahead.result) && | |
| contains(fromJSON('["success","skipped"]'), needs.test-next.result) && | |
| !(github.event_name == 'push' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead != 'true') }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 135 | |
| permissions: | |
| contents: read | |
| actions: read | |
| outputs: | |
| should_release: ${{ steps.compute.outputs.should_release }} | |
| version: ${{ steps.compute.outputs.version }} | |
| npm_tag: ${{ steps.compute.outputs.npm_tag }} | |
| prerelease: ${{ steps.compute.outputs.prerelease }} | |
| brew_name: ${{ steps.compute.outputs.brew_name }} | |
| scoop_name: ${{ steps.compute.outputs.scoop_name }} | |
| publish_brew_scoop: ${{ steps.compute.outputs.publish_brew_scoop }} | |
| dry_run: ${{ steps.compute.outputs.dry_run }} | |
| channel: ${{ steps.compute.outputs.channel }} | |
| steps: | |
| - name: Refuse a next dispatch with no commits beyond develop | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead != 'true' }} | |
| run: | | |
| echo "::error::next has no commits beyond develop, so a next release would tag a commit develop already released" | |
| exit 1 | |
| # The stable tag is pushed by main's publish job, so a prerelease planned before that job | |
| # finishes misses it. The rest of the run (brew, scoop, smoke tests) does not affect the plan. | |
| - name: Wait for an in-flight stable release | |
| if: ${{ (github.ref_name == 'develop' || github.ref_name == 'next') && (github.event_name == 'push' || inputs.version == '') }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| deadline=$((SECONDS + 7200)) | |
| while true; do | |
| pending=0 | |
| if run_ids="$(gh run list --repo "$REPO" --workflow release.yml --branch main --json databaseId,status --jq '.[] | select(.status != "completed") | .databaseId')"; then | |
| for run_id in $run_ids; do | |
| publish_status="$(gh api "repos/${REPO}/actions/runs/${run_id}/jobs" --paginate --jq '.jobs[] | select(.name == "Release / publish") | .status')" || publish_status=unknown | |
| if [[ "$publish_status" != completed ]]; then | |
| pending=$((pending + 1)) | |
| fi | |
| done | |
| else | |
| echo "::warning::Could not list Release runs on main; retrying." | |
| pending=1 | |
| fi | |
| if [[ "$pending" -eq 0 ]]; then | |
| break | |
| fi | |
| if (( SECONDS >= deadline )); then | |
| echo "::error::The publish job of a Release run on main is still in progress after 120 minutes. Re-run this job once it finishes." | |
| exit 1 | |
| fi | |
| echo "Waiting for the publish job of ${pending} Release run(s) on main to finish..." | |
| sleep 30 | |
| done | |
| # A major cut can fast-forward develop onto next while the tests and the wait above run. | |
| - id: next-still-ahead | |
| if: ${{ github.ref_name == 'next' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| ahead_by="$(gh api "repos/${REPO}/compare/develop...${SHA}" --jq .ahead_by)" | |
| if [[ "$ahead_by" -gt 0 ]]; then | |
| echo "ahead=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ahead=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # semantic-release runs `git push --dry-run HEAD:<branch>` as part of verifyAuth even in | |
| # `dry_run: true` mode, so the token needs push access to the protected develop/main | |
| # branches — the default GITHUB_TOKEN doesn't have it. | |
| - id: app-token | |
| if: github.event_name == 'push' || inputs.version == '' | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ vars.GH_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
| permission-contents: write | |
| # `persist-credentials: false` is required: otherwise checkout caches the default | |
| # GITHUB_TOKEN in git config, and its Authorization header overrides the App token | |
| # semantic-release puts in the push URL, making the dry-push get rejected by branch protection. | |
| - uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - id: semantic-release | |
| if: github.event_name == 'push' || inputs.version == '' | |
| uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0 | |
| with: | |
| working_directory: apps/cli | |
| dry_run: true | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| - id: compute | |
| env: | |
| EVENT: ${{ github.event_name }} | |
| REF_NAME: ${{ github.ref_name }} | |
| DISPATCH_CHANNEL: ${{ inputs.channel }} | |
| DISPATCH_VERSION: ${{ inputs.version }} | |
| DISPATCH_DRY_RUN: ${{ inputs.dry_run }} | |
| NEXT_AHEAD: ${{ steps.next-still-ahead.outputs.ahead }} | |
| SR_PUBLISHED: ${{ steps.semantic-release.outputs.new_release_published }} | |
| SR_VERSION: ${{ steps.semantic-release.outputs.new_release_version }} | |
| run: | | |
| set -euo pipefail | |
| maint_re='^v([0-9]+)\.x$' | |
| version_re='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$' | |
| plain_re='^[0-9]+\.[0-9]+\.[0-9]+$' | |
| next_re='^[0-9]+\.[0-9]+\.[0-9]+-next\.[0-9]+$' | |
| beta_re='^[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$' | |
| natural="" | |
| case "$REF_NAME" in | |
| develop) natural=beta ;; | |
| main) natural=stable ;; | |
| next) natural=next ;; | |
| *) [[ "$REF_NAME" =~ $maint_re ]] && natural=maintenance ;; | |
| esac | |
| if [[ "$REF_NAME" == next && "$NEXT_AHEAD" != true ]]; then | |
| if [[ "$EVENT" == "workflow_dispatch" ]]; then | |
| echo "::error::next has no commits beyond develop, so a next release would tag a commit develop already released" | |
| exit 1 | |
| fi | |
| echo "::notice::next has no commits beyond develop, so there is nothing to release" | |
| echo "should_release=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [[ "$EVENT" == "workflow_dispatch" ]]; then | |
| channel="$DISPATCH_CHANNEL" | |
| dry_run="$DISPATCH_DRY_RUN" | |
| if [[ "$natural" == maintenance && "$channel" != maintenance ]] || [[ "$natural" != maintenance && "$channel" == maintenance ]]; then | |
| echo "::error::channel=maintenance is only valid on v<N>.x refs, and v<N>.x refs only release the maintenance channel" | |
| exit 1 | |
| fi | |
| if [[ "$channel" == next && "$REF_NAME" != next ]]; then | |
| echo "::error::channel=next must be dispatched on the next branch" | |
| exit 1 | |
| fi | |
| if [[ "$natural" == next && "$channel" != next ]]; then | |
| echo "::error::${REF_NAME} only releases the next channel" | |
| exit 1 | |
| fi | |
| if [[ "$channel" == stable && "$dry_run" != true && "$REF_NAME" != main ]]; then | |
| echo "::error::A stable release must be dispatched on main; other refs may only dry-run" | |
| exit 1 | |
| fi | |
| if [[ -n "$DISPATCH_VERSION" ]]; then | |
| if [[ ! "$DISPATCH_VERSION" =~ $version_re ]]; then | |
| echo "::error::version ${DISPATCH_VERSION} is not a semver version" | |
| exit 1 | |
| fi | |
| case "$channel" in | |
| next) channel_re="$next_re"; shape="X.Y.Z-next.N" ;; | |
| beta) channel_re="$beta_re"; shape="X.Y.Z-beta.N" ;; | |
| *) channel_re="$plain_re"; shape="X.Y.Z" ;; | |
| esac | |
| if [[ ! "$DISPATCH_VERSION" =~ $channel_re ]]; then | |
| echo "::error::version ${DISPATCH_VERSION} does not match channel ${channel} (expected ${shape})" | |
| exit 1 | |
| fi | |
| version="$DISPATCH_VERSION" | |
| else | |
| if [[ "$channel" != "$natural" ]]; then | |
| echo "::error::An empty version uses semantic-release, so channel must match ${REF_NAME} (${natural:-unconfigured})" | |
| exit 1 | |
| fi | |
| if [[ "$SR_PUBLISHED" != "true" ]]; then | |
| echo "::error::semantic-release computed no release for ${REF_NAME}" | |
| exit 1 | |
| fi | |
| version="$SR_VERSION" | |
| fi | |
| should_release=true | |
| else | |
| dry_run=false | |
| if [[ "$SR_PUBLISHED" != "true" ]]; then | |
| echo "should_release=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| version="$SR_VERSION" | |
| channel="$natural" | |
| should_release=true | |
| fi | |
| case "$channel" in | |
| beta) | |
| npm_tag=beta | |
| prerelease=true | |
| brew_name=supabase-beta | |
| scoop_name=supabase-beta | |
| publish_brew_scoop=true | |
| ;; | |
| stable) | |
| npm_tag=latest | |
| prerelease=false | |
| brew_name=supabase | |
| scoop_name=supabase | |
| publish_brew_scoop=true | |
| ;; | |
| next) | |
| npm_tag=next | |
| prerelease=true | |
| brew_name="" | |
| scoop_name="" | |
| publish_brew_scoop=false | |
| ;; | |
| maintenance) | |
| [[ "$REF_NAME" =~ $maint_re ]] | |
| major="${BASH_REMATCH[1]}" | |
| if [[ "${version%%.*}" != "$major" ]]; then | |
| echo "::error::version ${version} is not on the v${major} line" | |
| exit 1 | |
| fi | |
| npm_tag="v${major}.stable" | |
| prerelease=false | |
| brew_name="supabase@${major}" | |
| scoop_name="supabase-v${major}-stable" | |
| publish_brew_scoop=true | |
| ;; | |
| *) | |
| echo "Unknown channel: $channel" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| { | |
| echo "should_release=$should_release" | |
| echo "version=$version" | |
| echo "npm_tag=$npm_tag" | |
| echo "prerelease=$prerelease" | |
| echo "brew_name=$brew_name" | |
| echo "scoop_name=$scoop_name" | |
| echo "publish_brew_scoop=$publish_brew_scoop" | |
| echo "dry_run=$dry_run" | |
| echo "channel=$channel" | |
| } >> "$GITHUB_OUTPUT" | |
| release: | |
| name: Release | |
| needs: plan | |
| if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.should_release == 'true' }} | |
| # pull-requests: write is required by the nested propose-release-notes workflow | |
| # (release-shared.yml -> propose-release-notes.yml): a called job's permissions can't exceed | |
| # those granted to the calling job, so this must be declared here too. | |
| permissions: | |
| actions: read | |
| attestations: write | |
| contents: write | |
| id-token: write | |
| pull-requests: write | |
| uses: ./.github/workflows/release-shared.yml | |
| with: | |
| version: ${{ needs.plan.outputs.version }} | |
| npm_tag: ${{ needs.plan.outputs.npm_tag }} | |
| prerelease: ${{ needs.plan.outputs.prerelease == 'true' }} | |
| publish_brew_scoop: ${{ needs.plan.outputs.publish_brew_scoop == 'true' }} | |
| brew_name: ${{ needs.plan.outputs.brew_name }} | |
| scoop_name: ${{ needs.plan.outputs.scoop_name }} | |
| dry_run: ${{ needs.plan.outputs.dry_run == 'true' }} | |
| channel: ${{ needs.plan.outputs.channel }} | |
| secrets: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} | |
| POSTHOG_ENDPOINT: ${{ secrets.POSTHOG_ENDPOINT }} | |
| GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| DF_FIREWALL_TOKEN: ${{ secrets.DF_FIREWALL_TOKEN }} | |
| LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY: ${{ secrets.LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY }} | |
| LINEAR_CLI_BETA_RELEASE_ACCESS_KEY: ${{ secrets.LINEAR_CLI_BETA_RELEASE_ACCESS_KEY }} | |
| SUPABASE_E2E_CLI_LIVE_STAGING_ACCESS_TOKEN: ${{ secrets.SUPABASE_E2E_CLI_LIVE_STAGING_ACCESS_TOKEN }} | |
| # Republishes the CLI reference and the config schemas to supabase.com. Stable only: both | |
| # document the CLI users actually install, so pre-releases and maintenance lines are skipped. | |
| # Nothing depends on this job, so a docs-site failure cannot affect the already-completed | |
| # release; a failed publish reaches Slack through notify-slack-publish-failure. | |
| docs: | |
| name: Publish to supabase.com | |
| needs: [plan, release] | |
| if: ${{ !cancelled() && needs.release.result == 'success' && needs.plan.outputs.channel == 'stable' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| continue-on-error: true | |
| outputs: | |
| failed: ${{ steps.report.outputs.failed }} | |
| steps: | |
| # Scoped to supabase/supabase: this job pushes a branch and opens a PR | |
| # there, and needs nothing beyond a plain read-only checkout here. | |
| - id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ vars.GH_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| repositories: | | |
| supabase | |
| permission-contents: write | |
| permission-pull-requests: write | |
| - uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup | |
| uses: ./.github/actions/setup | |
| with: | |
| dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} | |
| - name: Authenticate git for the docs push | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: gh auth setup-git | |
| - name: Publish the CLI reference | |
| id: reference | |
| continue-on-error: true | |
| working-directory: apps/cli | |
| # `shell: bash` for `-o pipefail`: the default `run:` shell takes only the publisher's | |
| # exit status, so a generator that died part-way would look like a successful run. | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| DRY_RUN: ${{ needs.plan.outputs.dry_run }} | |
| run: | | |
| args=() | |
| if [[ "$DRY_RUN" == "true" ]]; then | |
| args+=(--dry-run) | |
| fi | |
| bun scripts/generate-docs-spec.ts "$VERSION" \ | |
| | bun scripts/publish-docs-spec.ts --version "$VERSION" "${args[@]}" | |
| - name: Publish the config schemas | |
| id: schemas | |
| continue-on-error: true | |
| working-directory: apps/cli | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| VERSION: ${{ needs.plan.outputs.version }} | |
| DRY_RUN: ${{ needs.plan.outputs.dry_run }} | |
| run: | | |
| args=() | |
| if [[ "$DRY_RUN" == "true" ]]; then | |
| args+=(--dry-run) | |
| fi | |
| bun scripts/publish-schemas.ts --version "$VERSION" "${args[@]}" | |
| - name: Report failed publishes | |
| id: report | |
| if: ${{ !cancelled() }} | |
| env: | |
| REFERENCE_OUTCOME: ${{ steps.reference.outcome }} | |
| SCHEMAS_OUTCOME: ${{ steps.schemas.outcome }} | |
| run: | | |
| failed=false | |
| report() { | |
| if [[ "$2" != "success" ]]; then | |
| failed=true | |
| echo "::error title=Publishing the $1 failed::The $1 was not published to supabase.com (step outcome: $2)." | |
| echo "- The $1 was not published to supabase.com (step outcome: $2)." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| } | |
| report "CLI reference" "$REFERENCE_OUTCOME" | |
| report "config schemas" "$SCHEMAS_OUTCOME" | |
| echo "failed=$failed" >> "$GITHUB_OUTPUT" | |
| if [[ "$failed" == "true" ]]; then | |
| exit 1 | |
| fi | |
| # Notifies real (non-dry-run) stable and maintenance cuts. Nothing depends on this job, so a | |
| # Slack failure can't affect the already-completed release. | |
| notify-slack: | |
| name: Notify Slack | |
| needs: [plan, release] | |
| if: >- | |
| ${{ !cancelled() && | |
| needs.release.result == 'success' && | |
| needs.plan.outputs.dry_run != 'true' && | |
| (needs.plan.outputs.channel == 'stable' || needs.plan.outputs.channel == 'maintenance') }} | |
| uses: ./.github/workflows/slack-notify.yml | |
| with: | |
| package: Supabase CLI | |
| status: success | |
| version: ${{ needs.plan.outputs.version }} | |
| channel: ${{ needs.plan.outputs.channel }} | |
| secrets: | |
| SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }} | |
| # Pages when the release shipped but the supabase.com publish did not. The docs job's | |
| # continue-on-error hides its own failures, so its report step exports `failed`; a job that ran | |
| # without exporting `failed=false` (timeout, crash before the report step) counts as failed. | |
| notify-slack-publish-failure: | |
| name: Notify Slack (publish failure) | |
| needs: [plan, docs] | |
| if: ${{ !cancelled() && needs.plan.outputs.dry_run != 'true' && needs.docs.result != 'skipped' && needs.docs.outputs.failed != 'false' }} | |
| uses: ./.github/workflows/slack-notify.yml | |
| with: | |
| package: Supabase CLI | |
| status: publish-failure | |
| version: ${{ needs.plan.outputs.version }} | |
| channel: ${{ needs.plan.outputs.channel }} | |
| secrets: | |
| SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }} | |
| # Reports a broken release on every channel. `failure()` evaluates against the full `needs` | |
| # chain, so this fires whenever `next-ahead`, `test-next`, `plan` or `release` (including the | |
| # reusable release-shared workflow) fails. Skipped jobs don't count as failures. Dry runs are | |
| # excluded via the dispatch input directly, so even a plan job that dies before recording its | |
| # `dry_run` output stays quiet. A failed `next-ahead` or `test-next` skips `plan`, so the | |
| # channel falls back to the ref. | |
| notify-slack-failure: | |
| name: Notify Slack (failure) | |
| needs: [next-ahead, test-next, plan, release] | |
| if: ${{ failure() && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) }} | |
| uses: ./.github/workflows/slack-notify.yml | |
| with: | |
| package: Supabase CLI | |
| status: failure | |
| version: ${{ needs.plan.outputs.version }} | |
| channel: ${{ needs.plan.outputs.channel || (github.ref_name == 'next' && 'next') || '' }} | |
| secrets: | |
| SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }} |