Skip to content

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@v1 to 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 gh CLI, 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 gh CLI 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.

  1. Add a test workflow. Ask your agent: "Add a workflow at .github/workflows/test.yml that checks out the repo and runs this action with uses: ./ 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]
  2. Push and watch it run. Your agent runs gh workflow run or just pushes; check the result with gh run watch (or the Actions tab). Green means the step works. [confirmed]
  3. 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 (or action.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.yml you 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 name in action.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.yml must be at the repo root (not a sub-folder, for a top-level action), the repo must be public, and the uses: path must be owner/repo@ref exactly. 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. @v1 is a tag; @main is a branch. Cut the release (gh release create v1.0.0) and create the v1 tag. 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-pointed v1 with a breaking change. Steer recipients to @v1 for 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 declare shell: (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 via with:. 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:

  1. Make a public repo and add action.yml at 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
    
  2. Test it with a throwaway .github/workflows/test.yml that runs uses: ./ on push — then delete it before listing. [confirmed]

  3. Tag and release. git tag v1.0.0 && git tag v1 && git push --tags, then Draft a release on github.com for v1.0.0. [confirmed]
  4. 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.

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

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. @v1 and even @v1.2.3 are 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 by uses: 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 @v1 they 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]