From 208f51dcbc09a6a11ba5e3b7f936861647fd480d Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 16 Sep 2025 15:55:54 +0200 Subject: [PATCH 1/3] ci: canary deploy process --- .github/workflows/canary-comment.yml | 124 +++++++++++++++++++++++++++ .github/workflows/canary-deploy.yml | 123 ++++++++++++++++++++++++++ CONTRIBUTING.md | 25 +++++- 3 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/canary-comment.yml create mode 100644 .github/workflows/canary-deploy.yml diff --git a/.github/workflows/canary-comment.yml b/.github/workflows/canary-comment.yml new file mode 100644 index 00000000..ea6442b6 --- /dev/null +++ b/.github/workflows/canary-comment.yml @@ -0,0 +1,124 @@ +name: Update Canary PR Comment + +permissions: + pull-requests: write + actions: read + +on: + workflow_run: + workflows: ['Canary Deploy'] + types: [completed] + +jobs: + update-comment: + # Only run on the correct repository + if: github.repository == 'supabase/postgres-meta' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + # Get PR number from the workflow run + - name: Get PR info + id: pr-info + uses: actions/github-script@v7 + with: + script: | + // Get the workflow run details + const workflowRun = context.payload.workflow_run; + + // Find associated PR + const prs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${workflowRun.head_repository.owner.login}:${workflowRun.head_branch}` + }); + + if (prs.data.length > 0) { + const pr = prs.data[0]; + core.setOutput('pr_number', pr.number); + core.setOutput('found', 'true'); + console.log(`Found PR #${pr.number}`); + } else { + core.setOutput('found', 'false'); + console.log('No associated PR found'); + } + + # Only continue if we found a PR and the workflow succeeded + - name: Download canary info + if: ${{ steps.pr-info.outputs.found == 'true' && github.event.workflow_run.conclusion == 'success' }} + uses: actions/download-artifact@v4 + with: + name: canary-info + path: canary-info/ + run-id: ${{ github.event.workflow_run.id }} + continue-on-error: true + + - name: Read canary info + if: ${{ steps.pr-info.outputs.found == 'true' && github.event.workflow_run.conclusion == 'success' }} + id: canary-info + run: | + if [ -f "canary-info/canary-tags.txt" ]; then + # Read the first tag (DockerHub) from the tags + FIRST_TAG=$(head -n1 canary-info/canary-tags.txt) + echo "tag=$FIRST_TAG" >> $GITHUB_OUTPUT + echo "found=true" >> $GITHUB_OUTPUT + echo "commit-sha=$(cat canary-info/commit-sha.txt)" >> $GITHUB_OUTPUT + else + echo "found=false" >> $GITHUB_OUTPUT + fi + continue-on-error: true + + # Find existing comment + - name: Find existing comment + if: ${{ steps.pr-info.outputs.found == 'true' }} + uses: peter-evans/find-comment@v3 + id: find-comment + with: + issue-number: ${{ steps.pr-info.outputs.pr_number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + # Create or update comment based on workflow status + - name: Create or update canary comment + if: ${{ steps.pr-info.outputs.found == 'true' }} + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ steps.pr-info.outputs.pr_number }} + body: | + + ## 🚀 Canary Deployment Status + + ${{ github.event.workflow_run.conclusion == 'success' && steps.canary-info.outputs.found == 'true' && format('✅ **Canary image deployed successfully!** + + 🐳 **Docker Image:** `{0}` + 📝 **Commit:** `{1}` + + You can test this canary deployment by pulling the image: + ```bash + docker pull {0} + ``` + + Or use it in your docker-compose.yml: + ```yaml + services: + postgres-meta: + image: {0} + # ... other configuration + ``` + + The canary image is available on: + - 🐳 [Docker Hub](https://hub.docker.com/r/supabase/postgres-meta) + - 📦 [GitHub Container Registry](https://ghcr.io/supabase/postgres-meta) + - ☁️ [AWS ECR Public](https://gallery.ecr.aws/supabase/postgres-meta) + ', steps.canary-info.outputs.tag, steps.canary-info.outputs.commit-sha) || '' }} + + ${{ github.event.workflow_run.conclusion == 'failure' && '❌ **Canary deployment failed** + + Please check the [workflow logs](' }}${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.html_url || '' }}${{ github.event.workflow_run.conclusion == 'failure' && ') for more details. + + Make sure your PR has the `deploy-canary` label and targets the `master` branch.' || '' }} + + --- + Last updated: ${{ github.event.workflow_run.updated_at }} + edit-mode: replace diff --git a/.github/workflows/canary-deploy.yml b/.github/workflows/canary-deploy.yml new file mode 100644 index 00000000..872fcc41 --- /dev/null +++ b/.github/workflows/canary-deploy.yml @@ -0,0 +1,123 @@ +name: Canary Deploy + +permissions: + contents: read + pull-requests: read + packages: write + id-token: write + +on: + pull_request: + types: [opened, synchronize, labeled] + paths: + - 'src/**' + - 'package.json' + - 'package-lock.json' + - 'tsconfig.json' + - 'Dockerfile' + +jobs: + build-canary: + # Only run if PR has the 'deploy-canary' label, is on the correct repository, and targets master branch + if: | + github.repository == 'supabase/postgres-meta' && + github.event.pull_request.base.ref == 'master' && + contains(github.event.pull_request.labels.*.name, 'deploy-canary') + runs-on: ubuntu-22.04 + timeout-minutes: 30 + outputs: + canary-tag: ${{ steps.meta.outputs.tags }} + pr-number: ${{ github.event.pull_request.number }} + steps: + # Checkout fork code - safe because no secrets are available for building + - name: Checkout code + uses: actions/checkout@v5 + + # Log PR author for auditing + - name: Log PR author + run: | + echo "Canary deploy triggered by: ${{ github.event.pull_request.user.login }}" + echo "PR #${{ github.event.pull_request.number }} from fork: ${{ github.event.pull_request.head.repo.full_name }}" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies and build + run: | + npm clean-install + npm run build + + # Generate canary tag + - id: meta + uses: docker/metadata-action@v5 + with: + images: | + supabase/postgres-meta + public.ecr.aws/supabase/postgres-meta + ghcr.io/supabase/postgres-meta + tags: | + type=raw,value=canary-pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} + type=raw,value=canary-pr-${{ github.event.pull_request.number }} + + - uses: docker/setup-qemu-action@v3 + with: + platforms: amd64,arm64 + - uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.PROD_AWS_ROLE }} + aws-region: us-east-1 + + - name: Login to ECR + uses: docker/login-action@v3 + with: + registry: public.ecr.aws + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push canary image + uses: docker/build-push-action@v6 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: | + org.opencontainers.image.title=postgres-meta-canary + org.opencontainers.image.description=Canary build for PR #${{ github.event.pull_request.number }} + org.opencontainers.image.source=${{ github.event.pull_request.head.repo.html_url }} + org.opencontainers.image.revision=${{ github.event.pull_request.head.sha }} + canary.pr.number=${{ github.event.pull_request.number }} + canary.pr.author=${{ github.event.pull_request.user.login }} + + # Save canary info for the comment workflow + - name: Save canary info + run: | + mkdir -p canary-info + echo "${{ steps.meta.outputs.tags }}" > canary-info/canary-tags.txt + echo "${{ github.event.pull_request.number }}" > canary-info/pr-number.txt + echo "${{ github.event.pull_request.head.sha }}" > canary-info/commit-sha.txt + echo "postgres-meta" > canary-info/package-name.txt + + - name: Upload canary info + uses: actions/upload-artifact@v4 + with: + name: canary-info + path: canary-info/ + retention-days: 7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7f56a71..c6d17b80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,4 +11,27 @@ 2. Run the tests: `npm run test:run` 3. Make changes in code (`/src`) and tests (`/test/lib` and `/test/server`) 4. Run the tests again: `npm run test:run` -5. Commit + PR \ No newline at end of file +5. Commit + PR + +### Canary Deployments + +For testing your changes when they impact other things (like type generation and postgrest-js), you can deploy a canary version of postgres-meta: + +1. **Create a Pull Request** targeting the `master` branch +2. **Add the `deploy-canary` label** to your PR +3. **Wait for the canary build** - GitHub Actions will automatically build and push a canary Docker image +4. **Use the canary image** - The bot will comment on your PR with the exact image tag and usage instructions + +The canary image will be tagged as: + +- `supabase/postgres-meta:canary-pr-{PR_NUMBER}-{COMMIT_SHA}` +- `supabase/postgres-meta:canary-pr-{PR_NUMBER}` + +Example usage: + +```bash +docker pull supabase/postgres-meta:canary-pr-123-abc1234 +echo "canary-pr-123-abc1234" > supabase/.temp/pgmeta-version +``` + +**Note:** Only maintainers can add the `deploy-canary` label for security reasons. The canary deployment requires access to production Docker registries. From 3be0f712f6d38782c9348c509b747e1c21d50b0d Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 16 Sep 2025 16:01:59 +0200 Subject: [PATCH 2/3] chore: upgrade a dep --- package-lock.json | 161 +++++++--------------------------------------- package.json | 2 +- 2 files changed, 26 insertions(+), 137 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75d2da17..cd6f7b63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "cpy-cli": "^5.0.0", "nodemon": "^3.1.7", "npm-run-all": "^4.1.5", - "pino-pretty": "^12.0.0", + "pino-pretty": "^13.1.1", "rimraf": "^6.0.1", "ts-node": "^10.9.1", "typescript": "^5.6.3", @@ -2230,19 +2230,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "license": "ISC" }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -2551,27 +2538,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -2623,31 +2589,6 @@ "node": ">=8" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -3500,26 +3441,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/expect-type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.0.tgz", @@ -4277,27 +4198,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6329,9 +6229,9 @@ } }, "node_modules/pino-pretty": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-12.1.0.tgz", - "integrity": "sha512-Z7JdCPqggoRyo0saJyCe1BN8At5qE+ZBElNbyx+znCaCVN+ohOqlWb+/WSYnamzfi2e6P6pXq/3H66KwFQHXWg==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz", + "integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==", "dev": true, "license": "MIT", "dependencies": { @@ -6345,31 +6245,30 @@ "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", + "secure-json-parse": "^4.0.0", "sonic-boom": "^4.0.1", - "strip-json-comments": "^3.1.1" + "strip-json-comments": "^5.0.2" }, "bin": { "pino-pretty": "bin.js" } }, - "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "node_modules/pino-pretty/node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/pino-std-serializers": { "version": "7.0.0", @@ -6513,16 +6412,6 @@ "prettier": "^3.0.3" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", @@ -7629,13 +7518,13 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 62315e9f..941df965 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "cpy-cli": "^5.0.0", "nodemon": "^3.1.7", "npm-run-all": "^4.1.5", - "pino-pretty": "^12.0.0", + "pino-pretty": "^13.1.1", "rimraf": "^6.0.1", "ts-node": "^10.9.1", "typescript": "^5.6.3", From 3bd23d6dc2e7d3960ca0f6df841072e60ab571e4 Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 16 Sep 2025 16:31:36 +0200 Subject: [PATCH 3/3] chore: reword canary comment --- .github/workflows/canary-comment.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/canary-comment.yml b/.github/workflows/canary-comment.yml index ea6442b6..aae0a7dc 100644 --- a/.github/workflows/canary-comment.yml +++ b/.github/workflows/canary-comment.yml @@ -99,6 +99,11 @@ jobs: docker pull {0} ``` + You can also set the version in a supabase local project by running: + ```bash + echo "{0}" > supabase/.temp/pgmeta-version + ``` + Or use it in your docker-compose.yml: ```yaml services: