feat: Composite action for synchronizing SDK snippets#82
Merged
kinyoklion merged 5 commits intomainfrom May 1, 2026
Merged
Conversation
New action at actions/sync-snippets/. Resolves the latest launchdarkly/sdk-meta `snippets/*` GitHub Release, downloads the platform's signed binary, cosign-verifies it (keyless / OIDC, identity pinned to sdk-meta's release-please workflow), runs `snippets render` against the consumer checkout, and opens or updates a sync PR via peter-evans/create-pull-request when the render produced any diff. Designed for gonfalon (and future ld-docs-private use); both call this with `@main`. Daily cron + workflow_dispatch on the consumer side keeps the consumer current without a coordinated rollout. Implementation notes: - The `snippets` CLI binary embeds the canonical sdks/ tree at build time (sdk-meta side, separate PR). One artifact, one signature check, atomic content + engine pinning. - `--certificate-identity-regexp` matches sdk-meta's release-please workflow path on main, so a token leaked from any other workflow cannot produce a matching OIDC claim. - Per-platform archive name resolution mirrors goreleaser's default template (snippets_<version>_<os>_<arch>.tar.gz).
The CLI no longer asks each sdk.yaml which file in the consumer to rewrite (see launchdarkly/sdk-meta#405); instead the consumer declares its own list of search roots. Plumb that through: - New required input `entrypoints` (newline-separated list of directories, relative to $GITHUB_WORKSPACE). - Render step parses the input into individual `--entrypoint=` flags and passes them to `snippets render`. - Empty / whitespace-only lines are tolerated and skipped; an empty list fails loudly. - README + the in-action usage block updated to show the new shape. Consumer wiring becomes: - uses: launchdarkly/gh-actions/actions/sync-snippets@main with: entrypoints: | static/ld/components/getStarted github-token: ${{ secrets.GITHUB_TOKEN }}
The upstream sdk-meta release pipeline is moving from cosign keyless sidecars to GitHub's first-party SLSA build-provenance attestation (launchdarkly/sdk-meta#409). Match it on the verifier side: - Replace `cosign verify-blob` with `gh attestation verify --signer- workflow launchdarkly/sdk-meta/.github/workflows/release-please.yml`. Same identity pinning, but the attestation is fetched from sdk-meta's repo-level attestation store rather than as `.sig`/`.pem` release assets. - Drop the `sigstore/cosign-installer` step. `gh` ships preinstalled on every GitHub-hosted runner. - Drop the `id-token: write` permission from the consumer wiring example — verification is read-only against sdk-meta's attestation store and doesn't need an OIDC token. - Update README copy to match.
The action manifest's description: field is template-evaluated at action-load
time. Embedding a copy-paste-able workflow snippet that contained
${{ secrets.GITHUB_TOKEN }} caused 'Unrecognized named-value: secrets'
parse failures whenever a consumer used the action — secrets aren't in
scope when the manifest itself is being loaded.
Replace the inline workflow example with a prose pointer to README.md,
which carries the full consumer wiring snippet.
keelerm84
approved these changes
May 1, 2026
…ution A 'latest' lookup that catches sdk-meta's release-pipeline mid-publish window would point at a draft release whose archives and attestations haven't uploaded yet — and the subsequent 'gh release download' / 'gh attestation verify' would fail noisily but pointlessly. Pre-releases are excluded under the same logic: consumers asking for 'latest' want the latest stable, not a release-candidate the pipeline cut for testing. Per @keelerm84 review on PR #82.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New composite action at
actions/sync-snippets/. Consumers call it viaworkflow_dispatch(or a daily cron) to stay current with snippet changes published bylaunchdarkly/sdk-meta.How it works
snippets/v*GitHub Release onlaunchdarkly/sdk-meta(override withversion:).snippets_<version>_<os>_<arch>.tar.gz).gh attestation verify --signer-workflow launchdarkly/sdk-meta/.github/workflows/release-please.yml. Sigstore-backed keyless OIDC under the hood, but nocosigninstall required —ghships preinstalled on every GitHub-hosted runner.snippets render --target=<adapter> --entrypoint=<dir>...with one--entrypoint=flag per non-empty line of theentrypoints:input. The CLI walks each directory, picks up files containing theSDK_SNIPPET:RENDER:sentinel, and rewrites their marked regions in place. The binary embeds the canonicalsdks/tree at build time, so there is nothing else to fetch.peter-evans/create-pull-requestwhen the render produced any diff. Empty diff → exit 0, no PR.Inputs / outputs
See
actions/sync-snippets/README.md. Required:entrypoints,github-token. Everything else has a sensible default.How the supply chain is locked down
gh attestation verify --signer-workflow launchdarkly/sdk-meta/.github/workflows/release-please.ymlrejects any artifact whose attestation was issued by a different workflow file. A token leaked from any other workflow in any other repo cannot produce a matching OIDC claim.--sdks=flag is optional; when omitted (the default for this action) it reads fromembed.FS. Pinning a release version pins both the engine and the snippet content atomically — no separatesdks/checkout step.Depends on
go build+actions/attest-build-provenance) — merged.snippets/v0.2.1is the first published release this action can consume.End-to-end validation
Wired up in a private sandbox repo (
launchdarkly/learn-release-please) via:snippet-test/getStarted.tsx— TSX file with twoSDK_SNIPPET:RENDER:markers (python-server-sdk/getting-started/mkdirwith no inputs, plus.../installwith one optionalversionprop) seeded withhash=0 version=0.0.0placeholders..github/workflows/sync-snippets.yml—workflow_dispatchreferencing this action via@rlamb/sync-snippets-action.Sandbox PR #89 merged the harness; the next dispatch (run 25190361399) ran green and the action opened sandbox PR #91, rewriting both markers with their
v0.2.1content. Both render modes were exercised — bare-JSX-text (no inputs) and template-literal-with-ternary (optional input →${version ? \==${version}` : ''}`).Test plan
learn-release-pleaseopens a PR with rewritten markers (run #25190361399 → PR #91).gh attestation verifyagainst the published v0.2.1 archive succeeds with the--signer-workflowpin.peter-evans/create-pull-requestalready opened a PR — expect: same branch updated, no second PR opened.snippets verifyflags;renderjust re-renders).version:to a non-existent tag — expect: clean failure at the resolve step.Known sharp edges
${{ ... }}in the action manifest'sdescription:field is template-evaluated at action-load time. Don't put example workflow snippets containingsecrets.*intodescription:— those parse-fail withUnrecognized named-value: 'secrets'. Fixed in a0ad8bb by moving the inline example to README.md.peter-evans/create-pull-requestto open the sync PR; otherwise the PR-open step is silently blocked by the defaultGITHUB_TOKENpolicy.