Publish a reusable GitHub Action
Package a step you keep redoing — "check this CSV against our schema", "post a summary to Slack when CI passes" — into something anyone drops into their own CI in three lines. They add
uses: your-org/your-action@v1to a workflow file and your step runs on every push, no copying, no setup. You write it once, tag a version, and optionally list it on the GitHub Marketplace so people can find it.Time: ~3 min to your agent to scaffold + test one. The un-delegable bits are deciding the version is right and ticking the Marketplace checkbox in the browser — ~5 min once. You'll need: Claude Code, a GitHub account, the
ghCLI, and a clear idea of the one step you want others to reuse. Last verified: 2026-06-07 · commands checked against docs.github.com/actions (creating + publishing actions).[confirmed]New to all this? Do Set up Claude Code first (~10 min once), then come back. Just want to share the code, not a CI step? A GitHub repository is simpler. Want a command people run in a terminal instead? Package a CLI tool.
Before you begin
- Claude Code running in an empty folder — ~10 min once. It scaffolds the action, writes a test workflow, and tags the release.
- A GitHub account — ~10 min once. The action lives in a repo under your name or org.
- The
ghCLI connected — ~3 min once, so your agent can create the repo and cut a release for you.[confirmed] - One clear step to reuse. Name the single thing it does: "lint our YAML", "run our schema check", "notify Slack on failure". An action can take inputs and produce outputs, but start with one job.
[confirmed]
New to handing setup to an agent? Read How to ask your agent — one screen, then come back.
What a GitHub Action actually is
A small program that lives in its own public repo with one metadata file — action.yml at the repo root — describing the step: its name, its inputs, and how it runs. Anyone then references it from their workflow with one line (uses: your-org/your-action@v1) and GitHub fetches and runs it as a step in their pipeline. You publish the step; they wire it into their CI. [confirmed]
There are three flavours, set by one line (runs.using) in action.yml:
- Composite (
using: "composite") — a bundle of ordinary shell/CLI steps. Reach for this first: no build, no container, just the commands you already run.[confirmed] - JavaScript (
using: "node20") — a Node program. Pick it when the logic is real code, not a few commands. Runs on Ubuntu, Windows, and macOS runners.[confirmed] - Docker (
using: "docker") — your step in a container, for a fixed toolchain. Catch: Linux runners only.[confirmed]
The whole rest of this page uses the composite path, because it's the shortest route from "a step I run" to "a step others run".
The default: ask your agent to scaffold it
With Claude Code running in an empty folder, say:
Create a reusable GitHub composite action in this folder.
Put an action.yml at the root with a name, description, one input called
"path" (default "."), and a runs block using "composite" that runs my
schema-check script over that path with shell: bash. Add a README showing
the `uses:` line people put in their workflow. Then create a new PUBLIC
repo for it with the gh CLI, push, and tell me the repo name.
Swap in your own step — its name, its inputs (and defaults), and the commands it runs. Your agent writes action.yml (the metadata file is the action — it tells GitHub what the step takes and how to run it), a README with the copy-paste uses: line, creates the public repo with gh repo create, and pushes. [confirmed]
Why public: an action others reference has to be in a public repo. A private action only works inside the same repo or org that owns it. [confirmed]
Test it in a real workflow
Don't ship untested. The fastest check is to run the action against itself — add a tiny workflow to the action's own repo that uses it.
- Add a test workflow. Ask your agent: "Add a workflow at
.github/workflows/test.ymlthat checks out the repo and runs this action withuses: ./so it tests the local copy on every push." The./means "the action in this same repo", so you test before any version exists.[confirmed] - Push and watch it run. Your agent runs
gh workflow runor just pushes; check the result withgh run watch(or the Actions tab). Green means the step works.[confirmed] - Remove the test workflow before publishing to Marketplace (as a precaution) — see the catch under List it on the Marketplace. For a non-Marketplace action you can leave it.
[unclear](the workflow-file rule isn't in GitHub's current docs and some report listing with one present — checked 2026-06-08; removing it first is the safe default)
That round-trip — action written, workflow runs it, job goes green — is the whole test. If it passes here, it passes in someone else's pipeline.
Tag a version so people can pin to it
People reference your action by a tag, not a branch — so cut one. GitHub's recommendation: publish a release on a semantic-version tag like v1.2.3, and keep a moving major-version tag (v1) pointed at the latest release on that major line. [confirmed]
Tell your agent:
Cut the first release. Tag it v1.0.0 and also create (or move) a v1 tag
pointing at the same commit, then publish a GitHub release for v1.0.0 with
gh release create. From now on, "release a new version" should bump the
version and re-point v1 to it.
Why two tags: v1.0.0 is an exact, frozen point; v1 slides forward as you ship fixes. That gives your recipients a choice — pin hard or ride the major line:
@v1— latest release on the v1 line; gets your bug fixes automatically. The common choice.[confirmed]@v1.2.3— frozen to one exact release; never moves.[confirmed]@<full-commit-sha>— the most locked-down, immutable reference; what security-sensitive teams pin to, since a tag can be re-pointed.[confirmed]@main— a branch, always the tip. Avoid telling people to use this — it can change under them mid-pipeline.[confirmed]
What your recipient does
They add three lines to their own workflow file (.github/workflows/something.yml) — no install, no account beyond the GitHub one they already have to run CI:
- uses: your-org/your-action@v1
with:
path: ./data
That's the entire share. Paste those lines (or your README link) into chat, an email, a doc. On their next push, your step runs in their pipeline. Their agent can wire it in for them — "add the your-org/your-action@v1 step to our test workflow" — in ~1 min. [confirmed]
You're done when
A green workflow run somewhere — your test repo, or a recipient's — shows uses: your-org/your-action@v1 executing your step. From there, anyone adds three lines and it runs on every push; you never demo it by hand. [confirmed]
Optional: list it on the Marketplace
Listing puts your action in GitHub's searchable catalog so strangers find it. It's a finishing touch — an unlisted action shared by uses: line works identically. To list, the repo must clear a few rules:
- Public repo, with a single
action.yml(oraction.yaml) at the root — sub-folder actions don't get listed.[confirmed] - No workflow files in the repo (the commonly-reported gotcha): the
.github/workflows/test.ymlyou added above is widely said to block the listing, so remove it before you list. GitHub's current docs don't state this rule and some people report listing with a workflow present — so if the listing succeeds with one left in, you've lost nothing; removing it first just rules the snag out.[unclear](not in GitHub's current publishing docs; community reports conflict — checked 2026-06-08) - A unique
nameinaction.yml— it can't match an existing Marketplace action, a category, or a GitHub user/org you don't own.[confirmed] - 2FA on your account and a one-time acceptance of the Marketplace Developer Agreement — "Publishing requires you to use two-factor authentication."
[confirmed]
Then the publish itself is one click your agent can't do for you: open the repo's action.yml on github.com, click Draft a release, tick "Publish this Action to the GitHub Marketplace", pick a category, and publish. It goes live immediately — GitHub doesn't review it. ~5 min once. [confirmed]
If it doesn't work
- Recipient's workflow fails: "Can't find 'action.yml'" / "unable to resolve action" → the reference is wrong or the repo isn't reachable. The
action.ymlmust be at the repo root (not a sub-folder, for a top-level action), the repo must be public, and theuses:path must beowner/repo@refexactly. Check the repo opens in a browser without logging in.[confirmed] - "Unable to resolve action … @v1" — the tag doesn't exist → you pushed a branch but never tagged.
@v1is a tag;@mainis a branch. Cut the release (gh release create v1.0.0) and create thev1tag. Tell your agent "tag and release it."[confirmed] - It worked, then silently changed behaviour for a recipient → they pinned to
@main(a moving branch) or you re-pointedv1with a breaking change. Steer recipients to@v1for fixes-only, or@v1.2.3/ a commit SHA to freeze. Keep breaking changes on a new major (v2).[confirmed] - Marketplace "Publish" is greyed out / blocked → usually one of the listing rules: the repo is private (only public repos show the option), the file is
action.yaml/not at the root, the name collides with an existing listing, 2FA isn't on, or you haven't accepted the Marketplace Developer Agreement. A leftover workflow file is also worth removing as a precaution (commonly reported, though not in GitHub's current docs — see above). Fix the one it names and retry.[confirmed]on the documented rules;[unclear]on the workflow-file one - Composite step fails with "shell: required" or runs nothing → every
run:step in a composite action must declareshell:(e.g.shell: bash). Tell your agent "add shell: bash to each run step."[confirmed] - The action can't push/comment/etc. — "Resource not accessible by integration" / 403 → the action needs more than read permission on the recipient's repo. They grant it in their workflow with a
permissions:block (e.g.permissions: { contents: write }) or by passing a token viawith:. Document which permissions your action needs in your README.[confirmed] - Anything else → GitHub's Creating actions and metadata syntax reference walks every field. Paste the failed run's log to your agent — reading CI errors is what it's best at.
Prefer to do it by hand?
No agent — just you, following GitHub's quickstart. The minimum for a composite action:
-
Make a public repo and add
action.ymlat its root:name: 'Schema Check' description: 'Validate files against our schema' inputs: path: description: 'Folder to check' required: false default: '.' runs: using: "composite" steps: - run: ./schema-check.sh "${{ inputs.path }}" shell: bash -
Test it with a throwaway
.github/workflows/test.ymlthat runsuses: ./on push — then delete it before listing.[confirmed] - Tag and release.
git tag v1.0.0 && git tag v1 && git push --tags, then Draft a release on github.com forv1.0.0.[confirmed] - Share the line
uses: you/schema-check@v1. To list on the Marketplace, tick the box on the release.[confirmed]
JavaScript and Docker actions follow the same repo-and-tag shape — only runs.using and the build differ; the composite-action quickstart and its sibling pages have each full path.
Watch / read
YouTube blocked transcript pulls on 2026-06-07, so these aren't verified line-by-line — judged on title, length, and channel credibility; lean on the official written guides below if a video drifts.
- Publish your GitHub Action to Marketplace in only 5 minutes — bdougie (GitHub DevRel), 5:42. Why this one: shortest end-to-end — scaffold, tag, and the exact Marketplace publish checkbox this page describes, from a GitHub developer advocate.
- Composite Actions vs Reusable Workflows — CoderDave, 5:19. Why this one: clears up the one decision people get wrong — a composite action (what you share) vs a reusable workflow — in five minutes.
- Publish your Github Actions to Marketplace and use it in a Workflow — Sumanshu Nankana, 24:49. Why this one: the patient, full version — building
action.yml, releasing, and consuming it from another repo. Long — skim to the part you need.
Best written walkthrough: GitHub's own Creating a composite action quickstart — action.yml, inputs/outputs, the runs: using: "composite" block, and testing it — followed by Publishing in GitHub Marketplace for the listing rules and the release checkbox. The authoritative sources these steps were checked against. [confirmed]
Sources
- About custom actions — the three types (composite / JavaScript / Docker),
action.yml/action.yamlat the root, public repo to share, Docker = Linux runners only - Creating a composite action — minimal
action.yml,runs.using: "composite",shell: bashon each step,uses: ./to test locally,uses: owner/repo@v1to consume - Metadata syntax for GitHub Actions — required
name/description/runs, validusingvalues (composite,node20/node24,docker), inputs/outputs - Releasing and maintaining actions — semantic-version tags (
v1.1.3), keep the major (v1) tag current, reference by named tag / SHA for stability - Publishing actions in GitHub Marketplace — public repo, single root
action.yml, unique name, 2FA + Developer Agreement, Draft-a-release checkbox, immediate listing (the "no workflow files" rule is community-reported but not stated on this page as of 2026-06-08) - GitHub CLI —
gh release create— cutting a tagged release from the terminal - GitHub CLI quickstart — install + auth — connecting
ghso your agent can create the repo and release
Good to know
- Composite first, almost always. If your step is "run these commands", a composite action needs no build step and no container — it's the shortest path to a shareable action. Reach for JavaScript only when the logic is real code, and Docker only when you need a frozen toolchain (and can live with Linux-only runners).
[confirmed] - A tag is mutable; a SHA is not.
@v1and even@v1.2.3are tags you (or an attacker who compromised your account) could re-point. Security-conscious consumers pin to a full commit SHA for that reason — worth a line in your README for actions that touch secrets or write to repos.[confirmed] - The "no workflow files" rule is the most-reported Marketplace surprise — the very test workflow that proved your action works is widely said to block the listing, so delete
.github/workflows/before you publish to be safe. Heads-up: GitHub's current publishing docs don't list this rule and some people report listing with a workflow present, so treat it as a likely-but-unconfirmed snag, not a certainty. (Unlisted sharing byuses:line has no such rule either way.)[unclear](not in GitHub's current docs; community reports conflict — checked 2026-06-08) - A public action and its code are visible to everyone, and once people pin to
@v1they depend on it — deleting or breaking it breaks their pipelines. Don't put anything secret in the repo, and treat the major-version tag as a contract. See Who can see it? and Can you trust the company?.[confirmed]