Skip to content

Harden FastLED project sync workflow#3

Merged
zackees merged 1 commit into
mainfrom
codex/harden-project-sync
Jun 1, 2026
Merged

Harden FastLED project sync workflow#3
zackees merged 1 commit into
mainfrom
codex/harden-project-sync

Conversation

@zackees

@zackees zackees commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

  • switch add-to-project from pull_request_target to pull_request
  • make GitHub App token generation non-fatal so a missing app install emits a warning instead of a red workflow
  • opt into Node 24 for JavaScript Actions and keep project writes behind the app token

Validation

  • mirrors the already-green FastLED/fbuild add-to-project workflow

Summary by CodeRabbit

  • Chores
    • Updated project board automation with enhanced error handling, fork-safe operations, and improved workflow reliability when syncing to the FastLED Projects board.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The workflow file is refactored to use the pull_request trigger instead of pull_request_target, enabling fork-safe behavior. Permissions are expanded to include pull-requests: read. Node 24 is enforced via environment settings. GitHub App token generation is made non-fatal, and conditional steps handle success (add to project board) or failure (warn and skip) outcomes.

Changes

GitHub Actions workflow for fork-safe project board integration

Layer / File(s) Summary
Workflow trigger, permissions, and conditional project addition
.github/workflows/add-to-project.yml
Pull request trigger changed from pull_request_target to pull_request for fork safety. Permissions expanded to include pull-requests: read. Node 24 forced via environment. App token generation made non-fatal with continue-on-error, and conditional steps either warn/skip (when App not installed) or proceed to actions/add-to-project@v1.0.2 (when token succeeds).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A workflow hops from target to request,
Fork-safe and gentle, putting logic to the test,
With Node at v24 and conditions in place,
The project board fills at a graceful pace! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harden FastLED project sync workflow' accurately reflects the main objectives: improving security by switching from pull_request_target to pull_request, making token generation non-fatal, and opting into Node 24.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/harden-project-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/add-to-project.yml:
- Line 1: The workflow file was flattened onto a single line breaking YAML
structure; restore proper YAML by putting top-level keys (name, on, env,
permissions, jobs) each on their own lines with correct indentation, re-indent
the jobs.add block and its steps, and ensure the step with id "app-token" and
the "Add to project" step (uses: actions/add-to-project@v1.0.2) are nested under
jobs -> add -> steps as a sequence; validate with a YAML linter/actionlint
before committing.
- Line 1: The skip warning message in the workflow step named "App not installed
— skipping project sync" incorrectly asserts the App is not installed when
actions/create-github-app-token (step id app-token) fails; update that step's
run output to a neutral message like "Skipping project sync: unable to create
App token (this can occur for fork PRs or when the private key secret is
unavailable)" and optionally include the step outcome to aid debugging, so
replace the current install-focused warnings with a neutral skip explanation
referencing the app-token creation failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df4f7e50-b06b-4422-8cd2-57478bad680c

📥 Commits

Reviewing files that changed from the base of the PR and between a97f131 and 98f89de.

📒 Files selected for processing (1)
  • .github/workflows/add-to-project.yml

with:
project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }}
github-token: ${{ steps.app-token.outputs.token }}
name: add-to-project # Auto-adds every new issue / PR to the FastLED Tracker project (#1). # # Auth: GitHub App "FastLED Project Sync" — scoped to Projects: read/write + # Contents/Issues/Pull requests: read. No expiration (App installation tokens # auto-rotate). The App ID lives in a repo variable; the private key lives in # a repo secret. # # Required configuration (already set on all 6 feeder repos): # vars.PROJECT_APP_CLIENT_ID = Iv23liL4dLxjYFwTNWKt # vars.PROJECT_OWNER = FastLED # vars.PROJECT_NUMBER = 1 # secrets.PROJECT_APP_PRIVATE_KEY = <PEM contents> # # To rotate the App's private key: # 1. On https://github.com/organizations/FastLED/settings/apps generate new key # 2. For each repo: gh secret set PROJECT_APP_PRIVATE_KEY --repo FastLED/<repo> < new.pem # 3. Revoke the old key in the App settings on: issues: types: [opened] pull_request: types: [opened] env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" # pull_request (not pull_request_target) — fork PRs run in the fork's context # without access to PROJECT_APP_PRIVATE_KEY, so they simply won't be # auto-added. That is an intentional security trade-off: pull_request_target # would run with base-repo secrets against fork-authored metadata, which is a # known exfiltration vector even when no code is checked out. permissions: contents: read pull-requests: read jobs: add: runs-on: ubuntu-latest if: ${{ vars.PROJECT_APP_CLIENT_ID != '' && vars.PROJECT_OWNER != '' }} steps: - name: Generate App token id: app-token continue-on-error: true uses: actions/create-github-app-token@v3 with: client-id: ${{ vars.PROJECT_APP_CLIENT_ID }} private-key: ${{ secrets.PROJECT_APP_PRIVATE_KEY }} owner: ${{ vars.PROJECT_OWNER }} - name: App not installed — skipping project sync if: ${{ steps.app-token.outcome != 'success' }} run: | echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'." echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project." - name: Add to project if: ${{ steps.app-token.outcome == 'success' }} uses: actions/add-to-project@v1.0.2 with: project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }} github-token: ${{ steps.app-token.outputs.token }} No newline at end of file

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 | 🔴 Critical | ⚡ Quick win

Restore YAML structure before merging.

Line 1 currently contains the entire workflow body, so on, permissions, and jobs are not valid top-level YAML mappings. GitHub won't load this workflow at all, which matches the actionlint failures.

🐛 Proposed fix
-name: add-to-project  # Auto-adds every new issue / PR to the FastLED Tracker project (`#1`). # # Auth: GitHub App "FastLED Project Sync" — scoped to Projects: read/write + # Contents/Issues/Pull requests: read. No expiration (App installation tokens # auto-rotate). The App ID lives in a repo variable; the private key lives in # a repo secret. # # Required configuration (already set on all 6 feeder repos): #   vars.PROJECT_APP_CLIENT_ID      = Iv23liL4dLxjYFwTNWKt #   vars.PROJECT_OWNER            = FastLED #   vars.PROJECT_NUMBER           = 1 #   secrets.PROJECT_APP_PRIVATE_KEY = <PEM contents> # # To rotate the App's private key: #   1. On https://github.com/organizations/FastLED/settings/apps generate new key #   2. For each repo: gh secret set PROJECT_APP_PRIVATE_KEY --repo FastLED/<repo> < new.pem #   3. Revoke the old key in the App settings  on:   issues:     types: [opened]   pull_request:     types: [opened]  env:   FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"  # pull_request (not pull_request_target) — fork PRs run in the fork's context # without access to PROJECT_APP_PRIVATE_KEY, so they simply won't be # auto-added. That is an intentional security trade-off: pull_request_target # would run with base-repo secrets against fork-authored metadata, which is a # known exfiltration vector even when no code is checked out. permissions:   contents: read   pull-requests: read  jobs:   add:     runs-on: ubuntu-latest     if: ${{ vars.PROJECT_APP_CLIENT_ID != '' && vars.PROJECT_OWNER != '' }}     steps:       - name: Generate App token         id: app-token         continue-on-error: true         uses: actions/create-github-app-token@v3         with:           client-id: ${{ vars.PROJECT_APP_CLIENT_ID }}           private-key: ${{ secrets.PROJECT_APP_PRIVATE_KEY }}           owner: ${{ vars.PROJECT_OWNER }}        - name: App not installed — skipping project sync         if: ${{ steps.app-token.outcome != 'success' }}         run: |           echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'."           echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project."        - name: Add to project         if: ${{ steps.app-token.outcome == 'success' }}         uses: actions/add-to-project@v1.0.2         with:           project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }}           github-token: ${{ steps.app-token.outputs.token }}
+name: add-to-project
+
+on:
+  issues:
+    types: [opened]
+  pull_request:
+    types: [opened]
+
+env:
+  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
+
+permissions:
+  contents: read
+  pull-requests: read
+
+jobs:
+  add:
+    runs-on: ubuntu-latest
+    if: ${{ vars.PROJECT_APP_CLIENT_ID != '' && vars.PROJECT_OWNER != '' }}
+    steps:
+      - name: Generate App token
+        id: app-token
+        continue-on-error: true
+        uses: actions/create-github-app-token@v3
+        with:
+          client-id: ${{ vars.PROJECT_APP_CLIENT_ID }}
+          private-key: ${{ secrets.PROJECT_APP_PRIVATE_KEY }}
+          owner: ${{ vars.PROJECT_OWNER }}
+
+      - name: App not installed — skipping project sync
+        if: ${{ steps.app-token.outcome != 'success' }}
+        run: |
+          echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'."
+          echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project."
+
+      - name: Add to project
+        if: ${{ steps.app-token.outcome == 'success' }}
+        uses: actions/add-to-project@v1.0.2
+        with:
+          project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }}
+          github-token: ${{ steps.app-token.outputs.token }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: add-to-project # Auto-adds every new issue / PR to the FastLED Tracker project (#1). # # Auth: GitHub App "FastLED Project Sync" — scoped to Projects: read/write + # Contents/Issues/Pull requests: read. No expiration (App installation tokens # auto-rotate). The App ID lives in a repo variable; the private key lives in # a repo secret. # # Required configuration (already set on all 6 feeder repos): # vars.PROJECT_APP_CLIENT_ID = Iv23liL4dLxjYFwTNWKt # vars.PROJECT_OWNER = FastLED # vars.PROJECT_NUMBER = 1 # secrets.PROJECT_APP_PRIVATE_KEY = <PEM contents> # # To rotate the App's private key: # 1. On https://github.com/organizations/FastLED/settings/apps generate new key # 2. For each repo: gh secret set PROJECT_APP_PRIVATE_KEY --repo FastLED/<repo> < new.pem # 3. Revoke the old key in the App settings on: issues: types: [opened] pull_request: types: [opened] env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" # pull_request (not pull_request_target) — fork PRs run in the fork's context # without access to PROJECT_APP_PRIVATE_KEY, so they simply won't be # auto-added. That is an intentional security trade-off: pull_request_target # would run with base-repo secrets against fork-authored metadata, which is a # known exfiltration vector even when no code is checked out. permissions: contents: read pull-requests: read jobs: add: runs-on: ubuntu-latest if: ${{ vars.PROJECT_APP_CLIENT_ID != '' && vars.PROJECT_OWNER != '' }} steps: - name: Generate App token id: app-token continue-on-error: true uses: actions/create-github-app-token@v3 with: client-id: ${{ vars.PROJECT_APP_CLIENT_ID }} private-key: ${{ secrets.PROJECT_APP_PRIVATE_KEY }} owner: ${{ vars.PROJECT_OWNER }} - name: App not installed — skipping project sync if: ${{ steps.app-token.outcome != 'success' }} run: | echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'." echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project." - name: Add to project if: ${{ steps.app-token.outcome == 'success' }} uses: actions/add-to-project@v1.0.2 with: project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }} github-token: ${{ steps.app-token.outputs.token }}
name: add-to-project
# Auto-adds every new issue / PR to the FastLED Tracker project (`#1`).
#
# Auth: GitHub App "FastLED Project Sync" — scoped to Projects: read/write +
# Contents/Issues/Pull requests: read. No expiration (App installation tokens
# auto-rotate). The App ID lives in a repo variable; the private key lives in
# a repo secret.
#
# Required configuration (already set on all 6 feeder repos):
# vars.PROJECT_APP_CLIENT_ID = Iv23liL4dLxjYFwTNWKt
# vars.PROJECT_OWNER = FastLED
# vars.PROJECT_NUMBER = 1
# secrets.PROJECT_APP_PRIVATE_KEY = <PEM contents>
#
# To rotate the App's private key:
# 1. On https://github.com/organizations/FastLED/settings/apps generate new key
# 2. For each repo: gh secret set PROJECT_APP_PRIVATE_KEY --repo FastLED/<repo> < new.pem
# 3. Revoke the old key in the App settings
on:
issues:
types: [opened]
pull_request:
types: [opened]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
# pull_request (not pull_request_target) — fork PRs run in the fork's context
# without access to PROJECT_APP_PRIVATE_KEY, so they simply won't be
# auto-added. That is an intentional security trade-off: pull_request_target
# would run with base-repo secrets against fork-authored metadata, which is a
# known exfiltration vector even when no code is checked out.
permissions:
contents: read
pull-requests: read
jobs:
add:
runs-on: ubuntu-latest
if: ${{ vars.PROJECT_APP_CLIENT_ID != '' && vars.PROJECT_OWNER != '' }}
steps:
- name: Generate App token
id: app-token
continue-on-error: true
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.PROJECT_APP_CLIENT_ID }}
private-key: ${{ secrets.PROJECT_APP_PRIVATE_KEY }}
owner: ${{ vars.PROJECT_OWNER }}
- name: App not installed — skipping project sync
if: ${{ steps.app-token.outcome != 'success' }}
run: |
echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'."
echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project."
- name: Add to project
if: ${{ steps.app-token.outcome == 'success' }}
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/${{ vars.PROJECT_OWNER }}/projects/${{ vars.PROJECT_NUMBER }}
github-token: ${{ steps.app-token.outputs.token }}
🧰 Tools
🪛 actionlint (1.7.12)

[error] 1-1: "jobs" section is missing in workflow

(syntax-check)


[error] 1-1: "on" section is missing in workflow

(syntax-check)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/add-to-project.yml at line 1, The workflow file was
flattened onto a single line breaking YAML structure; restore proper YAML by
putting top-level keys (name, on, env, permissions, jobs) each on their own
lines with correct indentation, re-indent the jobs.add block and its steps, and
ensure the step with id "app-token" and the "Add to project" step (uses:
actions/add-to-project@v1.0.2) are nested under jobs -> add -> steps as a
sequence; validate with a YAML linter/actionlint before committing.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a neutral skip warning.

On fork PRs, pull_request runs without secrets.PROJECT_APP_PRIVATE_KEY, so create-github-app-token is expected to fail even when the App is installed correctly. The current warning mislabels that expected path as an installation problem.

📝 Proposed fix
-      - name: App not installed — skipping project sync
+      - name: App token unavailable — skipping project sync
         if: ${{ steps.app-token.outcome != 'success' }}
         run: |
-          echo "::warning::FastLED Project Sync App is not installed on '${{ vars.PROJECT_OWNER }}'."
-          echo "::warning::Install it at https://github.com/organizations/${{ vars.PROJECT_OWNER }}/settings/installations to enable auto-add-to-project."
+          echo "::warning::Skipping project sync because the App token could not be created in this workflow context."
🧰 Tools
🪛 actionlint (1.7.12)

[error] 1-1: "jobs" section is missing in workflow

(syntax-check)


[error] 1-1: "on" section is missing in workflow

(syntax-check)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/add-to-project.yml at line 1, The skip warning message in
the workflow step named "App not installed — skipping project sync" incorrectly
asserts the App is not installed when actions/create-github-app-token (step id
app-token) fails; update that step's run output to a neutral message like
"Skipping project sync: unable to create App token (this can occur for fork PRs
or when the private key secret is unavailable)" and optionally include the step
outcome to aid debugging, so replace the current install-focused warnings with a
neutral skip explanation referencing the app-token creation failure.

@zackees zackees merged commit 4e0c7b0 into main Jun 1, 2026
2 checks passed
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

1 participant