harness-deployments
Skill10 starsChanged 31 days agoApache-2.0View on GitHub
Add a CI deployment layer on top of the release workflow — registry publish, container image, or app deploy. Requires /flow-init to have run first.
In this repository21 other files it gives its agents.
- .claude/CLAUDE.mdCLAUDE.md
- commitSkill
- design-apiSkill
- design-architectureSkill
- design-erdSkill
- design-sdsSkill
- design-srsSkill
- design-tableSkill
- doc-syncSkill
- flow-initSkill
- flowSkill
- flow-uninstallSkill
- harness-authoringSkill
- harness-initSkill
- harness-insightSkill
- integrationSkill
- performanceSkill
- playwright-scaffoldSkill
- prose-reviewSkill
- release-commitSkill
- wiki-initSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Run after /flow-init. Adds deployment (registry publish · image · app deploy) on top of the release (tag + notes).
- Very specific: line 34
If ${CLAUDE_PROJECT_DIR}/.claude/harness-tier/config/flow-config.yaml is absent → tell the user to "run /flow-init first" and stop.
- Distinctive: line 47
Ask the user (structured choice) for each item below.
- Very specific: line 65
Write/update the deploy: block in flow-config.yaml (team-shared · git-tracked) — config carries only non-derivable values: write the …
- Very specific: line 110
Write/update docs/operations/deploy-guide.md — see the content below.
- Distinctive: line 130
When recommending a paid service, state the cost/licence explicitly.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Harness-Deployments — Deployment Layer Setup
Run after /flow-init. Adds deployment (registry publish · image · app deploy) on top of the
release (tag + notes). CI performs the actual deployment; this skill only detects, asks, and
generates (it never deploys from the host).
Orchestrator structure — wiring is always automatic: release.yml calls deploy.yml (the
orchestrator, generated) via workflow_call in the same run — no cross-workflow trigger and no
PAT. deploy.yml resolves the real tag once and calls each target component deploy-<name>.yml
(on: workflow_call) with per-target permissions. The script (flow_init_setup.py
--render-deploy / /flow-init) fills this wiring idempotently into release.yml's managed block.
Trigger and wiring therefore belong to the script; §2 and §3 say what that leaves you.
Interaction. Wherever this skill asks the user something, use the host's blocking question tool already in your tool list, matched by capability rather than by a host-specific name; if it is listed but not loaded, load it first with the host's tool-discovery primitive; only when no such tool is listed, or a question call errors, offer numbered options in chat and end your turn to wait for the reply — never answer the question yourself or skip it.
Path conventions
- Reads (templates/reference):
${CLAUDE_PLUGIN_ROOT}/... - Host writes:
${CLAUDE_PROJECT_DIR}/.github/workflows/,.../.claude/harness-tier/config/flow-config.yaml,.../docs/ - Never write into the plugin directory.
Execution
0. Guard (hard stop)
- If
${CLAUDE_PROJECT_DIR}/.claude/harness-tier/config/flow-config.yamlis absent → tell the user to "run/flow-initfirst" and stop.
1. Detection
- Stack: flow-config's
versioning.release_tool/version_files/modules[].checkslanguage. - Artifacts: does a
Dockerfileexist? A package library (pyproject.toml/package.json/Cargo.toml/pom.xml/*.csproj)? build_tool(needed only fortarget: maven-central):build.gradle/build.gradle.kts→ gradle,pom.xml→ maven,build.sbt→ sbt. This value decides whether the renderer uses the maven or the gradle template. §2 confirms it and §3 writes it.- Existing deployment: publish/deploy steps already in
.github/workflows/*(Grep). - Secrets: where possible, check already-registered registry/signing secrets with
gh secret list(for reference in the report step).
2. Q&A (adaptive) — ask only what cannot be derived
Ask the user (structured choice) for each item below; for deployment targets, ask the user (multi-select).
- Present the detected candidates and let the user pick deployment targets ("Dockerfile found → GHCR? pyproject → PyPI?").
- Per-target
auth(oidc|token— not detectable from the repo. Default to the per-target recommendation, mostly oidc). - Deployment
order(omitted → all parallel — ask only when ordering between targets is needed). image/context/dockerfilefor monorepo image targets (do not ask for a single image — the renderer fills the derived defaultsghcr.io/${{ github.repository }}·.·<context>/Dockerfile).permissions/withfor custom targets (not derivable — the orchestrator uses them verbatim).- brownfield: when an existing deployment is found, choose adopt/augment/replace (never overwrite silently).
- Confirm the detected
build_tool(do not re-ask). Advise thatversion/buildmay be omitted without forcing it (omitted → the renderer fills the stack default). Thepublishcommand formaven-central+build_tool: gradle/sbtis the sole exception — task names vary per project, so there is no safe universal default and the user must state it (build_tool: mavendoes not apply — the template'smvn deployperforms the pom's central-publishing-maven-plugin configuration as-is). - The trigger is already settled — release.yml→deploy.yml is always a same-run reusable-workflow call that the script wires, so there is no trigger to offer a choice about.
3. Generation
- Write/update the
deploy:block inflow-config.yaml(team-shared · git-tracked) — config carries only non-derivable values: write the human-decidedenable/name/target/order/auth/custompermissions, omit what the renderer can fill (image/context/dockerfile/build/version), and write the skill-detectedbuild_tool. - Component generation — 3-tier fallback (in order of confidence):
a. Mapped target (registry/image —
pypi/npm/nuget/cratesio/ghcr/dockerhub, plusmaven-central+build_tool=maven|gradle) → callpython3 "${CLAUDE_PLUGIN_ROOT}/scripts/flow_init_setup.py" --render-deployto render the static template (call the plugin SOURCE path directly —flow_init_setup.pyis not inCOPY_FILESand so is never copied to the host; run it from${CLAUDE_PLUGIN_ROOT}, not the host copy under${CLAUDE_PROJECT_DIR}/.claude/harness-tier/scripts/). b. custom/app-deploy that has references (ssh·kubernetes·cloud-run·ecs) or sbt (target: maven-central+build_tool=sbt, no static template) → author.github/workflows/deploy-<name>.ymldirectly, using the matching recipe (references/app-deploy/*.md,references/registry-publish/jvm-sbt.md) as the blueprint. c. A new target not in references either → research the official action, required secrets, and OIDC support viaWebSearch/WebFetch, then author to the contract + a "needs verification" flag + the required-secret list in the report. Whichever tier applies, the authored file honours this contract:on: workflow_call(inputs.tagrequired) +workflow_dispatch(inputs.tagrequired) +ref: ${{ inputs.tag }}checkout +timeout-minuteson the running job + its ownpermissions:. Declare it in config astarget: customworkflow/permissions/withso the orchestrator wraps it uniformly as auses:job.
- The orchestrator (
deploy.yml) is generated dynamically from targets by--render-deploy(it is not a static template) — no separate skill work needed. A file already at that path that does not open with the generated header is the host's own:--render-deployreports it and leaves it untouched rather than overwriting it — guide the user to delete it or merge it by hand, then re-run. - The script owns release wiring:
--render-deploy(and a/flow-initre-run) fills release.yml's# __HARNESS_DEPLOY_BEGIN/END__managed block with the deploy-call job idempotently viaintegrate_release_deploy(union permissions auto-recomputed, re-synced on a flow-init re-run too). The skill does not edit release.yml — the exceptions are both a script[!]refusal, for different reasons needing different fixes:- The managed block is absent (legacy-ours or truly-foreign release.yml). In that case the skill:
- Path A (regenerate): guide regenerating release.yml from the latest template → the script wires it automatically (simple, but customisations need review).
- Path B (semantic patch — the only path where the skill edits release.yml): read the release job and
insert
outputs.tag+ the# __HARNESS_DEPLOY_BEGIN/END__markers + the deploy-call job after the point where the released signal is produced (a per-tool semantic judgement the script cannot make, so the skill makes it) — apply only after showing the diff and getting user confirmation (preserves customisations).
deploy.ymlis not the generated orchestrator (the refusal above). The managed block can be present and well-formed, but the call job it would add names ataginput a hand-writtendeploy.ymlmay not declare, so wiring stays withheld until that file is the generated one — guide the user to delete or mergedeploy.yml, then re-run; there is no diff to confirm, since release.yml does not change until then.- Whichever path is taken, in the meantime the components, and any
deploy.ymlalready generated, can be run manually viaworkflow_dispatch(tag input) — deployment is not blocked, only the automatic wiring is deferred.
- The managed block is absent (legacy-ours or truly-foreign release.yml). In that case the skill:
- Write/update
docs/operations/deploy-guide.md— see the content below.
4. Report
- Summarise the created/changed files, the secrets the repo admin must set (including the JVM signing-key
format caution in the deploy-guide section below), whether release.yml changed (wired automatically / or
there was a
[!]consultation because it, ordeploy.ymlitself, was legacy·foreign), and any conflicts found.
docs/operations/deploy-guide.md content
- The secrets to set (per target — see
references/registry-publish/*.md·references/container-image/*.md).maven-centraldiffers in format bybuild_tool: maven·gradle use the ASCII-armoredMAVEN_GPG_PRIVATE_KEY(shared), sbt uses base64PGP_SECRET(separate) — confusing them makes signing fail silently. - That wiring is always automatic (release.yml → deploy.yml, same-run
workflow_call, no PAT needed). - Manual redeploy: run
.github/workflows/deploy.ymldirectly viaworkflow_dispatch(tag required, target optional). - Rollback pointers (see the rollback section of each target's
references/*.md).
Reuse before build
- Each stack prefers the official action (pypa/gh-action-pypi-publish, docker/build-push-action, com.vanniktech.maven.publish, etc. — see references).
- When recommending a paid service, state the cost/licence explicitly.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

