Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 9 additions & 21 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ jobs:
name: 🔎 Deploy Preview Resources
timeout-minutes: 4
environment:
name:
preview-${{ github.event.pull_request.number || inputs.pr_number ||
name: preview-${{ github.event.pull_request.number || inputs.pr_number ||
github.run_id }}
Comment on lines +57 to 58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the preview.yml workflow file
fd -t f "preview.yml" .github/

Repository: kentcdodds/kody

Length of output: 88


🏁 Script executed:

# Read the workflow file to examine the lines mentioned
wc -l .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 92


🏁 Script executed:

# Check lines 57-58 (deploy environment naming) and surrounding context
sed -n '50,70p' .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 712


🏁 Script executed:

# Check lines 599-603 (cleanup deletion logic)
sed -n '590,610p' .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 782


🏁 Script executed:

# Check the cleanup script to understand the condition
sed -n '440,460p' .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 651


🏁 Script executed:

# Get the full cleanup script context (lines 550-620)
sed -n '550,620p' .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 2617


🏁 Script executed:

# Also check what inputs are available and how target is used
sed -n '1,100p' .github/workflows/preview.yml

Repository: kentcdodds/kody

Length of output: 2726


🌐 Web query:

GitHub REST API DELETE /repos/environments permission scope required

💡 Result:

The required permission scope for the GitHub REST API DELETE /repos/{owner}/{repo}/environments/{environment_name} endpoint is the "repo" scope for OAuth app tokens and personal access tokens (classic). This applies particularly when using the endpoint with private repositories, but the documentation states it explicitly for this DELETE operation.

Citations:


🌐 Web query:

GitHub Actions GITHUB_TOKEN permissions deployments write delete environment

💡 Result:

The GITHUB_TOKEN in GitHub Actions workflows has configurable permissions via the 'permissions' key at workflow or job level. By default, it has read access to most scopes (contents: read, actions: read, etc.), but write permissions must be explicitly granted, such as deployments: write for creating/updating deployments and environments: write for managing environment configurations. For deployments: - GITHUB_TOKEN can create deployments when a job references an environment (automatic via GitHub Actions). - To explicitly grant: permissions: { deployments: write } For environments: - GITHUB_TOKEN supports read access by default. - For write/delete (e.g., API calls to create/update/delete environments), explicitly grant: permissions: { environments: write } - Note: Some advanced environment management may require a GitHub App token or PAT with repo/administration scope, as GITHUB_TOKEN (GitHub App installation token) lacks certain repository administration permissions like full environment CRUD via API. Example workflow snippet: jobs: deploy: permissions: deployments: write environments: write environment: production steps: - name: Deploy run: echo "Deploying..." Sources confirm GITHUB_TOKEN needs explicit write permissions for these scopes, and limitations exist for delete/create environments via standard GITHUB_TOKEN (requires alternatives for full control).

Citations:


Fix orphaned branch preview environments and add missing environment deletion permissions.

When workflow_dispatch is triggered with target=branch (the default), the deploy job creates preview-${github.run_id}, but the cleanup job intentionally skips GitHub environment deletion with the message "branch-targeted cleanups use a different naming scheme." This leaves an orphaned environment for each manual branch deployment.

Additionally, the cleanup job declares only deployments: write permission, but calling DELETE /repos/{owner}/{repo}/environments/{environment_name} requires either environments: write or the broader repo scope. The GITHUB_TOKEN used here lacks full environment management permissions for deletion via the REST API.

To fix this:

  1. Add environments: write permission to the cleanup job (minimum requirement for environment deletion)
  2. Update the deploy environment name to use a deterministic identifier for branch deployments (e.g., inputs.preview_name || github.ref_name), or explicitly implement branch-targeted deletion logic using the same naming expression in the cleanup script
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/preview.yml around lines 57 - 58, The preview environment
naming and permissions are incorrect: change the deploy job's environment name
expression (currently "preview-${{ github.event.pull_request.number ||
inputs.pr_number || github.run_id }}") to a deterministic branch identifier such
as using "inputs.preview_name || github.ref_name" so branch-triggered
workflow_dispatch doesn't create orphaned "preview-${{ github.run_id }}"
environments, and update the cleanup job to both request the correct permission
scope by adding "environments: write" to its permissions and ensure the cleanup
logic uses the exact same naming expression (or the inputs.preview_name ||
github.ref_name expression) when calling DELETE
/repos/{owner}/{repo}/environments/{environment_name} so deletion succeeds.

url: ${{ steps.deploy_preview.outputs.url }}
if: >-
Expand Down Expand Up @@ -143,8 +142,7 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ENV: preview
WRANGLER_CONFIG: ${{ steps.resources.outputs.wrangler_config }}
run:
node ./wrangler-env.ts d1 migrations apply APP_DB --remote --config
run: node ./wrangler-env.ts d1 migrations apply APP_DB --remote --config
"$WRANGLER_CONFIG"

- name: 🧪 Deploy preview mock Workers
Expand Down Expand Up @@ -344,8 +342,7 @@ jobs:
if: steps.deploy_preview.outputs.url != ''
env:
PREVIEW_URL: ${{ steps.deploy_preview.outputs.url }}
PREVIEW_VERSION_URL:
'${{ steps.deploy_preview.outputs.version_preview_url }}'
PREVIEW_VERSION_URL: "${{ steps.deploy_preview.outputs.version_preview_url }}"
WORKER_NAME: ${{ steps.deploy_preview.outputs.worker_name }}
MOCK_SUMMARY: ${{ steps.deploy_mocks.outputs.mock_summary }}
D1_DATABASE_NAME: ${{ steps.resources.outputs.d1_database_name }}
Expand Down Expand Up @@ -380,8 +377,7 @@ jobs:
uses: actions/github-script@v8.0.0
env:
PREVIEW_URL: ${{ steps.deploy_preview.outputs.url }}
PREVIEW_VERSION_URL:
'${{ steps.deploy_preview.outputs.version_preview_url }}'
PREVIEW_VERSION_URL: "${{ steps.deploy_preview.outputs.version_preview_url }}"
WORKER_NAME: ${{ steps.deploy_preview.outputs.worker_name }}
MOCK_SUMMARY: ${{ steps.deploy_mocks.outputs.mock_comment_summary }}
D1_DATABASE_NAME: ${{ steps.resources.outputs.d1_database_name }}
Expand Down Expand Up @@ -445,15 +441,15 @@ jobs:
runs-on: ubuntu-latest
name: 🧹 Cleanup Preview Resources
timeout-minutes: 4
permissions:
contents: read
deployments: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
if: >-
(github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
github.event.pull_request.head.repo.fork == false) ||
(github.event_name == 'workflow_dispatch' &&
inputs.action == 'cleanup')
env:
PREVIEW_ENVIRONMENT_GITHUB_TOKEN:
${{ secrets.PREVIEW_ENVIRONMENT_GITHUB_TOKEN }}
steps:
- name: 📦 Checkout
uses: actions/checkout@v6.0.2
Expand Down Expand Up @@ -568,24 +564,16 @@ jobs:
set -euo pipefail
node tools/ci/preview-resources.ts cleanup --worker-name "$APP_WORKER_NAME"

- name: ℹ️ Skip GitHub preview environment delete
if: >-
always() && env.PREVIEW_ENVIRONMENT_GITHUB_TOKEN == ''
run: >
echo "Skipping GitHub preview environment delete;
PREVIEW_ENVIRONMENT_GITHUB_TOKEN is not configured."

- name: 🏷️ Delete GitHub preview environment
if: >-
always() && env.PREVIEW_ENVIRONMENT_GITHUB_TOKEN != ''
if: always()
uses: actions/github-script@v8.0.0
env:
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
INPUT_TARGET: ${{ inputs.target }}
INPUT_PR_NUMBER: ${{ inputs.pr_number }}
with:
github-token: ${{ env.PREVIEW_ENVIRONMENT_GITHUB_TOKEN }}
github-token: ${{ github.token }}
script: |
const eventName = process.env.EVENT_NAME;
let envName;
Expand Down
Loading