`homeboy deploy`
Synopsis
homeboy deploy [<project_id>|<component_id>] [<component_ids...>] [-p|--project <id>] [-c|--component <id>]... [--all] [--outdated|--behind-upstream] [--head|--ref <git-ref-or-sha>] [--release-set <path>] [--check] [--dry-run] [--apply] [--json '<spec>']
# If no component IDs are provided, you must use --all, --outdated, --behind-upstream, or --check.
# Multi-project deployment
homeboy deploy --projects <project1>,<project2> <component_ids...>
# Fleet deployment
homeboy deploy <component_id> --fleet <fleet_id>
# Shared component deployment (auto-detect projects)
homeboy deploy <component_id> --sharedArguments and flags
project_id: project ID<component_ids...>(optional): component IDs to deploy (positional, trailing)
Options:
-c,--component: component ID to deploy (can be repeated, alternative to positional)-p,--project: explicit project ID; takes precedence over positional project/component detection--all: deploy all configured components--outdated: deploy only outdated components- Determined from the first version target for each component.
--behind-upstream: deploy only components whose local checkout is behind upstream. Conflicts with--outdated.--check: check component status without building or deploying- Shows all components for the project with version comparison status.
- Combines with
--outdatedor component IDs to filter results.
--dry-run: preview what would be deployed without executing (no build, no upload)--apply: confirm real deploys that use dangerous modes such as--head,--ref, or--force--force: deploy even with uncommitted changes--json: JSON input spec for bulk operations ({"component_ids": ["component-id", ...]})--projects: deploy to multiple projects (comma-separated). When using this flag, all positional arguments are treated as component IDs. Each project deployment builds independently.-f,--fleet: deploy to all projects in a fleet. Resolves fleet to project IDs, then runs multi-project deployment.-s,--shared: deploy to all projects using the specified component(s). Auto-detects which projects have the component configured and deploys to all of them.--keep-deps: keep build dependencies and skip post-deploy cleanup.--version <version>: assert the expected local component version before deploying; aborts on mismatch.--no-pull: skip the automatic pull before deploy.--head: deploy the current branchHEADinstead of the latest tag.--ref <git-ref-or-sha>: resolve a commit from each component’s declared Git repository and deploy that exact immutable tree. The configured checkout’s current branch andHEADdo not affect resolution.--tagged: force tag-based deploy and ignore reusable build artifacts.
Real deploys with --head, --ref, or --force require --apply. Preview and status commands (--dry-run or --check) do not require --apply.
--ref is an explicit source selector and conflicts with --head, --tagged, --version, --outdated, --behind-upstream, and --check. Without --ref, release and --head selection behave as before. File sources and deploy_strategy: "git" are rejected because those strategies cannot package and upload the selected Git tree.
Homeboy resolves <git-ref-or-sha>^{commit} in the repository containing the declared component local_path. If that checkout lacks the object, it fetches only the requested SHA or a single matching named ref through the checkout’s configured Git remote, using the existing Git transport and credential policy. It pins the full commit SHA and uses a detached temporary worktree for local build and packaging without changing the configured checkout’s branch, index, or worktree. Missing, non-commit, ambiguous, and unauthenticated refs fail before build or remote mutation. A dry run may add the resolved object to the local Git object database but does not build, deploy, or create a worktree.
Components can declare deploy_together in their component config. When any selected component belongs to a deploy-together group, Homeboy requires the full group in the same deploy plan and fails before build/upload if only part of the group was selected. Use explicit component IDs for the whole group or --all for the project.
Bulk JSON input uses component_ids (snake_case):
{ "component_ids": ["component-a", "component-b"] }Positional and flag component IDs can be mixed; both are merged into the deployment list.
If no component IDs are provided and none of --all, --outdated, --behind-upstream, or --check is set, Homeboy returns an error. If --outdated or --behind-upstream finds no matching components, Homeboy returns an error.
JSON output
Note: all command output is wrapped in the global JSON envelope described in the JSON output contract. The object below is
data.
{
"command": "deploy.run",
"project_id": "<project_id>",
"all": false,
"outdated": false,
"behind_upstream": false,
"check": false,
"dry_run": false,
"force": false,
"results": [
{
"id": "<component_id>",
"status": "deployed|failed|skipped|planned|checked",
"deploy_reason": "explicitly_selected|all_selected|version_mismatch|unknown_local_version|unknown_remote_version",
"component_status": "up_to_date|needs_update|behind_remote|behind_upstream|source_stale|unknown",
"local_version": "<v>|null",
"remote_version": "<v>|null",
"error": "<string>|null",
"artifact_path": "<path>|null",
"remote_path": "<path>|null",
"build_exit_code": "<int>|null",
"deploy_exit_code": "<int>|null",
"release_state": {
"commits_since_version": 5,
"code_commits": 4,
"docs_only_commits": 1,
"has_uncommitted_changes": false,
"baseline_ref": "v0.9.15"
},
"deployed_ref": "<tag-or-branch>|null",
"requested_ref": "<operator-provided-ref>",
"resolved_sha": "<full-commit-sha>",
"source": "<declared-git-repository-or-configured-remote>",
"resolution_mode": "local|remote_sha|remote_named_ref"
}
],
"summary": { "total": 1, "succeeded": 0, "failed": 0, "skipped": 0 }
}Note: all command output is wrapped in the global JSON envelope described in the JSON output contract. The object below is data.
- Determined from the first version target for each component.
Note: all command output is wrapped in the global JSON envelope described in the JSON output contract. The object below is data.
Component status values
Notes:
- Shows all components for the project with version comparison status.
- Combines with
--outdatedor component IDs to filter results.
Note: build_exit_code/deploy_exit_code are numbers when present (not strings).
Release state
When using --check, each component result includes a component_status field:
- Determined from the first version target for each component.
Deployment health is compared independently from configured-source freshness. A newer remote release is behind_remote, never needs_update; check output retains source checkout warnings such as configured_source_detached and configured_source_behind_upstream.
When using --check, each component result includes a release_state field that tracks unreleased changes:
baseline_ref, baseline_warning, code_commits, and docs_only_commits are omitted when empty.
Exit code
- Shows all components for the project with version comparison status.
- Combines with
--outdatedor component IDs to filter results.
Multi-Project Deployment
This helps identify components where component_status is up_to_date but work has been done since the current version baseline (commits_since_version > 0), indicating a release may be needed before deployment.
# Deploy sample-plugin to both extra-chill and sarai-chinwag projects
homeboy deploy --projects extra-chill,sarai-chinwag sample-plugin
# Deploy multiple components to multiple projects
homeboy deploy --projects extra-chill,sarai-chinwag sample-plugin extrachill-api
# Preview multi-project deployment
homeboy deploy --projects extra-chill,sarai-chinwag sample-plugin --dry-runExit code is 0 when summary.failed == 0, otherwise 1.
Multi-project JSON output
When a component belongs to multiple projects, use --projects to deploy to all of them in a single command:
{
"command": "deploy.run_multi",
"component_ids": ["sample-plugin"],
"dry_run": false,
"check": false,
"force": false,
"projects": [
{
"project_id": "extra-chill",
"status": "deployed|failed|planned|checked",
"error": "<string>|null",
"results": [...],
"summary": { "total": 1, "succeeded": 1, "skipped": 0, "failed": 0 }
},
{
"project_id": "sarai-chinwag",
"status": "deployed|failed|planned|checked",
"error": "<string>|null",
"results": [...],
"summary": { "total": 1, "succeeded": 1, "skipped": 0, "failed": 0 }
}
],
"summary": { "total_projects": 2, "succeeded": 2, "failed": 0, "skipped": 0, "planned": 0 }
}Each project deployment builds independently; build artifacts are scoped to the individual project run.
Fleet Deployment
When using --projects, the output structure differs:
# Deploy my-plugin to all projects in the production fleet
homeboy deploy my-plugin --fleet production
# Preview fleet deployment
homeboy deploy my-plugin --fleet production --dry-run
# Check status across fleet before deploying
homeboy fleet check productionExit code is 1 if any project deployment fails.
Fleet vs Shared: When to Use Which
Deploy to all projects in a named fleet:
deploy_reasonis omitted when not applicable.component_statusis only present when using--checkor--check --dry-run.artifact_pathis the component build artifact path as configured; it may be relative but must include a filename.- Deploy output does not include
build_command. Builds are resolved from the linked extension, and deploy records only build/deploy exit codes plus the artifact path used. deployed_refis omitted when no tag or branch ref was deployed.requested_ref,resolved_sha,source, andresolution_modeare persisted for--refdeploy evidence and omitted for other source modes.build_provenance.built_from_refandbuild_provenance.built_from_commitcarry the same identity.
See fleet for fleet management commands.
--fleet and --shared often produce the same result, especially in smaller setups where a fleet’s projects are exactly the set of projects that use a given component. The difference is in how they resolve targets:
Shared Component Deployment
Rule of thumb: Use --shared for "update this component everywhere." Use --fleet for "update this fleet specifically."
# Deploy my-plugin to every project that uses it
homeboy deploy my-plugin --shared
# See which projects would be affected
homeboy component shared my-plugin
# Preview shared deployment
homeboy deploy my-plugin --shared --dry-runIn practice, if your fleet membership mirrors your component usage, they’re interchangeable — but as your fleet grows (staging vs production, multi-site networks), the distinction becomes meaningful.
Preview Before Deploying
Deploy to all projects using a component, auto-detected:
homeboy deploy myproject --outdated --dry-run
homeboy deploy myproject --behind-upstream --dry-run
homeboy deploy myproject my-plugin --version 1.2.3 --tagged --dry-run
homeboy deploy myproject my-plugin --ref origin/reviewed-branch --dry-runThis is useful when you don’t have a named fleet but want to update a component everywhere it’s used.
Check Component Status
Use --dry-run to see what would be deployed without executing:
# Check all components for a project
homeboy deploy myproject --check
# Check only outdated components
homeboy deploy myproject --check --outdated
# Check specific components
homeboy deploy myproject --check component-a component-bAn exact-ref dry-run resolves the ref without checking out, creating a worktree, building, or uploading. When the configured checkout lacks the requested object, it may fetch that object through the declared remote. Each planned result reports the requested ref, resolved SHA, source, resolution mode, packaging plan, and remote destination.
# Show changes for all project components
homeboy release changes --project myproject
# Show changes with git diffs included
homeboy release changes --project myproject --git-diffsPost-Deploy Hooks
Use --check to view version status for all components without building or deploying:
To see detailed git changes (commits, diffs) before deploying, use the changes command:
| Variable | Description |
|---|---|
{{component_id}} | The component ID |
{{install_dir}} | Remote install directory |
{{base_path}} | Project base path on the remote server |
Extension-level hooks
After a successful deploy, Homeboy runs post:deploy hooks remotely via SSH on the deployment target. Hooks are resolved from extensions and components (see hooks).
{
"hooks": {
"post:deploy": [
"wp plugin is-installed {{component_id}} --path={{base_path}} --allow-root 2>/dev/null && wp plugin activate {{component_id}} --path={{base_path}} --allow-root 2>/dev/null || true",
"wp cache flush --path={{base_path}} --allow-root 2>/dev/null || true"
]
}
}Component-level hooks
Template variables available:
{
"hooks": {
"post:deploy": ["systemctl restart my-service"]
}
}Extensions like WordPress define post:deploy hooks in their manifest. These run for every component using that extension:
Related
up_to_date: local and remote versions matchneeds_update: local version ahead of remote (needs deployment)behind_remote: remote version ahead of local (local is behind)behind_upstream: local checkout is behind its upstream branchsource_stale: deployed version matches, but the configured checkout is detached or stale against its default branchunknown: cannot determine status (missing version information)
Release Sets
Components can add their own post:deploy hooks for custom automation: