From bc1a928e5266a83e68c275b6c0465179d5fdcf9b Mon Sep 17 00:00:00 2001 From: Levi van Noort <73097785+levivannoort@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:26:47 +0200 Subject: [PATCH 1/4] feat: add production and staging deployment workflows for console --- .github/workflows/production.yml | 91 ++++++++++++++++++++++++++++++++ .github/workflows/publish.yml | 83 ----------------------------- .github/workflows/staging.yml | 91 ++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 83 deletions(-) create mode 100644 .github/workflows/production.yml create mode 100644 .github/workflows/staging.yml diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml new file mode 100644 index 0000000000..6194b48981 --- /dev/null +++ b/.github/workflows/production.yml @@ -0,0 +1,91 @@ +name: Production deployment + +on: + workflow_dispatch: + release: + types: [published] + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +env: + ENVIRONMENT: production + DECLARATIVE_OWNER: appwrite-labs + DECLARATIVE_REPOSITORY: cloud-applications + # console is the `cloud-console` alias inside the cloud umbrella chart. + DECLARATIVE_ALIAS: cloud-console + IMAGE_NAME: appwrite/console-cloud + TAG: ${{ github.event.release.tag_name || github.sha }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Log in to Docker Hub + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push Docker image + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ env.IMAGE_NAME }}:${{ env.TAG }} + build-args: | + "PUBLIC_CONSOLE_MODE=cloud" + "PUBLIC_CONSOLE_FEATURE_FLAGS=" + "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" + "PUBLIC_GROWTH_ENDPOINT=${{ vars.VITE_APPWRITE_GROWTH_ENDPOINT }}" + "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" + "PUBLIC_CONSOLE_FINGERPRINT_KEY=${{ secrets.PUBLIC_CONSOLE_FINGERPRINT_KEY }}" + "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" + "SENTRY_RELEASE=${{ github.event.release.tag_name }}" + + deploy: + needs: build + runs-on: ubuntu-latest + # Shared across the staging and production workflows so their deploy + # jobs never push to cloud-applications at the same time. + concurrency: + group: declarative-deploy + cancel-in-progress: false + steps: + - name: Get token for ${{ env.DECLARATIVE_REPOSITORY }} + id: app-token + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2 + with: + app-id: ${{ vars.DECLARATIVE_DEPLOYMENT_GITHUB_APP_ID }} + private-key: ${{ secrets.DECLARATIVE_DEPLOYMENT_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ env.DECLARATIVE_OWNER }} + repositories: ${{ env.DECLARATIVE_REPOSITORY }} + + - name: Checkout ${{ env.DECLARATIVE_REPOSITORY }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }} + token: ${{ steps.app-token.outputs.token }} + + - name: Update image tag + run: yq -i '.[strenv(DECLARATIVE_ALIAS)].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/cloud/default.yaml + + - name: Commit and push + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add ${{ env.ENVIRONMENT }}/cloud/default.yaml + if git diff --cached --quiet; then + echo "No changes to commit" + else + git commit -m "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" + git push + fi diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a6851c70ac..534b86bf00 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,89 +5,6 @@ on: types: [published] jobs: - publish-cloud: - runs-on: ubuntu-latest - steps: - - name: Checkout the repo - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Set up QEMU - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Log in to Docker Hub - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: appwrite/console-cloud - tags: | - type=semver,pattern={{major}}.{{minor}}.{{patch}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - name: Build and push Docker image - id: push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - "PUBLIC_CONSOLE_MODE=cloud" - "PUBLIC_CONSOLE_FEATURE_FLAGS=" - "PUBLIC_APPWRITE_MULTI_REGION=true" - "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" - "PUBLIC_GROWTH_ENDPOINT=${{ vars.VITE_APPWRITE_GROWTH_ENDPOINT }}" - "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" - "PUBLIC_CONSOLE_FINGERPRINT_KEY=${{ secrets.PUBLIC_CONSOLE_FINGERPRINT_KEY }}" - "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" - "SENTRY_RELEASE=${{ github.event.release.tag_name }}" - publish-cloud-stage: - environment: staging - runs-on: ubuntu-latest - steps: - - name: Checkout the repo - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Set up QEMU - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Log in to Docker Hub - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: appwrite/console-cloud-stage - tags: | - type=semver,pattern={{major}}.{{minor}}.{{patch}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - name: Build and push Docker image - id: push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - "PUBLIC_CONSOLE_MODE=cloud" - "PUBLIC_CONSOLE_FEATURE_FLAGS=" - "PUBLIC_APPWRITE_MULTI_REGION=true" - "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" - "PUBLIC_GROWTH_ENDPOINT=${{ vars.VITE_APPWRITE_GROWTH_ENDPOINT }}" - "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" - "PUBLIC_CONSOLE_FINGERPRINT_KEY=${{ secrets.PUBLIC_CONSOLE_FINGERPRINT_KEY_STAGE }}" publish-self-hosted: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml new file mode 100644 index 0000000000..fa49f2ec38 --- /dev/null +++ b/.github/workflows/staging.yml @@ -0,0 +1,91 @@ +name: Staging deployment + +on: + workflow_dispatch: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +env: + ENVIRONMENT: staging + DECLARATIVE_OWNER: appwrite-labs + DECLARATIVE_REPOSITORY: cloud-applications + # console is the `cloud-console` alias inside the cloud umbrella chart. + DECLARATIVE_ALIAS: cloud-console + IMAGE_NAME: appwrite/console-cloud-stage + TAG: ${{ github.sha }} + +jobs: + build: + environment: staging + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Log in to Docker Hub + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push Docker image + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ env.IMAGE_NAME }}:${{ env.TAG }} + build-args: | + "PUBLIC_CONSOLE_MODE=cloud" + "PUBLIC_CONSOLE_FEATURE_FLAGS=" + "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" + "PUBLIC_GROWTH_ENDPOINT=${{ vars.VITE_APPWRITE_GROWTH_ENDPOINT }}" + "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" + "PUBLIC_CONSOLE_FINGERPRINT_KEY=${{ secrets.PUBLIC_CONSOLE_FINGERPRINT_KEY_STAGE }}" + + deploy: + needs: build + runs-on: ubuntu-latest + # Shared across the staging and production workflows so their deploy + # jobs never push to cloud-applications at the same time. + concurrency: + group: declarative-deploy + cancel-in-progress: false + steps: + - name: Get token for ${{ env.DECLARATIVE_REPOSITORY }} + id: app-token + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2 + with: + app-id: ${{ vars.DECLARATIVE_DEPLOYMENT_GITHUB_APP_ID }} + private-key: ${{ secrets.DECLARATIVE_DEPLOYMENT_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ env.DECLARATIVE_OWNER }} + repositories: ${{ env.DECLARATIVE_REPOSITORY }} + + - name: Checkout ${{ env.DECLARATIVE_REPOSITORY }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }} + token: ${{ steps.app-token.outputs.token }} + + - name: Update image tag + run: yq -i '.[strenv(DECLARATIVE_ALIAS)].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/cloud/default.yaml + + - name: Commit and push + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add ${{ env.ENVIRONMENT }}/cloud/default.yaml + if git diff --cached --quiet; then + echo "No changes to commit" + else + git commit -m "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" + git push + fi From d44a07b8a484a71202faa6c34ebb20bcd0cb8437 Mon Sep 17 00:00:00 2001 From: Levi van Noort <73097785+levivannoort@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:34:12 +0200 Subject: [PATCH 2/4] Update production.yml --- .github/workflows/production.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 6194b48981..77f0fb951a 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -13,7 +13,6 @@ env: ENVIRONMENT: production DECLARATIVE_OWNER: appwrite-labs DECLARATIVE_REPOSITORY: cloud-applications - # console is the `cloud-console` alias inside the cloud umbrella chart. DECLARATIVE_ALIAS: cloud-console IMAGE_NAME: appwrite/console-cloud TAG: ${{ github.event.release.tag_name || github.sha }} From 771f09f8faed07ba7916d4dc3ba20ce5208d6029 Mon Sep 17 00:00:00 2001 From: Levi van Noort <73097785+levivannoort@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:34:51 +0200 Subject: [PATCH 3/4] Update staging.yml --- .github/workflows/staging.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index fa49f2ec38..3c4283447f 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -14,7 +14,6 @@ env: ENVIRONMENT: staging DECLARATIVE_OWNER: appwrite-labs DECLARATIVE_REPOSITORY: cloud-applications - # console is the `cloud-console` alias inside the cloud umbrella chart. DECLARATIVE_ALIAS: cloud-console IMAGE_NAME: appwrite/console-cloud-stage TAG: ${{ github.sha }} From 2abbc8f9f176f6d3b40f396be274f5e79ca27d50 Mon Sep 17 00:00:00 2001 From: Levi van Noort <73097785+levivannoort@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:31:56 +0200 Subject: [PATCH 4/4] fix(deployment): update workflows to create pull requests for image tag changes --- .github/workflows/production.yml | 24 ++++++++++++++++++++---- .github/workflows/staging.yml | 6 +++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 77f0fb951a..216f75b5c3 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -73,18 +73,34 @@ jobs: with: repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }} token: ${{ steps.app-token.outputs.token }} + # Don't leave the write-capable token in .git/config for later steps. + persist-credentials: false - name: Update image tag run: yq -i '.[strenv(DECLARATIVE_ALIAS)].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/cloud/default.yaml - - name: Commit and push + - name: Open pull request + # The release author reviews; falls back to the dispatcher for + # manual runs. The PR author is the GitHub App bot, so requesting + # the release author as reviewer is always allowed. + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + REVIEWER: ${{ github.event.release.author.login || github.actor }} + BRANCH: deploy/console-${{ env.TAG }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add ${{ env.ENVIRONMENT }}/cloud/default.yaml if git diff --cached --quiet; then echo "No changes to commit" - else - git commit -m "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" - git push + exit 0 fi + git checkout -b "$BRANCH" + git commit -m "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" + git push "https://x-access-token:${GH_TOKEN}@github.com/${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }}.git" "$BRANCH" + gh pr create \ + --base main \ + --head "$BRANCH" \ + --reviewer "$REVIEWER" \ + --title "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" \ + --body "Promotes \`${{ env.IMAGE_NAME }}:${{ env.TAG }}\` to production. Requested by @$REVIEWER." diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 3c4283447f..5ba4e204bb 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -73,11 +73,15 @@ jobs: with: repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }} token: ${{ steps.app-token.outputs.token }} + # Don't leave the write-capable token in .git/config for later steps. + persist-credentials: false - name: Update image tag run: yq -i '.[strenv(DECLARATIVE_ALIAS)].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/cloud/default.yaml - name: Commit and push + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -86,5 +90,5 @@ jobs: echo "No changes to commit" else git commit -m "chore(${{ env.ENVIRONMENT }}): console image tag to ${{ env.TAG }}" - git push + git push "https://x-access-token:${GH_TOKEN}@github.com/${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }}.git" HEAD:main fi