Cracking the Code: Unlocking Automated PRs in GitHub Actions with a Two-Layer Permission Fix
Ever struggled with GitHub Actions not doing what it's told? We dive into a recent fix for our 'Vibe Publisher' workflow, uncovering the critical two-layer permission system that blocked our automated pull requests.
Imagine a world where your internal development notes, your "session memories," automatically transform into actionable pull requests, ready for review. That's the dream we're building with our "Vibe Publisher" workflow – a system designed to streamline our internal dev process by turning raw .memory/ files into concrete code contributions.
But like any good dream, there was a snag. Our Vibe Publisher workflow, while brilliant in concept, was hitting a brick wall. It simply couldn't create branches or pull requests, despite our initial attempts to grant it the necessary power. This past session, we rolled up our sleeves and tackled this permissions puzzle head-on, ensuring our automated future is truly automated.
The Problem: "GitHub Actions is Not Permitted..."
Our goal was clear: The Vibe Publisher workflow needed to be able to:
- Create a new branch.
- Push changes to that branch.
- Open a new pull request.
It's powered by the incredibly useful peter-evans/create-pull-request@v6 action, which handles much of the heavy lifting. However, every time we tried, we were met with a frustrating error: "GitHub Actions is not permitted to create or approve pull requests." This wasn't just a minor hiccup; it was a fundamental blocker for our automation strategy.
The Deep Dive: Unraveling GitHub Actions Permissions
Our first instinct, and a perfectly logical one, was to add the necessary permissions block directly to our workflow YAML file (.github/workflows/vibe_publisher.yml).
# .github/workflows/vibe_publisher.yml
name: Vibe Publisher
on:
push:
branches:
- main # Or your trigger branch
permissions:
contents: write # Allow the workflow to push branches and files
pull-requests: write # Allow the workflow to create and modify PRs
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
# ... other settings for your PR content ...
title: 'Automated: New Vibe Publisher Update'
body: 'This PR was automatically generated from a session memory.'
branch: 'vibe-publisher-update-${{ github.run_number }}'
commit-message: 'feat: publish new session memory'
This should have been enough, right? The contents: write and pull-requests: write directives explicitly grant the workflow's GITHUB_TOKEN the necessary scopes to perform these actions. We expected green, but we got red. The same "not permitted" error persisted.
The Breakthrough: The Second Layer
This led us down a rabbit hole, eventually revealing a critical distinction: GitHub Actions permissions operate on two separate layers:
- Workflow-level
permissionsblock: This controls the scope of theGITHUB_TOKENfor that specific workflow run. It defines what that token is allowed to do. - Repository-level GitHub Actions permissions: This is a global setting for your repository that dictates whether any GitHub Actions workflow can interact with pull requests at all, regardless of the token's individual scopes.
Our workflow-level permissions were correct, but the repository-level setting was still restrictive. To fix this, we needed to update the repo's default GitHub Actions permissions. While this can be done via the GitHub UI (Settings > Actions > General), for a quick and programmatic fix, we leveraged the gh cli and its api command:
gh api \
--method PUT \
-H "Accept: application/vnd.github.v3+json" \
/repos/{owner}/{repo}/actions/permissions/workflow \
-F default_workflow_permissions=write \
-F can_approve_pull_request_reviews=true
Here's what these flags mean:
default_workflow_permissions=write: This changes the default permissions for all future workflow tokens in the repository fromreadtowrite. While our workflow-level block overrides this for specific workflows, setting a sensible default is good practice.can_approve_pull_request_reviews=true: This was the crucial missing piece! This setting explicitly allows GitHub Actions to create (and approve) pull requests at the repository level. Without this, even a token withpull-requests: writescope will be denied.
Once these repo-level permissions were updated, our Vibe Publisher workflow sprang to life! It successfully created a new branch, pushed changes, and opened a pull request, all automatically.
Lessons Learned: The Two-Layer Permission System
This experience solidified a vital understanding of GitHub Actions security:
- Don't forget the repo-level defaults: Even if your workflow YAML seems perfectly configured, an overarching repository setting can still block actions. Always check
Settings > Actions > Generalor use thegh apito verify. can_approve_pull_request_reviewsis key for automation: If your workflow needs to initiate or interact heavily with pull requests, make sure this specific repo-level setting is enabled.- Explicit is better: Always define
permissionsblocks directly in your workflow YAML, even ifdefault_workflow_permissionsis set towrite. This makes your workflow's intentions clear and limits its scope to only what's necessary.
With this fix, all four of our core CI/CD workflows (linting, unit tests, E2E tests, and now Vibe Publisher) are fully operational and passing green!
Beyond the Permissions: Other Progress
While permissions were the main event, we also made significant strides in other areas during this session:
- Enhanced Project Dashboard: Our project list page now offers richer per-card stats, including workflows, discussions, notes, action points, spend, and success rates. This provides a quick, at-a-glance overview of project health.
- CI Pipeline Fortification: We fixed three previously failing CI jobs, ensuring our linting plugin registration, Kimi test model, and
pgvectorextension in E2E tests are all running smoothly. - Lint Cleanup: We diligently cleared out approximately 40 pre-existing lint errors across about 25 files, making our codebase cleaner and more maintainable.
Looking Ahead
With our Vibe Publisher now purring along, creating automated PRs from our session memories, we're already eyeing the next set of improvements:
- Visually verifying the new project list page in the browser.
- Considering a helpful tooltip for the "success rate" badge.
- Thinking about denormalizing stats into our Project model for better performance at scale.
- And, of course, reviewing the very first auto-generated blog post PR from the Vibe Publisher itself! (Meta, right?)
The journey to fully automated CI/CD often involves unexpected detours into the nitty-gritty of platform configurations. But with each challenge overcome, our systems become more robust, and our development flow smoother. Here's to more green pipelines!