{"id":"b67d5c73-191b-430b-8a49-e27b3c849e8c","shortId":"fMFZLW","kind":"skill","title":"gh-actions","tagline":"GitHub Actions best practices — current action versions, caching, security, and common patterns. Activate when writing or modifying GitHub Actions workflows.","description":"# GitHub Actions Best Practices\n\nWhen writing or modifying GitHub Actions workflows (`.github/workflows/*.yml`), follow these guidelines.\n\n## Action Versions\n\nNever guess action versions. Before writing or updating a workflow, check the latest release for each action you use:\n\n```bash\ngh api repos/{owner}/{action}/releases/latest --jq '.tag_name'\n```\n\nThe `{owner}/{action}` maps directly to the GitHub repo — e.g. `actions/checkout` lives at `github.com/actions/checkout`.\n\nFor example:\n```bash\ngh api repos/actions/checkout/releases/latest --jq '.tag_name'\ngh api repos/actions/setup-node/releases/latest --jq '.tag_name'\n```\n\nUse the latest major version (e.g. if the latest tag is `v6.3.0`, use `v6`). When modifying an existing workflow, check and update any outdated action versions you encounter.\n\n## Security\n\n- Always set top-level `permissions` to least privilege. Start with `permissions: {}` and add only what's needed:\n  ```yaml\n  permissions:\n    contents: read\n  ```\n- Never use `permissions: write-all` or omit permissions entirely\n- Use `pull_request`, not `pull_request_target`. Only use `pull_request_target` when the workflow must write to the base repo from a fork — it runs with write access and secrets from the base branch\n- Never interpolate untrusted input directly in `run:` blocks — use environment variables instead:\n  ```yaml\n  # Bad — expression injection\n  - run: echo \"${{ github.event.pull_request.title }}\"\n\n  # Good — safe via environment variable\n  - run: echo \"$TITLE\"\n    env:\n      TITLE: ${{ github.event.pull_request.title }}\n  ```\n- Pin third-party actions (outside `actions/` and `github/`) to a full commit SHA to prevent tag-rewriting attacks. Use [pinact](https://github.com/suzuki-shunsuke/pinact) to automate this — write workflows with version tags, then run `pinact run` to replace them with SHAs:\n  ```yaml\n  # Before pinact\n  - uses: shivammathur/setup-php@v2\n\n  # After pinact run\n  - uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0\n  ```\n- Secrets are not available to `pull_request` workflows from forks — this is intentional; do not work around it with `pull_request_target`\n\n## Caching\n\n- `actions/setup-node`, `actions/setup-python`, `actions/setup-go`, and `actions/setup-java` all have built-in caching via the `cache` input — prefer this over separate `actions/cache` steps:\n  ```yaml\n  - uses: actions/setup-node@v6  # check latest version\n    with:\n      node-version-file: .node-version\n      cache: npm\n  ```\n- Only use `actions/cache` directly when you need custom cache keys or paths\n\n## Common Patterns\n\n### Concurrency\n\nCancel in-progress runs for the same branch to save minutes:\n```yaml\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n```\n\nFor deployment workflows, don't cancel in progress — queue instead:\n```yaml\nconcurrency:\n  group: deploy-${{ github.ref }}\n  cancel-in-progress: false\n```\n\n### Matrix strategies\n\nUse `fail-fast: false` when you want all matrix combinations to complete:\n```yaml\nstrategy:\n  fail-fast: false\n  matrix:\n    node-version: [22, 24]\n```\n\n### Reusable workflows\n\nPrefer `workflow_call` for shared CI logic across repos instead of duplicating steps:\n```yaml\njobs:\n  test:\n    uses: org/.github/.github/workflows/test.yml@v1\n    with:\n      node-version: 24\n```\n\n### Triggering\n\n- `pull_request` runs against the merge commit — use this for CI validation\n- `push` on the default branch runs post-merge — use this for deployments, publishing, or cache warming\n- Filter by paths when the workflow only applies to certain files:\n  ```yaml\n  on:\n    push:\n      paths:\n        - 'src/**'\n        - 'package.json'\n  ```\n\n### Timeouts\n\nAlways set `timeout-minutes` on jobs. The default is 360 minutes (6 hours), which can burn through Actions minutes on a hung job:\n```yaml\njobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n```\n\nTreat all workflow content as code — review changes carefully before committing.","tags":["actions","retlehs","agent-skills","github-actions","skills"],"capabilities":["skill","source-retlehs","skill-gh-actions","topic-agent-skills","topic-github-actions","topic-skills"],"categories":["gh-actions"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/retlehs/gh-actions","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add retlehs/gh-actions","source_repo":"https://github.com/retlehs/gh-actions","install_from":"skills.sh"}},"qualityScore":"0.455","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 10 github stars · SKILL.md body (3,997 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:08:19.876Z","embedding":null,"createdAt":"2026-05-09T01:05:43.255Z","updatedAt":"2026-05-18T19:08:19.876Z","lastSeenAt":"2026-05-18T19:08:19.876Z","tsv":"'/actions/checkout':86 '/releases/latest':67 '/suzuki-shunsuke/pinact)':252 '15':538 '22':426 '24':427,453 '360':512 '6':514 'access':191 'across':437 'action':3,5,9,22,25,33,40,44,58,66,73,126,232,234,520 'actions/cache':325,346 'actions/checkout':81 'actions/setup-go':308 'actions/setup-java':310 'actions/setup-node':306,329 'actions/setup-python':307 'activ':16 'add':144 'alway':131,502 'api':63,91,97 'appli':491 'around':299 'attack':247 'autom':254 'avail':286 'bad':211 'base':182,196 'bash':61,89 'best':6,26 'block':205 'branch':197,367,471 'built':314 'built-in':313 'burn':518 'cach':11,305,316,319,342,352,482 'call':432 'cancel':359,377,386,397 'cancel-in-progress':376,396 'care':547 'certain':493 'chang':546 'check':52,121,331 'ci':435,465 'code':544 'combin':413 'commit':240,461,549 'common':14,356 'complet':415 'concurr':358,372,392 'content':151,542 'current':8 'custom':351 'default':470,510 'deploy':382,394,479 'direct':75,202,347 'duplic':441 'e.g':80,107 'echo':215,223 'encount':129 'entir':162 'env':225 'environ':207,220 'exampl':88 'exist':119 'express':212 'fail':405,419 'fail-fast':404,418 'fals':400,407,421 'fast':406,420 'fcafdd6392932010c2bd5094439b8e33be2a8a09':281 'file':338,494 'filter':484 'follow':37 'fork':186,292 'full':239 'gh':2,62,90,96 'gh-action':1 'github':4,21,24,32,78,236 'github.com':85,251 'github.com/actions/checkout':84 'github.com/suzuki-shunsuke/pinact)':250 'github.event.pull_request.title':216,227 'github.ref':375,395 'github.workflow':374 'github/workflows':35 'good':217 'group':373,393 'guess':43 'guidelin':39 'hour':515 'hung':524 'in-progress':360 'inject':213 'input':201,320 'instead':209,390,439 'intent':295 'interpol':199 'job':444,508,525,527 'jq':68,93,99 'key':353 'latest':54,104,110,332,534 'least':138 'level':135 'live':82 'logic':436 'major':105 'map':74 'matrix':401,412,422 'merg':460,475 'minut':370,506,513,521,537 'modifi':20,31,117 'must':178 'name':70,95,101 'need':148,350 'never':42,153,198 'node':336,340,424,451 'node-vers':339,423,450 'node-version-fil':335 'npm':343 'omit':160 'org/.github/.github/workflows/test.yml':447 'outdat':125 'outsid':233 'owner':65,72 'package.json':500 'parti':231 'path':355,486,498 'pattern':15,357 'permiss':136,142,150,155,161 'pin':228 'pinact':249,263,272,277 'post':474 'post-merg':473 'practic':7,27 'prefer':321,430 'prevent':243 'privileg':139 'progress':362,379,388,399 'publish':480 'pull':164,167,172,288,302,455 'push':467,497 'queue':389 'read':152 'releas':55 'replac':266 'repo':64,79,183,438 'repos/actions/checkout/releases/latest':92 'repos/actions/setup-node/releases/latest':98 'request':165,168,173,289,303,456 'reusabl':428 'review':545 'rewrit':246 'run':188,204,214,222,262,264,278,363,457,472,530 'runs-on':529 'safe':218 'save':369 'secret':193,283 'secur':12,130 'separ':324 'set':132,503 'sha':241 'share':434 'shas':269 'shivammathur/setup-php':274,280 'skill' 'skill-gh-actions' 'source-retlehs' 'src':499 'start':140 'step':326,442 'strategi':402,417 'tag':69,94,100,111,245,260 'tag-rewrit':244 'target':169,174,304 'test':445,528 'third':230 'third-parti':229 'timeout':501,505,536 'timeout-minut':504,535 'titl':224,226 'top':134 'top-level':133 'topic-agent-skills' 'topic-github-actions' 'topic-skills' 'treat':539 'trigger':454 'true':380 'ubuntu':533 'ubuntu-latest':532 'untrust':200 'updat':49,123 'use':60,102,114,154,163,171,206,248,273,279,328,345,403,446,462,476 'v1':448 'v2':275 'v2.37.0':282 'v6':115,330 'v6.3.0':113 'valid':466 'variabl':208,221 'version':10,41,45,106,127,259,333,337,341,425,452 'via':219,317 'want':410 'warm':483 'work':298 'workflow':23,34,51,120,177,257,290,383,429,431,489,541 'write':18,29,47,157,179,190,256 'write-al':156 'yaml':149,210,270,327,371,391,416,443,495,526 'yml':36","prices":[{"id":"e38ee6e5-1235-42fc-a8a5-082604f93688","listingId":"b67d5c73-191b-430b-8a49-e27b3c849e8c","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"retlehs","category":"gh-actions","install_from":"skills.sh"},"createdAt":"2026-05-09T01:05:43.255Z"}],"sources":[{"listingId":"b67d5c73-191b-430b-8a49-e27b3c849e8c","source":"github","sourceId":"retlehs/gh-actions","sourceUrl":"https://github.com/retlehs/gh-actions","isPrimary":false,"firstSeenAt":"2026-05-09T01:05:43.255Z","lastSeenAt":"2026-05-18T19:08:19.876Z"}],"details":{"listingId":"b67d5c73-191b-430b-8a49-e27b3c849e8c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"retlehs","slug":"gh-actions","github":{"repo":"retlehs/gh-actions","stars":10,"topics":["agent-skills","github-actions","skills"],"license":null,"html_url":"https://github.com/retlehs/gh-actions","pushed_at":"2026-04-13T20:45:22Z","description":"AI agent skill for GitHub Actions best practices — current versions, security, caching, and common patterns","skill_md_sha":"ff6fec680f6adc99e924e9c2cc1b373938f329c1","skill_md_path":"SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/retlehs/gh-actions"},"layout":"root","source":"github","category":"gh-actions","frontmatter":{"name":"gh-actions","description":"GitHub Actions best practices — current action versions, caching, security, and common patterns. Activate when writing or modifying GitHub Actions workflows."},"skills_sh_url":"https://skills.sh/retlehs/gh-actions"},"updatedAt":"2026-05-18T19:08:19.876Z"}}