diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e58bd32eb0..8dbdf4c6b1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @supabase/cli +* @supabase/cli \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000000..dec2cba28e --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,58 @@ +name: Setup + +description: Perform standard setup and install dependencies using pnpm + +runs: + using: "composite" + steps: + - name: Resolve Bun version + shell: bash + run: echo "BUN_VERSION=1.3.13" >> "$GITHUB_ENV" + + - name: Restore Bun toolchain cache + id: bun-toolchain-cache + uses: actions/cache@v4 + with: + path: /opt/hostedtoolcache/bun + key: bun-toolchain-${{ runner.os }}-${{ runner.arch }}-${{ env.BUN_VERSION }} + + - name: Install Bun + id: install-bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + continue-on-error: true + with: + bun-version: ${{ env.BUN_VERSION }} + + - name: Install Bun (fallback with retries) + if: steps.install-bun.outcome == 'failure' + uses: nick-fields/retry@v3 + with: + timeout_minutes: 3 + max_attempts: 5 + retry_wait_seconds: 30 + command: | + curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" + echo "$HOME/.bun/bin" >> "$GITHUB_PATH" + + - name: Verify Bun + shell: bash + run: bun --version + + - name: Install Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 + with: + node-version-file: .nvmrc + package-manager-cache: false + + - name: Enable Corepack + shell: bash + run: npm install --global --force corepack && corepack enable + + - name: Configure dependency cache + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 + with: + cache: pnpm + + - name: Install dependencies + shell: bash + run: pnpm install --frozen-lockfile \ No newline at end of file diff --git a/.github/workflows/api-sync.yml b/.github/workflows/cli-go-api-sync.yml similarity index 95% rename from .github/workflows/api-sync.yml rename to .github/workflows/cli-go-api-sync.yml index 28fe129a5d..8500797adf 100644 --- a/.github/workflows/api-sync.yml +++ b/.github/workflows/cli-go-api-sync.yml @@ -5,8 +5,6 @@ on: types: - api-sync workflow_dispatch: # allow manual triggering - -# Add explicit permissions permissions: contents: write pull-requests: write @@ -20,7 +18,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true - name: Run codegen @@ -70,3 +68,6 @@ jobs: run: gh pr merge --auto --squash "${{ steps.cpr.outputs.pull-request-number }}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/ci.yml b/.github/workflows/cli-go-ci.yml similarity index 87% rename from .github/workflows/ci.yml rename to .github/workflows/cli-go-ci.yml index 5ede9e3005..850300b879 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/cli-go-ci.yml @@ -2,10 +2,16 @@ name: CI on: pull_request: + paths: + - apps/cli-go/** merge_group: + paths: + - apps/cli-go/** push: branches: - develop + paths: + - apps/cli-go/** permissions: contents: read @@ -19,7 +25,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true # Required by: internal/utils/credentials/keyring_test.go @@ -32,7 +38,7 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: code-coverage-report - path: coverage.out + path: apps/cli-go/coverage.out coverage: name: Coverage @@ -56,7 +62,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod # Linter requires no cache cache: false @@ -65,6 +71,7 @@ jobs: args: --timeout 5m --verbose version: latest only-new-issues: true + working-directory: apps/cli-go start: name: Start @@ -73,7 +80,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true - run: go build main.go - run: ./main init @@ -90,13 +97,14 @@ jobs: link: name: Link - if: ${{ github.event_name == 'merge_group' || (github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork) }} + if: ${{ github.event_name == 'merge_group' || (github.event_name == + 'pull_request' && !github.event.pull_request.head.repo.fork) }} runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true - run: go build main.go - run: ./main link @@ -112,7 +120,7 @@ jobs: - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true - run: go generate @@ -122,3 +130,6 @@ jobs: git diff exit 1 fi +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/cli-go-codeql.yml similarity index 86% rename from .github/workflows/codeql-analysis.yml rename to .github/workflows/cli-go-codeql.yml index 168bd09203..1c29daa44a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/cli-go-codeql.yml @@ -13,10 +13,16 @@ name: "CodeQL" on: pull_request: + paths: + - apps/cli-go/** merge_group: + paths: + - apps/cli-go/** push: branches: - develop + paths: + - apps/cli-go/** jobs: analyze: @@ -72,12 +78,12 @@ jobs: # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # â„šī¸ 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 + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ 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 - if: matrix.build-mode == 'manual' shell: bash run: | @@ -92,3 +98,6 @@ jobs: uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: category: "/language:${{matrix.language}}" +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/mirror-image.yml b/.github/workflows/cli-go-mirror-image.yml similarity index 94% rename from .github/workflows/mirror-image.yml rename to .github/workflows/cli-go-mirror-image.yml index d3e34f2bf3..92250bd79f 100644 --- a/.github/workflows/mirror-image.yml +++ b/.github/workflows/cli-go-mirror-image.yml @@ -15,6 +15,8 @@ on: description: "org/image:tag" required: true type: string + paths: + - apps/cli-go/** permissions: contents: read @@ -48,3 +50,6 @@ jobs: dst: | public.ecr.aws/supabase/${{ steps.strip.outputs.image }} ghcr.io/supabase/${{ steps.strip.outputs.image }} +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/mirror.yml b/.github/workflows/cli-go-mirror.yml similarity index 93% rename from .github/workflows/mirror.yml rename to .github/workflows/cli-go-mirror.yml index df93dac907..ad6e3d5d2f 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/cli-go-mirror.yml @@ -15,7 +15,8 @@ on: # # TODO: Make the cli start test run *after* we mirror images (if needed). workflow_dispatch: - + paths: + - apps/cli-go/** permissions: contents: read @@ -29,7 +30,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: go.mod + go-version-file: apps/cli-go/go.mod cache: true - id: list run: | @@ -58,3 +59,6 @@ jobs: with: image: ${{ matrix.src }} secrets: inherit +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/pg-prove.yml b/.github/workflows/cli-go-pg-prove.yml similarity index 94% rename from .github/workflows/pg-prove.yml rename to .github/workflows/cli-go-pg-prove.yml index 4d79abd601..792485e709 100644 --- a/.github/workflows/pg-prove.yml +++ b/.github/workflows/cli-go-pg-prove.yml @@ -2,7 +2,8 @@ name: Publish pg_prove on: workflow_dispatch: - + paths: + - apps/cli-go/** permissions: contents: read @@ -33,7 +34,7 @@ jobs: strategy: matrix: include: - - runner: [self-hosted, X64] + - runner: [ self-hosted, X64 ] arch: amd64 - runner: arm-runner arch: arm64 @@ -58,7 +59,8 @@ jobs: tags: ${{ needs.settings.outputs.image_tag }}_${{ matrix.arch }} platforms: linux/${{ matrix.arch }} cache-from: type=gha,scope=${{ github.ref_name }}-pg_prove-${{ matrix.arch }} - cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-pg_prove-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-pg_prove-${{ + matrix.arch }} merge_manifest: needs: @@ -86,3 +88,6 @@ jobs: with: image: ${{ needs.settings.outputs.image_tag }} secrets: inherit +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/publish-migra.yml b/.github/workflows/cli-go-publish-migra.yml similarity index 94% rename from .github/workflows/publish-migra.yml rename to .github/workflows/cli-go-publish-migra.yml index 65debc77e9..13caa28941 100644 --- a/.github/workflows/publish-migra.yml +++ b/.github/workflows/cli-go-publish-migra.yml @@ -2,7 +2,8 @@ name: Publish migra on: workflow_dispatch: - + paths: + - apps/cli-go/** permissions: contents: read @@ -33,7 +34,7 @@ jobs: strategy: matrix: include: - - runner: [self-hosted, X64] + - runner: [ self-hosted, X64 ] arch: amd64 - runner: arm-runner arch: arm64 @@ -58,7 +59,8 @@ jobs: tags: ${{ needs.settings.outputs.image_tag }}_${{ matrix.arch }} platforms: linux/${{ matrix.arch }} cache-from: type=gha,scope=${{ github.ref_name }}-migra-${{ matrix.arch }} - cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-migra-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-migra-${{ matrix.arch + }} merge_manifest: needs: @@ -86,3 +88,6 @@ jobs: with: image: ${{ needs.settings.outputs.image_tag }} secrets: inherit +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/tag-pkg.yml b/.github/workflows/cli-go-tag-pkg.yml similarity index 91% rename from .github/workflows/tag-pkg.yml rename to .github/workflows/cli-go-tag-pkg.yml index 8eaf266109..bf6c625585 100644 --- a/.github/workflows/tag-pkg.yml +++ b/.github/workflows/cli-go-tag-pkg.yml @@ -7,6 +7,8 @@ on: description: "pkg version to tag (e.g. v1.2.2)" required: true type: string + paths: + - apps/cli-go/** permissions: contents: write @@ -35,3 +37,6 @@ jobs: fi git tag "$TAG" git push origin "$TAG" +defaults: + run: + working-directory: apps/cli-go diff --git a/.github/workflows/deploy-check.yml b/.github/workflows/deploy-check.yml index 3715958a00..8db23a6e55 100644 --- a/.github/workflows/deploy-check.yml +++ b/.github/workflows/deploy-check.yml @@ -10,7 +10,7 @@ on: branches: - main -permissions: +permissions: contents: read jobs: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 61ee1f603e..77b8c23ffe 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - id: app-token diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml deleted file mode 100644 index 1eabd1aedf..0000000000 --- a/.github/workflows/install.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: Install - -on: - pull_request: - paths: - - ".github/workflows/install.yml" - - "package.json" - - "scripts/**" - push: - branches: - - develop - paths: - - ".github/workflows/install.yml" - - "package.json" - - "scripts/**" - -permissions: - contents: read - -jobs: - pack: - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - run: | - jq -c '.version = "1.28.0"' package.json > tmp.$$.json - mv tmp.$$.json package.json - npm pack - - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: installer - path: supabase-1.28.0.tgz - - npm: - needs: pack - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: installer - - - run: npm init -y - - run: npm i --save-dev ./supabase-1.28.0.tgz - - run: npx --no-install supabase --version - - yarn: - needs: pack - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: installer - - - run: yarn init -y - - run: yarn add -D ./supabase-1.28.0.tgz - - run: yarn supabase --version - - yarn_berry: - needs: pack - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: installer - - - run: yarn set version berry - # - run: yarn config set nodeLinker node-modules - - run: yarn init -y - # Yarn Berry 4.14+ disables install scripts by default (yarnpkg/berry#7089). - # The supabase package relies on a postinstall script to fetch its binary, - # so we opt in via YARN_ENABLE_SCRIPTS just for this install step (the - # Yarn analog to pnpm's --allow-build=supabase). - - run: yarn add -D ./supabase-1.28.0.tgz - env: - YARN_ENABLE_SCRIPTS: "true" - - if: ${{ matrix.os != 'windows-latest' }} - run: yarn supabase --version - # Workaround for running extensionless executable on windows - - if: ${{ matrix.os == 'windows-latest' }} - run: | - & "$(yarn bin supabase).exe" --version - - pnpm: - needs: pack - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: installer - - - run: npm install -g pnpm - - run: pnpm init - # https://github.com/pnpm/pnpm/issues/9124#issuecomment-2663021284 - - run: pnpm i --save-dev ./supabase-1.28.0.tgz --allow-build=supabase - - run: pnpm supabase --version - - bun: - needs: pack - strategy: - fail-fast: false - matrix: - # Bun build is experimental on windows - os: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: installer - - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - run: | - echo '{"trustedDependencies": ["supabase"]}' > package.json - - run: bun add -D ./supabase-1.28.0.tgz - - run: bunx supabase --version diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml new file mode 100644 index 0000000000..4d24fa4509 --- /dev/null +++ b/.github/workflows/lint-pull-request.yml @@ -0,0 +1,35 @@ +name: Lint Pull Request + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + - reopened + - ready_for_review + merge_group: + types: + - checks_requested + branches: + - main + +permissions: + pull-requests: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + main: + if: github.event_name != 'pull_request_target' || github.event.pull_request.draft == false + name: Lint Pull Request + runs-on: ubuntu-latest + steps: + - if: github.event_name == 'pull_request_target' + uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - if: github.event_name == 'merge_group' + run: echo "Merge queue entry does not have a pull request payload; reporting success for the required lint check." \ No newline at end of file diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml deleted file mode 100644 index b529f8db26..0000000000 --- a/.github/workflows/release-beta.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: Release (Beta) - -on: - push: - branches: - - develop - workflow_dispatch: - -permissions: - contents: read - -jobs: - release: - name: semantic-release - runs-on: ubuntu-latest - permissions: - contents: write - outputs: - new-release-published: ${{ steps.semantic-release.outputs.new_release_published }} - new-release-version: ${{ steps.semantic-release.outputs.new_release_version }} - new-release-channel: ${{ steps.semantic-release.outputs.new_release_channel }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - id: semantic-release - uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - goreleaser: - name: GoReleaser - needs: - - release - if: needs.release.outputs.new-release-published == 'true' - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - cache: true - - - uses: goreleaser/goreleaser-action@e24998b8b67b290c2fa8b7c14fcfa7de2c5c9b8c # v7.1.0 - with: - distribution: goreleaser - version: ~> v2 - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} - POSTHOG_ENDPOINT: ${{ secrets.POSTHOG_ENDPOINT }} - - - run: gh release edit v${{ needs.release.outputs.new-release-version }} --draft=false --prerelease - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - commit: - name: Publish Brew and Scoop - needs: - - release - - goreleaser - if: needs.release.outputs.new-release-published == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - cache: true - # use GitHub app to create a release token that can publish to homebrew-tap and scoop - - name: Generate token - id: app-token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - homebrew-tap - scoop-bucket - - run: go run tools/publish/main.go --beta "${{ needs.release.outputs.new-release-version }}" - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - - publish: - name: Publish NPM - needs: - - release - - goreleaser - if: needs.release.outputs.new-release-published == 'true' - permissions: - contents: read - id-token: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: latest - registry-url: https://registry.npmjs.org - - run: npm --git-tag-version=false version ${{ needs.release.outputs.new-release-version }} - - run: npm publish --tag ${{ needs.release.outputs.new-release-channel }} diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml new file mode 100644 index 0000000000..b9b41b6f5f --- /dev/null +++ b/.github/workflows/release-shared.yml @@ -0,0 +1,309 @@ +name: Release Shared + +on: + workflow_call: + inputs: + version: + description: npm package version to publish + required: true + type: string + shell: + description: CLI shell to package as the shipped supabase binary + required: true + type: string + npm_tag: + description: npm dist-tag to publish under + required: true + type: string + channel: + description: semantic-release channel name (alpha | beta | stable) + required: true + type: string + prerelease: + description: Whether the GitHub release should be marked as a prerelease + required: true + type: boolean + dry_run: + description: Dry run (skip actual publishing) + required: true + type: boolean + publish_brew_scoop: + description: Whether to update the Homebrew tap and Scoop bucket + required: false + type: boolean + default: false + brew_name: + description: Homebrew formula name (e.g. supabase or supabase-beta) + required: false + type: string + default: supabase + scoop_name: + description: Scoop manifest name (e.g. supabase or supabase-beta) + required: false + type: string + default: supabase + +jobs: + build: + runs-on: large-linux-x86 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Setup Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: apps/cli-go/go.mod + cache: true + cache-dependency-path: apps/cli-go/go.sum + + - name: Pre-download Go modules + working-directory: apps/cli-go + run: go mod download -x + + - name: Install nfpm + run: | + echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list + sudo apt-get update + sudo apt-get install -y nfpm + + - name: Sync versions + run: pnpm exec bun apps/cli/scripts/sync-versions.ts --version ${{ inputs.version }} + + - name: Build selected shell + run: pnpm exec bun apps/cli/scripts/build.ts --version ${{ inputs.version }} --shell ${{ inputs.shell }} + + - name: Verify build artifacts + run: | + for pkg in cli-darwin-arm64 cli-darwin-x64 cli-linux-arm64 cli-linux-arm64-musl cli-linux-x64 cli-linux-x64-musl cli-windows-arm64 cli-windows-x64; do + echo "Checking packages/$pkg/bin/..." + ls -la "packages/$pkg/bin/" + done + echo "Checking dist/..." + ls -la dist/ + + - name: Upload build artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: cli-build-${{ inputs.shell }}-${{ inputs.version }} + path: | + packages/cli-*/bin/ + dist/ + + smoke-test: + needs: build + strategy: + fail-fast: false + matrix: + runner: [ubuntu-latest, macos-latest, macos-15-intel, windows-latest] + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Download build artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: cli-build-${{ inputs.shell }}-${{ inputs.version }} + + - name: Setup QEMU for cross-platform Docker + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + + - name: Install Scoop + if: runner.os == 'Windows' + shell: pwsh + run: | + iex "& {$(irm get.scoop.sh)} -RunAsAdmin" + Join-Path (Resolve-Path ~).Path "scoop\shims" >> $env:GITHUB_PATH + + - name: Fix binary permissions + if: runner.os != 'Windows' + run: chmod +x packages/cli-*/bin/supabase || true + + - name: Run smoke tests + run: pnpm run test:smoke -- --version ${{ inputs.version }} --tag ${{ inputs.npm_tag }} + working-directory: apps/cli + + publish: + needs: smoke-test + if: ${{ !inputs.dry_run }} + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Download build artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: cli-build-${{ inputs.shell }}-${{ inputs.version }} + + - name: Sync versions + run: pnpm exec bun apps/cli/scripts/sync-versions.ts --version ${{ inputs.version }} + + - name: Publish to npm + run: pnpm exec bun apps/cli/scripts/publish.ts --tag ${{ inputs.npm_tag }} + + # Push the version tag to origin as soon as npm has the bytes, before any + # downstream step that can fail. Without this, a failure in the GH-release + # step (or anything after) leaves origin with no tag for the version that + # is now live on npm — and `semantic-release --dry-run` on the next push + # will keep recomputing the same version, causing the publish job to no-op + # against stale bytes. Idempotent: skips push if the tag is already on + # origin (e.g. a re-run of a job that previously got past this step). + - name: Push version tag + run: | + set -euo pipefail + tag="v${{ inputs.version }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + if git ls-remote --tags origin "refs/tags/${tag}" | grep -q .; then + echo "Tag ${tag} already on origin; skipping push." + else + git tag -a "${tag}" -m "Release ${tag}" + git push origin "${tag}" + fi + + # semantic-release matches tags against branch.channel using git-notes + # under refs/notes/semantic-release-. Without this note, the develop + # plan job recomputes the same prerelease version every push (see + # docs/ci-beta-tags-investigation.md). Stable releases on main don't + # need this — main's branch config has no explicit channel. + # + # The note must be attached to the underlying commit, not the annotated + # tag object created by "Push version tag" above. semantic-release reads + # notes via `git log --no-walk --tags=* --notes=...`, which looks for + # notes on commits; a note on the tag object is invisible to it and + # leaves the tag with channels:[null], so the prerelease arm of + # getLastRelease's filter rejects it and the plan falls back to the + # last stable. Hence the `${tag}^{commit}` deref. + - name: Push channel note for prerelease tags + if: ${{ inputs.prerelease }} + run: | + set -euo pipefail + tag="v${{ inputs.version }}" + channel="${{ inputs.channel }}" + note_ref="refs/notes/semantic-release-${tag}" + if git ls-remote origin "${note_ref}" | grep -q .; then + echo "Channel note ${note_ref} already on origin; skipping push." + else + git notes --ref="${note_ref}" add -f -m "{\"channels\":[\"${channel}\"]}" "${tag}^{commit}" + git push origin "${note_ref}" + fi + + - name: Create draft GitHub Release + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2 + with: + tag_name: v${{ inputs.version }} + name: v${{ inputs.version }} + draft: true + prerelease: ${{ inputs.prerelease }} + files: | + dist/supabase_${{ inputs.version }}_darwin_arm64.tar.gz + dist/supabase_${{ inputs.version }}_darwin_amd64.tar.gz + dist/supabase_${{ inputs.version }}_linux_arm64.tar.gz + dist/supabase_${{ inputs.version }}_linux_amd64.tar.gz + dist/supabase_${{ inputs.version }}_linux_arm64.deb + dist/supabase_${{ inputs.version }}_linux_amd64.deb + dist/supabase_${{ inputs.version }}_linux_arm64.rpm + dist/supabase_${{ inputs.version }}_linux_amd64.rpm + dist/supabase_${{ inputs.version }}_linux_arm64.apk + dist/supabase_${{ inputs.version }}_linux_amd64.apk + dist/supabase_${{ inputs.version }}_windows_amd64.zip + dist/supabase_${{ inputs.version }}_windows_arm64.zip + dist/checksums.txt + + - name: Publish GitHub Release (immutable) + env: + GH_TOKEN: ${{ github.token }} + run: gh release edit v${{ inputs.version }} --draft=false + + publish-homebrew: + needs: publish + if: ${{ !inputs.dry_run && inputs.publish_brew_scoop }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Download build artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: cli-build-${{ inputs.shell }}-${{ inputs.version }} + + - name: Generate Homebrew tap token + id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: | + homebrew-tap + + - name: Configure git for tap push + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh auth setup-git + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Update Homebrew formula + run: pnpm exec bun apps/cli/scripts/update-homebrew.ts --version ${{ inputs.version }} --name ${{ inputs.brew_name }} + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + + publish-scoop: + needs: publish + if: ${{ !inputs.dry_run && inputs.publish_brew_scoop }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Download build artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: cli-build-${{ inputs.shell }}-${{ inputs.version }} + + - name: Generate Scoop bucket token + id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: | + scoop-bucket + + - name: Configure git for bucket push + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh auth setup-git + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Update Scoop manifest + run: pnpm exec bun apps/cli/scripts/update-scoop.ts --version ${{ inputs.version }} --name ${{ inputs.scoop_name }} + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1fba8c4ae8..a58c2d839a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,133 +1,203 @@ name: Release on: + push: + branches: + - develop + - main pull_request_review: - types: - - submitted + types: [submitted] + # 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. + workflow_dispatch: + inputs: + channel: + description: Release channel + required: true + type: choice + options: + - alpha + - beta + - stable + version: + description: npm package version to publish (must be unique on npm; pick the next unused version) + required: true + type: string + # Defaults to `true` so a stray "Run workflow" click can't accidentally + # publish — operators recovering from a stale-bytes run must consciously + # untick this when dispatching. + dry_run: + description: Dry run (skip actual publishing) + required: false + type: boolean + default: true permissions: contents: read jobs: fast-forward: + name: Fast-forward develop to main if: | + github.event_name == 'pull_request_review' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main' && github.event.review.state == 'approved' runs-on: ubuntu-latest permissions: contents: write - packages: write - outputs: - release_tag: ${{ steps.latest-release.outputs.tagName }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - id: app-token + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - - run: | + token: ${{ steps.app-token.outputs.token }} + - name: Fast-forward main + run: | git checkout main git merge --ff-only "${{ github.event.pull_request.head.sha }}" git push origin main - - id: latest-release - run: | - latest=$(gh release list --limit 1 --json tagName --jq '.[].tagName') - gh release edit $latest --latest --prerelease=false - echo "tagName=$latest" >> $GITHUB_OUTPUT - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - commit: - name: Publish Brew and Scoop - needs: - - fast-forward + plan: + name: Plan release + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + outputs: + should_release: ${{ steps.compute.outputs.should_release }} + version: ${{ steps.compute.outputs.version }} + shell: ${{ steps.compute.outputs.shell }} + 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: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - cache: true + # semantic-release runs `git push --dry-run HEAD:` as part of + # verifyAuth even in `dry_run: true` mode, so the token must have push + # access to the protected `develop`/`main` branches. The default + # GITHUB_TOKEN doesn't, so we mint an App-installation token from the + # same App used for fast-forward + brew/scoop pushes. - id: app-token + if: github.event_name == 'push' uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - homebrew-tap - scoop-bucket - - run: go run tools/publish/main.go ${{ needs.fast-forward.outputs.release_tag }} - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - - compose: - name: Bump self-hosted versions - needs: - - fast-forward - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + # `persist-credentials: false` is required: otherwise checkout caches the + # default GITHUB_TOKEN as an `http.extraheader` in git config, and that + # Authorization header overrides the App token semantic-release puts in + # the push URL — making the dry-push identify as `github-actions[bot]` + # and get rejected by branch protection. + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - go-version-file: go.mod - cache: true - - id: app-token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + fetch-depth: 0 + persist-credentials: false + - id: semantic-release + if: github.event_name == 'push' + uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0 with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - supabase - - run: go run tools/selfhost/main.go + working_directory: apps/cli + dry_run: true env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - - changelog: - name: Publish changelog - needs: - - fast-forward - - commit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - cache: true - - id: app-token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - supabase - cli - - run: go run tools/changelog/main.go ${{ secrets.SLACK_CHANNEL }} + - id: compute env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + EVENT: ${{ github.event_name }} + REF_NAME: ${{ github.ref_name }} + DISPATCH_CHANNEL: ${{ inputs.channel }} + DISPATCH_VERSION: ${{ inputs.version }} + DISPATCH_DRY_RUN: ${{ inputs.dry_run }} + SR_PUBLISHED: ${{ steps.semantic-release.outputs.new_release_published }} + SR_VERSION: ${{ steps.semantic-release.outputs.new_release_version }} + run: | + set -euo pipefail + if [[ "$EVENT" == "workflow_dispatch" ]]; then + channel="$DISPATCH_CHANNEL" + version="$DISPATCH_VERSION" + dry_run="$DISPATCH_DRY_RUN" + should_release=true + else + dry_run=false + if [[ "$SR_PUBLISHED" != "true" ]]; then + echo "should_release=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + version="$SR_VERSION" + should_release=true + if [[ "$REF_NAME" == "develop" ]]; then + channel="beta" + else + channel="stable" + fi + fi + case "$channel" in + alpha) + shell=next + npm_tag=alpha + prerelease=true + brew_name="" + scoop_name="" + publish_brew_scoop=false + ;; + beta) + shell=legacy + npm_tag=beta + prerelease=true + brew_name=supabase-beta + scoop_name=supabase-beta + publish_brew_scoop=true + ;; + stable) + shell=legacy + npm_tag=latest + prerelease=false + brew_name=supabase + scoop_name=supabase + publish_brew_scoop=true + ;; + *) + echo "Unknown channel: $channel" >&2 + exit 1 + ;; + esac + { + echo "should_release=$should_release" + echo "version=$version" + echo "shell=$shell" + 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" - docs: - name: Publish reference docs - needs: - - fast-forward - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - cache: true - - id: app-token - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - supabase - - run: go run docs/main.go ${{ needs.fast-forward.outputs.release_tag }} | go run tools/bumpdoc/main.go apps/docs/spec/cli_v1_commands.yaml - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + release: + name: Release + needs: plan + if: needs.plan.outputs.should_release == 'true' + permissions: + contents: write + id-token: write + uses: ./.github/workflows/release-shared.yml + with: + version: ${{ needs.plan.outputs.version }} + shell: ${{ needs.plan.outputs.shell }} + 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: inherit diff --git a/.github/workflows/smoke-test-pr.yml b/.github/workflows/smoke-test-pr.yml new file mode 100644 index 0000000000..4879139593 --- /dev/null +++ b/.github/workflows/smoke-test-pr.yml @@ -0,0 +1,61 @@ +name: Smoke Test (PR) + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + branches: + - develop + # Trigger on any change that could affect the build phase or how the + # built artifacts behave at runtime. Anything in this list is something + # `release-shared.yml`'s build/smoke jobs need to re-validate. + paths: + - "apps/cli/scripts/build.ts" + - "apps/cli/scripts/sync-versions.ts" + - "apps/cli/src/**" + - "apps/cli/package.json" + - "apps/cli-go/**" + - "packages/cli-*/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - ".github/actions/setup/**" + +permissions: + # release-shared.yml's `publish` job declares `contents: write` and + # `id-token: write`. Even though that job is gated by `!inputs.dry_run` + # and never runs here, GitHub validates nested-workflow permissions at + # startup and rejects the run if the caller grants less than any + # nested job requests. Granting the superset here is safe because (a) + # `dry_run: true` short-circuits the privileged jobs at runtime and + # (b) for fork PRs GitHub still issues a read-only GITHUB_TOKEN + # regardless of the declared scope. + contents: write + id-token: write + actions: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + smoke: + if: github.event.pull_request.draft == false + uses: ./.github/workflows/release-shared.yml + with: + # PR-scoped version so concurrent PRs don't collide on the build artifact + # name (release-shared.yml uploads `cli-build-${shell}-${version}`). + version: 0.0.0-pr-${{ github.event.pull_request.number }} + shell: legacy + npm_tag: latest + prerelease: true + dry_run: true + # release-shared.yml's publish/homebrew/scoop jobs reference + # `secrets.APP_ID` and `secrets.GH_APP_PRIVATE_KEY`. They are gated by + # `!inputs.dry_run` and never execute here, but GitHub validates secret + # references at startup, so the called workflow needs the secrets bag + # propagated even when the jobs that use them are skipped. + secrets: inherit diff --git a/.github/workflows/tag-npm.yml b/.github/workflows/tag-npm.yml deleted file mode 100644 index 6206b422ec..0000000000 --- a/.github/workflows/tag-npm.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Tag NPM - -on: - workflow_call: - inputs: - release: - required: true - type: string - workflow_dispatch: - inputs: - release: - description: "v1.0.0" - required: true - type: string - -permissions: - contents: read - id-token: write - -jobs: - tag: - name: Move latest tag - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: latest - registry-url: https://registry.npmjs.org - - - run: npm dist-tag add "supabase@${RELEASE_TAG#v}" latest - env: - RELEASE_TAG: ${{ inputs.release }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..39abd9b08c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,184 @@ +name: Test + +on: + push: + branches: + - develop + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + branches: + - develop + +permissions: + contents: read + actions: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + check: + if: github.event.pull_request.draft == false || github.event_name == 'push' + name: Check code quality + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: apps/cli-go/go.mod + cache-dependency-path: apps/cli-go/go.sum + - name: Install golangci-lint + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest && + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + - name: Unlock keyring (for cli-go keyring tests) + uses: t1m0thyj/unlock-keyring@cbcf205c879ebd86add70bab3a6abfcce59a5cae + + - name: Check code quality + run: pnpm run check:all + + test-core: + if: github.event.pull_request.draft == false || github.event_name == 'push' + name: Run unit and integration tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup + uses: ./.github/actions/setup + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: apps/cli-go/go.mod + cache-dependency-path: apps/cli-go/go.sum + - name: Install golangci-lint + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest && + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + - name: Unlock keyring (for cli-go keyring tests) + uses: t1m0thyj/unlock-keyring@cbcf205c879ebd86add70bab3a6abfcce59a5cae + + - name: Run unit and integration tests + run: pnpm run test:core + + test-e2e: + if: github.event.pull_request.draft == false || github.event_name == 'push' + name: Run end-to-end tests (shard ${{ matrix.shard }}/3) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [ 1, 2, 3 ] + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Set base and head SHAs for affected + if: github.event_name == 'pull_request' + uses: nrwl/nx-set-shas@v4 + + - name: Setup + uses: ./.github/actions/setup + + # Detect which e2e suites should run. On PR we honour `nx affected` + # (using `NX_BASE`/`NX_HEAD` set by `nx-set-shas` above); on push we run + # everything that has a `test:e2e` target. + - name: Detect affected e2e projects + id: detect + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + affected=$(pnpm exec nx show projects --affected --withTarget test:e2e --json | jq -r '.[]') + else + affected=$(pnpm exec nx show projects --withTarget test:e2e --json | jq -r '.[]') + fi + echo "Affected e2e projects:" + echo "$affected" + cli=false + other=false + if echo "$affected" | grep -qx '@supabase/cli-e2e'; then cli=true; fi + if echo "$affected" | grep -vx '@supabase/cli-e2e' | grep -q .; then other=true; fi + echo "cli_e2e=$cli" >> "$GITHUB_OUTPUT" + echo "other=$other" >> "$GITHUB_OUTPUT" + + - name: Cache Go CLI binary + if: steps.detect.outputs.cli_e2e == 'true' + id: cache-go-binary + uses: actions/cache@v4 + with: + path: apps/cli-go/supabase-go + key: go-cli-${{ runner.os }}-${{ hashFiles('apps/cli-go/**/*.go', + 'apps/cli-go/go.mod', 'apps/cli-go/go.sum') }} + + - name: Setup Go + if: steps.detect.outputs.cli_e2e == 'true' && + steps.cache-go-binary.outputs.cache-hit != 'true' + uses: actions/setup-go@v5 + with: + go-version-file: apps/cli-go/go.mod + cache-dependency-path: apps/cli-go/go.sum + + - name: Build Go CLI + if: steps.detect.outputs.cli_e2e == 'true' && + steps.cache-go-binary.outputs.cache-hit != 'true' + run: go build -o supabase-go . + working-directory: apps/cli-go + + # The ts-legacy/ts-next harnesses invoke `node apps/cli/dist/supabase.js` + # with `SUPABASE_CLI_BINARY_OVERRIDE` pointing at the compiled per-shell + # binary in `apps/cli/dist/`. The `nx run @supabase/cli-e2e:test:e2e` + # target normally builds the supabase umbrella via `dependsOn`, but we + # bypass nx for sharding so we build it explicitly. + - name: Build CLI + if: steps.detect.outputs.cli_e2e == 'true' + run: pnpm exec nx run supabase:build + + # Sharding for cli-e2e (the heavy suite) per + # https://vitest.dev/guide/improving-performance.html#sharding. We invoke + # vitest directly because routing `--shard` through `pnpm run / nx run-many + # -- --shard=N/3` introduces a stray `--` that vitest treats as a + # positional-argument terminator, defeating the shard flag. Other e2e + # suites (process-compose) run unsharded on shard 1. + - name: Run cli-e2e end-to-end tests + if: steps.detect.outputs.cli_e2e == 'true' + run: pnpm --filter @supabase/cli-e2e exec bun --bun vitest run --shard=${{ + matrix.shard }}/3 + env: + CLI_HARNESS_TARGET: ts-legacy + SUPABASE_GO_BINARY: ${{ github.workspace }}/apps/cli-go/supabase-go + + - name: Run other e2e tests (shard 1 only) + if: matrix.shard == 1 && steps.detect.outputs.other == 'true' + run: pnpm exec nx run-many -t test:e2e --exclude=@supabase/cli-e2e + env: + SUPABASE_GO_BINARY: ${{ github.workspace }}/apps/cli-go/supabase-go + + # Summary job that gates branch protection. The matrix `test-e2e` job + # produces per-shard check names (`Run end-to-end tests (shard N/3)`), so + # this job preserves the original `Run end-to-end tests` check name that + # branch protection rules already require. It succeeds iff every shard + # succeeded (or skipped — `success()` is true for skipped jobs). + test-e2e-summary: + if: always() && (github.event.pull_request.draft == false || github.event_name + == 'push') + name: Run end-to-end tests + needs: test-e2e + runs-on: ubuntu-latest + steps: + - name: Verify all shards succeeded + run: | + if [ "${{ needs.test-e2e.result }}" = "failure" ] || [ "${{ needs.test-e2e.result }}" = "cancelled" ]; then + echo "::error ::One or more e2e shards failed: ${{ needs.test-e2e.result }}" + exit 1 + fi + echo "All e2e shards reported: ${{ needs.test-e2e.result }}" diff --git a/.gitignore b/.gitignore index e119d97252..789bb71712 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,19 @@ -# General -.DS_Store +node_modules +dist +coverage/ .env +.claude/ +.agents/.repos/effect-v3 +.worktrees/ +.supabase/ +.idea/ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -/cli - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out +# Local dev registry (verdaccio storage, generated config, auth tokens) +tmp/ -# Dependency directories (remove the comment below to include it) -# vendor/ - -# IDE -/.vscode -/.idea - -# NPM -node_modules -package-lock.json +# Compiled CLI binaries (generated by build scripts, not source-controlled) +packages/cli-*/bin/ -# Initialized by cli for local testing -/supabase +# Nx +.nx/cache +.nx/workspace-data \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..1d6a09bd7c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule ".repos/effect"] + path = .repos/effect + url = https://github.com/Effect-TS/effect-smol.git +[submodule ".repos/effect-patterns"] + path = .repos/effect-patterns + url = https://github.com/PaulJPhilp/EffectPatterns +[submodule ".repos/effect-v3"] + path = .repos/effect-v3 + url = https://github.com/Effect-TS/effect.git +[submodule ".repos/lalph"] + path = .repos/lalph + url = https://github.com/tim-smart/lalph.git +[submodule ".repos/cheffect"] + path = .repos/cheffect + url = https://github.com/tim-smart/cheffect.git +[submodule ".repos/process-compose"] + path = .repos/process-compose + url = https://github.com/F1bonacc1/process-compose.git +[submodule ".repos/t3code"] + path = .repos/t3code + url = https://github.com/pingdotgg/t3code.git diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index 98e3cef593..0000000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: 2 -project_name: supabase -builds: - - id: supabase - binary: supabase - flags: - - -trimpath - ldflags: - - -s -w -X github.com/supabase/cli/internal/utils.Version={{.Version}} -X github.com/supabase/cli/internal/utils.SentryDsn={{ .Env.SENTRY_DSN }} -X github.com/supabase/cli/internal/utils.PostHogAPIKey={{ .Env.POSTHOG_API_KEY }} -X github.com/supabase/cli/internal/utils.PostHogEndpoint={{ .Env.POSTHOG_ENDPOINT }} - env: - - CGO_ENABLED=0 - targets: - - darwin_amd64 - - darwin_arm64 - - linux_amd64 - - linux_arm64 - - windows_amd64 - - windows_arm64 -archives: - - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' -release: - draft: true - replace_existing_draft: true - prerelease: auto -changelog: - use: github - groups: - - title: Features - regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' - order: 0 - - title: "Bug fixes" - regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' - order: 1 - - title: Others - order: 999 -nfpms: - - vendor: Supabase - description: Supabase CLI - maintainer: Supabase CLI - homepage: https://supabase.com - license: MIT - formats: - - apk - - deb - - rpm - - archlinux diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..cabf43b5dd --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 \ No newline at end of file diff --git a/.repos/cheffect b/.repos/cheffect new file mode 160000 index 0000000000..f4c923c96b --- /dev/null +++ b/.repos/cheffect @@ -0,0 +1 @@ +Subproject commit f4c923c96b7153671b4a2c9474c1e7e03ab8f36c diff --git a/.repos/effect b/.repos/effect new file mode 160000 index 0000000000..aae8797b9c --- /dev/null +++ b/.repos/effect @@ -0,0 +1 @@ +Subproject commit aae8797b9cb383be0c182dd58d03d787c354238b diff --git a/.repos/effect-patterns b/.repos/effect-patterns new file mode 160000 index 0000000000..f3a0da2299 --- /dev/null +++ b/.repos/effect-patterns @@ -0,0 +1 @@ +Subproject commit f3a0da2299717cf31d31313c5661cebd2446d5a3 diff --git a/.repos/effect-v3 b/.repos/effect-v3 new file mode 160000 index 0000000000..70ce155cd7 --- /dev/null +++ b/.repos/effect-v3 @@ -0,0 +1 @@ +Subproject commit 70ce155cd73a3b4cd723fe955454b5837b428f76 diff --git a/.repos/lalph b/.repos/lalph new file mode 160000 index 0000000000..203f1ec28f --- /dev/null +++ b/.repos/lalph @@ -0,0 +1 @@ +Subproject commit 203f1ec28f26d3a4f18c0f3e092eae3695de1842 diff --git a/.repos/process-compose b/.repos/process-compose new file mode 160000 index 0000000000..cd7f6af235 --- /dev/null +++ b/.repos/process-compose @@ -0,0 +1 @@ +Subproject commit cd7f6af235149a075385f3b8b54d635e83dc0f52 diff --git a/.repos/t3code b/.repos/t3code new file mode 160000 index 0000000000..91a03e0747 --- /dev/null +++ b/.repos/t3code @@ -0,0 +1 @@ +Subproject commit 91a03e074751e9dc732d0dddcd7b3a291caba34f diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..aada68d804 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["oven.bun-vscode", "oxc.oxc-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..0ff9ffd7c9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "typescript.experimental.useTsgo": true, + "oxc.useExecPath": true, +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..b215cb1e1b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,227 @@ +# Supabase + +Bun monorepo with workspaces under `apps/` and `packages/`. + +## Package Manager + +`pnpm` is the package manager. Use `pnpm