-
Notifications
You must be signed in to change notification settings - Fork 801
ci: add explicit permissions to build-desktop workflow #1739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,9 @@ | |
| name: Build Desktop (reusable) | ||
| on: | ||
| workflow_call: | ||
| permissions: | ||
| contents: read | ||
| actions: write | ||
| inputs: | ||
|
Comment on lines
14
to
19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Validate the workflow YAML and confirm workflow_call.inputs is populated.
python - <<'PY'
import sys, yaml
with open('.github/workflows/build-desktop.yml') as f:
try:
doc = yaml.safe_load(f)
except yaml.YAMLError as e:
print("YAML parse error:", e); sys.exit(1)
wc = (doc.get('on') or doc.get(True) or {}).get('workflow_call') or {}
print("workflow_call.inputs keys:", list((wc.get('inputs') or {}).keys()))
print("top-level permissions:", doc.get('permissions'))
PYRepository: tinyhumansai/openhuman Length of output: 183 🏁 Script executed: # First, let's view the current build-desktop.yml around lines 14-25 to see exact indentation
sed -n '14,25p' .github/workflows/build-desktop.yml | cat -ARepository: tinyhumansai/openhuman Length of output: 321 🏁 Script executed: # Now test the corrected structure by creating a test YAML and validating it
python - <<'PY'
import sys, yaml
# Read current file
with open('.github/workflows/build-desktop.yml', 'r') as f:
content = f.read()
# Show what we have at lines 14-25
lines = content.split('\n')
print("Current lines 14-25:")
for i in range(13, min(25, len(lines))):
print(f"{i+1:3d}: {repr(lines[i])}")
print("\n" + "="*60)
print("Attempting to parse current file:")
try:
doc = yaml.safe_load(content)
print("ERROR: File parsed successfully (unexpected!)")
except yaml.YAMLError as e:
print(f"✓ Parse error confirmed: {e}")
PYRepository: tinyhumansai/openhuman Length of output: 648 🏁 Script executed: # Find the line containing "inputs:" to understand the structure
grep -n "inputs:" .github/workflows/build-desktop.yml | head -5Repository: tinyhumansai/openhuman Length of output: 81 🏁 Script executed: # Get broader context: lines 1-30
sed -n '1,30p' .github/workflows/build-desktop.yml | cat -nRepository: tinyhumansai/openhuman Length of output: 1391 🏁 Script executed: # Find where the on: block ends (jobs: keyword marks next top-level section)
grep -n "^jobs:" .github/workflows/build-desktop.ymlRepository: tinyhumansai/openhuman Length of output: 76 🏁 Script executed: # Get lines around the end of the on: block (inputs section)
sed -n '90,105p' .github/workflows/build-desktop.yml | cat -nRepository: tinyhumansai/openhuman Length of output: 843 🏁 Script executed: # Test the proposed fix by creating corrected YAML
python - <<'PY'
import yaml
# Create the corrected structure
corrected_yaml = """
on:
workflow_call:
inputs:
build_ref:
description: Git ref to check out for the build (tag or SHA).
type: string
required: true
permissions:
contents: read
actions: write
jobs:
build:
runs-on: ubuntu-latest
"""
print("Testing corrected YAML structure:")
try:
doc = yaml.safe_load(corrected_yaml)
wc = (doc.get('on') or {}).get('workflow_call') or {}
print(f"✓ YAML parses successfully")
print(f" workflow_call.inputs keys: {list((wc.get('inputs') or {}).keys())}")
print(f" top-level permissions: {doc.get('permissions')}")
except yaml.YAMLError as e:
print(f"✗ Parse error: {e}")
PYRepository: tinyhumansai/openhuman Length of output: 225 🏁 Script executed: # Now check line 587 to understand what permission is needed for gh release upload
sed -n '580,595p' .github/workflows/build-desktop.yml | cat -nRepository: tinyhumansai/openhuman Length of output: 811 🏁 Script executed: # Verify the permission scope: check if there are other uses of contents: write needed
grep -n "gh release\|contents: write\|actions: write" .github/workflows/build-desktop.ymlRepository: tinyhumansai/openhuman Length of output: 180 🏁 Script executed: # Check the current permission block in context (lines 13-20)
sed -n '13,20p' .github/workflows/build-desktop.yml | cat -nRepository: tinyhumansai/openhuman Length of output: 250 🏁 Script executed: # Test what the corrected permissions should be
python - <<'PY'
import yaml
# Test with correct permissions scope
corrected_yaml_with_full_inputs = """
on:
workflow_call:
inputs:
build_ref:
description: Git ref
type: string
required: true
with_updater:
description: desc
type: boolean
default: true
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
"""
print("Testing corrected YAML with contents: write (for gh release upload):")
try:
doc = yaml.safe_load(corrected_yaml_with_full_inputs)
wc = (doc.get('on') or {}).get('workflow_call') or {}
inputs = list((wc.get('inputs') or {}).keys())
perms = doc.get('permissions')
print(f"✓ YAML parses successfully")
print(f" workflow_call.inputs keys: {inputs}")
print(f" top-level permissions: {perms}")
if 'build_ref' in inputs and 'with_updater' in inputs:
print(f"✓ All inputs properly nested under workflow_call")
except yaml.YAMLError as e:
print(f"✗ Parse error: {e}")
PYRepository: tinyhumansai/openhuman Length of output: 241 🏁 Script executed: # Check what the actual fix should look like - find exact end of inputs block
sed -n '95,102p' .github/workflows/build-desktop.yml | cat -ARepository: tinyhumansai/openhuman Length of output: 402 🏁 Script executed: # Verify where inputs block ends to understand complete fix scope
sed -n '19,100p' .github/workflows/build-desktop.yml | grep -n "^ " | tail -1Repository: tinyhumansai/openhuman Length of output: 91 🏁 Script executed: # Check what permissions release upload actually requires
python - <<'PY'
# According to GitHub Actions documentation:
# - actions/checkout@v4: needs contents:read
# - actions/cache@v5: does NOT require actions:write (uses its own service token)
# - actions/upload-artifact@v5: does NOT require actions:write (uses artifact service, not GITHUB_TOKEN for uploads)
# - gh release upload: needs contents:write (releases are part of repository contents)
# The workflow uses "gh release upload" at line 587, so:
print("Permission requirements for this workflow:")
print("- contents:read (for actions/checkout)")
print("- contents:write (for gh release upload at line 587)")
print("- actions:write (NOT needed for cache, checkout, or upload-artifact)")
print("\nConclusion: permissions should be 'contents: write' (or 'contents: read' if release upload is disabled)")
print("The current 'contents: read + actions: write' is WRONG for two reasons:")
print(" 1. actions:write is unnecessary")
print(" 2. contents:read is insufficient for gh release upload (needs write)")
PYRepository: tinyhumansai/openhuman Length of output: 554 🏁 Script executed: # Final test: create a complete valid YAML structure matching the file
python - <<'PY'
import yaml
# Simulate the corrected structure with proper indentation
corrected = """---
name: Build Desktop (reusable)
on:
workflow_call:
inputs:
build_ref:
description: Git ref to check out for the build (tag or SHA).
type: string
required: true
tag:
description: Tag name
type: string
required: true
with_updater:
description: When true, set WITH_UPDATER=true
type: boolean
default: true
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
"""
print("Testing corrected structure (with contents:write):")
try:
doc = yaml.safe_load(corrected)
on_block = doc.get('on') or {}
wc = on_block.get('workflow_call') or {}
inputs = wc.get('inputs') or {}
perms = doc.get('permissions') or {}
print(f"✓ YAML parses successfully")
print(f" on.workflow_call.inputs: {list(inputs.keys())}")
print(f" permissions: {perms}")
print(f" jobs: {list(doc.get('jobs', {}).keys())}")
except yaml.YAMLError as e:
print(f"✗ Parse error: {e}")
PYRepository: tinyhumansai/openhuman Length of output: 229 🏁 Script executed: # Confirm the end of the on: block and structure
sed -n '14,100p' .github/workflows/build-desktop.yml | grep -E "^(on:| workflow_call:|permissions:|jobs:)" | head -5Repository: tinyhumansai/openhuman Length of output: 106 Critical: The file fails to parse with error "mapping values are not allowed here" at line 19. The current indentation places
Major: Permission scope is wrong and insufficient for The current
The permission block should use 🛠️ Proposed fix — move `permissions:` to top level after `on:` block on:
workflow_call:
inputs:
build_ref:
description: Git ref to check out for the build (tag or SHA).
type: string
required: true
tag:
description:
Tag name used by GH Release uploads (e.g. v1.2.4) and by the staging
standalone CLI artifact name (e.g. v1.2.4-staging).
type: string
required: true
with_updater:
description:
When true, set `WITH_UPDATER=true` so the Tauri bundler emits the
signed `.sig` updater artifacts the auto-updater consumes.
Production sets this and assembles `latest.json` in a follow-on
job; staging leaves it off because there is no manifest publish
step to consume the .sig files — producing them just wastes
signing time and pollutes the artifact tree.
type: boolean
default: true
+permissions:
+ contents: write
jobs:🧰 Tools🪛 actionlint (1.7.12)[error] 19-19: could not parse as YAML: mapping values are not allowed in this context (syntax-check) 🪛 YAMLlint (1.38.0)[error] 19-19: syntax error: mapping values are not allowed here (syntax) 🤖 Prompt for AI Agents |
||
| build_ref: | ||
| description: Git ref to check out for the build (tag or SHA). | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
What GITHUB_TOKEN permissions are required for actions/upload-artifact v5, actions/cache v5, andgh release uploadin 2026?💡 Result:
actions/upload-artifact@v5: contents: read (works even with empty permissions in private repos, but contents: read explicitly for safety) [1]. Official docs do not specify beyond general repo access, and artifact upload uses a special token but GITHUB_TOKEN needs contents: read for checkout/context [2][3]. actions/cache@v5: actions: write (for saving/restoring caches, as cache is managed under Actions API) [4][3]. Docs confirm no explicit permissions listed, but related tools require actions: read/write [5].
gh release upload: contents: write (required for creating/updating releases and uploading assets via REST API /releases endpoint) [6][7][8][9]. Uses GH_TOKEN which maps to contents permission for repo write operations including releases [10].Citations:
actions: read/writepermissions are required to use gh-actions-cache in gh actions CI actions/gh-actions-cache#85🌐 Web query:
Does actions/cache@v5 use GITHUB_TOKEN or a separate cache service token? GitHub Actions permissions💡 Result:
actions/cache@v5 uses the GITHUB_TOKEN (via ${{ github.token }} context) to access GitHub's built-in cache service. It does not require a separate cache service token. The action internally calls GitHub's REST API endpoints for Actions cache (e.g., list, get, create, update caches), which require the GITHUB_TOKEN to have
actions:read(for restore) andactions:write(for save) permissions [1][2][3][4][5][6]. By default, GITHUB_TOKEN has these permissions unless restricted in the workflow [3][7]. No external cache service or additional token is needed or mentioned in official docs or the action's README [8][9][2][10][11]. For explicit cache management (e.g., delete via gh cache),actions:writemust be granted [1][4][7].Citations:
actions: read/writepermissions are required to use gh-actions-cache in gh actions CI actions/gh-actions-cache#85🌐 Web query:
GitHub Actions cache token mechanism in 2026 - GITHUB_TOKEN vs runner cache service💡 Result:
In 2026, GitHub Actions caching uses an internal “Actions cache service” authentication token (ACTIONS_RUNTIME_TOKEN / bearer token), not the same credential that you normally use as GITHUB_TOKEN for GitHub API calls. 1) What GITHUB_TOKEN is (and is for) - GITHUB_TOKEN is the automatically generated token in a workflow job; it can be used for authenticated GitHub API calls and its permissions are controlled via the workflow’s permissions settings. It expires after the job completes and is scoped to the repository that contains the workflow.[1][2] 2) What the cache service token is (and is for) - The cache action (actions/cache) talks to GitHub’s cache backend service over an internal HTTP API. In the actions/toolkit cache HTTP client used by actions/cache, the client reads a token from the environment variable ACTIONS_RUNTIME_TOKEN and sends it as a Bearer credential in requests to the cache service.[3] - The Docker BuildKit “gha” cache backend docs likewise describe cache v2 inputs as coming from ACTIONS_RESULTS_URL and ACTIONS_RUNTIME_TOKEN, describing token “see authentication”.[4][5] 3) How the two differ in practice (GITHUB_TOKEN vs cache token) - GITHUB_TOKEN: intended for GitHub API authentication and is the token you reference as secrets.GITHUB_TOKEN / github.token.[2] - Cache service token (ACTIONS_RUNTIME_TOKEN): intended for calling the cache/artifacts/runtime service endpoints that actions/cache uses. It’s provided by the runner for workflow/action execution contexts, and it is the token wired into the cache HTTP client (Bearer) to access the cache service endpoints.[3][6] 4) Does actions/cache fall back to GITHUB_TOKEN? - The published toolkit cache client code shown above specifically uses ACTIONS_RUNTIME_TOKEN as the token it loads for cache service communication.[3] - I did not find an authoritative 2026 doc explicitly stating “ACTIONS_RUNTIME_TOKEN == GITHUB_TOKEN”. What is clearly documented is that cache actions depend on their runtime environment variables for cache service access, while GITHUB_TOKEN is for GitHub API authentication.[3][2][5] 5) Security boundary note (why GitHub doesn’t want you storing secrets in caches) - GitHub explicitly recommends not storing sensitive information (including access tokens/credentials) in cached files because cache contents can be accessed via pull requests from forks / readers.[7] Bottom line - Use GITHUB_TOKEN for GitHub API calls. - For GitHub Actions cache upload/download via actions/cache (and cache service v2 clients), the authentication mechanism is the bearer token provided to the runtime as ACTIONS_RUNTIME_TOKEN (with cache service URLs like ACTIONS_RESULTS_URL), not GITHUB_TOKEN by itself.[3][4][2]
Citations:
ACTIONS_RUNTIME_TOKENand runtime env vars toScriptHandleractions/runner#4325🌐 Web query:
Official GitHub documentation actions/cache@v5 GITHUB_TOKEN permissions requirements💡 Result:
Official GitHub documentation / behavior:
actions/cache@v5doesn’t require you to passGITHUB_TOKENexplicitly. GitHub Actions provides aGITHUB_TOKENautomatically to each job, and workflow/jobpermissions:controls what that token can do for the job [1][2]. Whatpermissions:you need foractions/cache@v5: - To allow theGITHUB_TOKENto create/save or manage GitHub Actions caches used byactions/cache, you generally need theactionsscope to be writable: setpermissions: actions: write(commonly alongsidecontents: readif you also check out code). - This is supported by GitHub’s mechanism for configuringGITHUB_TOKENpermissions viapermissions:at the workflow/job level [2][3]. - Cache-related guidance indicatesactions: writeis needed for cache management operations that hit the Actions cache REST API (e.g., delete or other operations beyond restore). For example, a documented fix statespermissions: actions: writeas the needed setting [4]. - GitHub Actions “actions cache” operations (like delete) also referenceactions:writeas the required permission in practice [5]. Example (least privilege for typical cache usage + checkout): - Job-level permissions: -contents: read-actions: writeNotes / gotchas: -GITHUB_TOKENis a GitHub App installation access token whose permissions are limited; you can control those permissions withpermissions:[1][6]. - Forked pull requests are treated specially and may receive reducedGITHUB_TOKENpermissions (often read-only), which can restrict cache write/save behavior depending on the trigger/context [6][7].Citations:
actions: read/writepermissions are required to use gh-actions-cache in gh actions CI actions/gh-actions-cache#85🏁 Script executed:
Repository: tinyhumansai/openhuman
Length of output: 1621
🏁 Script executed:
Repository: tinyhumansai/openhuman
Length of output: 364
Fix permissions block:
contents: writerequired forgh release upload(line 587) — currentcontents: readwill fail with 403.The workflow calls
gh release uploadat line 587 and viascripts/release/upload-macos-artifacts.sh, which requirescontents: writeon the releases API. With onlycontents: read, release uploads will fail on the first run withwith_release_upload: true.The
actions: writescope is unnecessary. Whileactions/cache@v5appears in this workflow (5 times), it uses the runner's internalACTIONS_RUNTIME_TOKEN, notGITHUB_TOKENpermissions—the cache service operates independently of thepermissions:block.Minimal correct set:
🛡️ Corrected scopes
If you prefer least-privilege granularity, scope
contents: writeonly to the release upload steps via step-levelpermissions:and leave the workflow default atcontents: read.📝 Committable suggestion
🤖 Prompt for AI Agents