Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Test

on:
push:
branches: [main, v2]
pull_request:
workflow_dispatch:

jobs:
Comment on lines +1 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set explicit least-privilege permissions for the workflow.

There is no permissions: block, so the workflow inherits repository defaults. Declare minimal permissions explicitly.

🛡️ Suggested change
 on:
   push:
     branches: [main, v2]
   pull_request:
   workflow_dispatch:

+permissions:
+  contents: read
+
 jobs:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: Test
on:
push:
branches: [main, v2]
pull_request:
workflow_dispatch:
jobs:
name: Test
on:
push:
branches: [main, v2]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 1-73: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 1 - 9, Add an explicit top-level
permissions block to the "Test" workflow to enforce least-privilege rather than
inheriting repo defaults: declare a minimal permissions map (for example,
permissions: contents: read and any other specific scopes the jobs actually
need) at the top-level of the workflow file so jobs cannot use broader rights;
update only the workflow header (the "name: Test" workflow) and ensure each job
uses only the required permission scopes.

# Cross-platform smoke test: install the pinned CLI on every supported runner
# and confirm it executes. Doesn't touch any DeployHQ resources.
install:
name: Install CLI (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow with line numbers (small file)
echo "=== .github/workflows/test.yml ==="
wc -l .github/workflows/test.yml
nl -ba .github/workflows/test.yml | sed -n '1,120p'

# Find action.yml (either root or within .github/actions)
echo
echo "=== Find action.yml files ==="
fd -t f -a action.yml . | head -n 50

# If root action.yml exists, print inputs section
if [ -f action.yml ]; then
  echo
  echo "=== action.yml (root) ==="
  nl -ba action.yml | sed -n '1,200p'
fi

# If there's an action.yml under .github, print any inputs.cli-version defaults
echo
echo "=== Search for cli-version default ==="
rg -n "cli-version" -S .github action.yml || true

Repository: deployhq/deployhq-action

Length of output: 194


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow with line numbers (small file)
echo "=== .github/workflows/test.yml ==="
wc -l .github/workflows/test.yml
nl -ba .github/workflows/test.yml | sed -n '1,160p'

# Find action.yml files
echo
echo "=== Find action.yml files ==="
fd -t f action.yml . | head -n 50

# If root action.yml exists, print inputs section
if [ -f action.yml ]; then
  echo
  echo "=== action.yml (root) ==="
  nl -ba action.yml | sed -n '1,220p'
fi

# Search for cli-version default and any secrets gating
echo
echo "=== Search for cli-version / TEST_DEPLOYHQ_API_KEY / persist-credentials ==="
rg -n "cli-version|TEST_DEPLOYHQ_API_KEY|persist-credentials|permissions:" . || true

Repository: deployhq/deployhq-action

Length of output: 194


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== .github/workflows/test.yml (line-numbered) ==="
wc -l .github/workflows/test.yml
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/test.yml | sed -n '1,200p'

echo
echo "=== Find action.yml files ==="
fd -t f action.yml . | head -n 50

echo
echo "=== Print any action.yml files (first ~220 lines) and search inputs/defaults ==="
for f in $(fd -t f action.yml .); do
  echo
  echo "---- $f ----"
  sed -n '1,220p' "$f"
done

echo
echo "=== Search for persist-credentials / permissions / TEST_DEPLOYHQ_API_KEY / cli-version ==="
rg -n "persist-credentials|permissions:|TEST_DEPLOYHQ_API_KEY|cli-version|actions/checkout@v4|actions/checkout@" -S . || true

Repository: deployhq/deployhq-action

Length of output: 7534


🌐 Web query:

GitHub actions checkout@v4 persist-credentials default value

💡 Result:

The default value of persist-credentials for actions/checkout@v4 is effectively true (credentials are persisted in the local git config unless you set persist-credentials: false). [1][2] What this means: - actions/checkout “persists the auth token in the local git config” so subsequent steps can run authenticated git commands. [1][2] - The docs say you can opt out by setting persist-credentials: false. [1][2] Example (opt out): uses: actions/checkout@v4 with: persist-credentials: false

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Locate scripts/install-cli.sh ==="
fd -t f install-cli.sh . || true

echo
echo "=== Print scripts/install-cli.sh (first 200 lines) ==="
sed -n '1,220p' scripts/install-cli.sh

echo
echo "=== Search for DHQ_VERSION usage ==="
rg -n "DHQ_VERSION|inputs\\.cli-version|cli-version" scripts -S || true

Repository: deployhq/deployhq-action

Length of output: 3270


Pin actions/checkout to a commit SHA and disable credential persistence.

actions/checkout@v4 is used on lines 20 and 39 without SHA pinning, and persist-credentials is not set to false, so credentials are persisted in the local git config during CI.

🔒 Suggested hardening
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@<pinned-commit-sha> # v4.x
+        with:
+          persist-credentials: false
...
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@<pinned-commit-sha> # v4.x
+        with:
+          persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 20-20: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml at line 20, Replace both occurrences of "uses:
actions/checkout@v4" in the workflow with a SHA-pinned checkout reference and
disable credential persistence by adding the input "persist-credentials: false";
specifically update the steps that currently say uses: actions/checkout@v4 so
they use the actions/checkout commit SHA (pin to the latest stable commit SHA)
and include persist-credentials: false under that step to prevent CI from
writing credentials into the local git config.


- name: Run install-cli.sh
shell: bash
env:
DHQ_VERSION: v0.17.1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate workflow =="
ls -la .github/workflows || true
sed -n '1,120p' .github/workflows/test.yml

echo
echo "== Find action.yml =="
ls -la .github || true
ls -la . || true

# Try common locations for action.yml
for f in "action.yml" ".github/action.yml" ".github/actions/action.yml" ".github/actions/test/action.yml"; do
  if [ -f "$f" ]; then
    echo "== Contents: $f =="
    sed -n '1,200p' "$f"
  fi
done

echo
echo "== Search for DHQ_VERSION and cli-version default =="
rg -n "DHQ_VERSION|cli-version|inputs:|action.yml" .github -S || true
rg -n "DHQ_VERSION|cli-version|inputs:" . -S --hidden || true

Repository: deployhq/deployhq-action

Length of output: 8861


Avoid duplicating the dhq CLI version pin in this workflow

  • .github/workflows/test.yml line 25 hardcodes DHQ_VERSION: v0.17.1, duplicating the pinned value already defined in action.yml as inputs.cli-version.default (and used by the composite action via DHQ_VERSION: ${{ inputs.cli-version }}), which can drift over time.
          DHQ_VERSION: v0.17.1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml at line 25, The workflow hardcodes DHQ_VERSION:
v0.17.1 which duplicates the pinned value in action.yml
(inputs.cli-version.default) and can drift; remove the hardcoded DHQ_VERSION
assignment and instead reference the composite action input (use DHQ_VERSION:
${{ inputs.cli-version }} or omit the env assignment so the composite action's
DHQ_VERSION input is used) to ensure the version is sourced from
inputs.cli-version rather than duplicated.

run: ./scripts/install-cli.sh

- name: dhq --version
shell: bash
run: dhq --version

# End-to-end dry-run against a real DeployHQ account. Gated on secrets so
# external PRs don't fail; runs on push to v2/main and on workflow_dispatch.
dry-run:
name: dhq deploy --dry-run
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
steps:
- uses: actions/checkout@v4

- name: Skip if secrets are missing
id: gate
env:
API_KEY: ${{ secrets.TEST_DEPLOYHQ_API_KEY }}
run: |
if [[ -z "$API_KEY" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "::notice::TEST_DEPLOYHQ_API_KEY not set; skipping dry-run job."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
Comment on lines +41 to +51
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Gate on all required dry-run secrets, not only API key.

Line 46 checks only TEST_DEPLOYHQ_API_KEY, but the action step also requires account, email, project, and server. Partial secret configuration will fail instead of cleanly skipping.

✅ Suggested gate logic
       - name: Skip if secrets are missing
         id: gate
         env:
           API_KEY: ${{ secrets.TEST_DEPLOYHQ_API_KEY }}
+          ACCOUNT: ${{ secrets.TEST_DEPLOYHQ_ACCOUNT }}
+          EMAIL: ${{ secrets.TEST_DEPLOYHQ_EMAIL }}
+          PROJECT: ${{ secrets.TEST_DEPLOYHQ_PROJECT }}
+          SERVER: ${{ secrets.TEST_DEPLOYHQ_SERVER }}
         run: |
-          if [[ -z "$API_KEY" ]]; then
+          missing=()
+          [[ -z "${API_KEY:-}" ]] && missing+=("TEST_DEPLOYHQ_API_KEY")
+          [[ -z "${ACCOUNT:-}" ]] && missing+=("TEST_DEPLOYHQ_ACCOUNT")
+          [[ -z "${EMAIL:-}" ]] && missing+=("TEST_DEPLOYHQ_EMAIL")
+          [[ -z "${PROJECT:-}" ]] && missing+=("TEST_DEPLOYHQ_PROJECT")
+          [[ -z "${SERVER:-}" ]] && missing+=("TEST_DEPLOYHQ_SERVER")
+
+          if ((${`#missing`[@]})); then
             echo "skip=true" >> "$GITHUB_OUTPUT"
-            echo "::notice::TEST_DEPLOYHQ_API_KEY not set; skipping dry-run job."
+            echo "::notice::Missing required test secrets: ${missing[*]}. Skipping dry-run job."
           else
             echo "skip=false" >> "$GITHUB_OUTPUT"
           fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Skip if secrets are missing
id: gate
env:
API_KEY: ${{ secrets.TEST_DEPLOYHQ_API_KEY }}
run: |
if [[ -z "$API_KEY" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "::notice::TEST_DEPLOYHQ_API_KEY not set; skipping dry-run job."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip if secrets are missing
id: gate
env:
API_KEY: ${{ secrets.TEST_DEPLOYHQ_API_KEY }}
ACCOUNT: ${{ secrets.TEST_DEPLOYHQ_ACCOUNT }}
EMAIL: ${{ secrets.TEST_DEPLOYHQ_EMAIL }}
PROJECT: ${{ secrets.TEST_DEPLOYHQ_PROJECT }}
SERVER: ${{ secrets.TEST_DEPLOYHQ_SERVER }}
run: |
missing=()
[[ -z "${API_KEY:-}" ]] && missing+=("TEST_DEPLOYHQ_API_KEY")
[[ -z "${ACCOUNT:-}" ]] && missing+=("TEST_DEPLOYHQ_ACCOUNT")
[[ -z "${EMAIL:-}" ]] && missing+=("TEST_DEPLOYHQ_EMAIL")
[[ -z "${PROJECT:-}" ]] && missing+=("TEST_DEPLOYHQ_PROJECT")
[[ -z "${SERVER:-}" ]] && missing+=("TEST_DEPLOYHQ_SERVER")
if ((${`#missing`[@]})); then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "::notice::Missing required test secrets: ${missing[*]}. Skipping dry-run job."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 41 - 51, Update the "Skip if secrets
are missing" step to gate on all required dry-run secrets rather than only
TEST_DEPLOYHQ_API_KEY: check TEST_DEPLOYHQ_API_KEY, TEST_DEPLOYHQ_ACCOUNT,
TEST_DEPLOYHQ_EMAIL, TEST_DEPLOYHQ_PROJECT and TEST_DEPLOYHQ_SERVER and set
skip=true if any are empty; otherwise set skip=false. Modify the shell logic in
that step (the run block) to test each environment variable (e.g., using a loop
or combined conditional) and emit the same GITHUB_OUTPUT and notice when
skipping so the job cleanly skips on partial secret configuration.


- name: Run the action (dry-run)
if: steps.gate.outputs.skip == 'false'
id: deploy
uses: ./
with:
api-key: ${{ secrets.TEST_DEPLOYHQ_API_KEY }}
account: ${{ secrets.TEST_DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.TEST_DEPLOYHQ_EMAIL }}
project: ${{ secrets.TEST_DEPLOYHQ_PROJECT }}
server: ${{ secrets.TEST_DEPLOYHQ_SERVER }}
dry-run: "true"
wait: "false"

- name: Assert outputs are populated
if: steps.gate.outputs.skip == 'false'
run: |
test -n "${{ steps.deploy.outputs.deployment_id }}" \
|| { echo "deployment_id was empty"; exit 1; }
echo "deployment_id=${{ steps.deploy.outputs.deployment_id }}"
echo "status=${{ steps.deploy.outputs.status }}"
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Changelog

## v2.0.0 — Switch to the official DeployHQ CLI

This is a breaking rewrite. The action now wraps the [`dhq` CLI](https://github.com/deployhq/deployhq-cli) instead of POSTing to a webhook URL. To stay on the old behaviour, pin `@v1`.

### Breaking changes

- **Auth**: webhook URL replaced by API key. New required inputs: `api-key`, `account`, `email`.
- **Input style**: inputs are now declared properly (`with:`) instead of passed via `env:`. The legacy `DEPLOYHQ_*` env vars are no longer recognised.
- **Removed inputs**: `DEPLOYHQ_WEBHOOK_URL`, `REPO_CLONE_URL`. The CLI resolves the repository from the project configuration.
- **Defaults changed**: `revision` defaults to `${{ github.sha }}` (was `"latest"`). `branch` no longer defaults to `main` — the CLI auto-resolves it from server config.
- **Runtime**: Docker (Alpine) action replaced by a composite action. Runs on Linux, macOS, and Windows runners, including self-hosted.

### Added

- New inputs: `project`, `server`, `wait`, `timeout`, `dry-run`, `full`, `start-revision`, `extra-args`, `cli-version`.
- New outputs: `deployment_id`, `deployment_url`, `status`, `server`, `project`.
- Built-in wait-for-completion with timeout (polls `dhq deployments show` until terminal).
- Job step summary table with deployment ID, status, project, server, and link.
- Pinned CLI version with SHA-256 checksum verification on download.

### Migration

See the **Migration from v1** section in `README.md`.

## v1.x — Legacy Docker webhook action

Final webhook-based release. Maintained on the `v1` tag only.
56 changes: 56 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

A GitHub composite action that triggers a deployment on DeployHQ by invoking the official `dhq` CLI (<https://github.com/deployhq/deployhq-cli>). v1 (Docker + `curl` to a webhook URL) lives on the `v1` git tag and is no longer maintained on `main`.

## Architecture

`runs.using: composite` in `action.yml`. Two steps:

1. **Install dhq CLI** — `scripts/install-cli.sh` detects `RUNNER_OS`/`RUNNER_ARCH`, downloads the pinned `dhq` archive from `github.com/deployhq/deployhq-cli/releases`, verifies its SHA-256 against the release's `checksums.txt`, extracts to `$RUNNER_TOOL_CACHE/dhq/<version>/<os>_<arch>`, and appends that dir to `$GITHUB_PATH`. Cross-platform: Linux, macOS, Windows (Git Bash); amd64 + arm64.
2. **Deploy** — `scripts/deploy.sh` builds the `dhq deploy --json --non-interactive` argv from `INPUT_*` env vars, runs the CLI, parses the JSON envelope with `jq`, and emits action outputs + a `$GITHUB_STEP_SUMMARY` table.

### The `--wait` trick

`dhq deploy --json` returns immediately after queueing — its built-in `--wait` is a no-op in JSON mode (see `internal/commands/deploy.go` in the CLI). So `scripts/deploy.sh` implements waiting itself by polling `dhq deployments show <id> -p <project> --json` every 5s until the status is terminal (`completed`/`failed`/`cancelled`), respecting `INPUT_TIMEOUT`. If this changes upstream, the wait loop can be removed.

### Auth

Three required env vars passed straight through to the CLI: `DEPLOYHQ_API_KEY`, `DEPLOYHQ_ACCOUNT`, `DEPLOYHQ_EMAIL`. No `dhq auth login` call — the CLI reads these directly.

### Outputs

`scripts/deploy.sh` writes to `$GITHUB_OUTPUT`: `deployment_id`, `deployment_url`, `status`, `server`, `project`. The URL is constructed locally as `https://${DEPLOYHQ_ACCOUNT}.deployhq.com/projects/<permalink>/deployments/<id>` — the API doesn't return one. If the URL pattern changes server-side, fix it here.

### CLI version pin

The pinned default lives in **one place only**: `inputs.cli-version.default` in `action.yml`. Bump it there when validating against a new CLI release. Users can override per-workflow via the `cli-version` input.

## Things to know before editing

- **Two shells, one language.** Scripts use bash (`#!/usr/bin/env bash`, `set -euo pipefail`). On Windows runners GitHub's `shell: bash` picks Git Bash; `jq`, `tar`, `unzip`, `curl`, and `shasum`/`sha256sum` are all available there. Don't assume GNU-only flags.
- **`jq` is a hard dependency.** Preinstalled on all GitHub-hosted runners; self-hosted users must install it. The script fails fast with a clear message.
- **Exit codes are meaningful.** `0` success/queued, `1` deploy failed, `2` cancelled, `124` wait timeout, anything else = CLI error propagated.
- **`extra-args` is word-split unquoted by design** (escape hatch for new CLI flags). Don't quote it.
- **No `Dockerfile`/`entrypoint.sh`** — those live on `@v1`. Don't reintroduce them.
- Consumers can pin `@v2`, `@v2.x.y`, `@v1` (legacy), or `@main` (rolling).

## Local testing

```sh
DHQ_VERSION=v0.17.1 \
RUNNER_OS=$(uname -s) RUNNER_ARCH=$(uname -m) \
GITHUB_ACTION_PATH="$PWD" GITHUB_PATH=/tmp/gh-path \
./scripts/install-cli.sh

DEPLOYHQ_API_KEY=... DEPLOYHQ_ACCOUNT=... DEPLOYHQ_EMAIL=... \
INPUT_PROJECT=... INPUT_SERVER=... INPUT_REVISION=... \
INPUT_WAIT=false INPUT_DRY_RUN=true \
GITHUB_OUTPUT=/tmp/gh-output GITHUB_STEP_SUMMARY=/tmp/gh-summary \
./scripts/deploy.sh
```

(Set `GITHUB_OUTPUT`/`GITHUB_STEP_SUMMARY` to writable file paths when running outside Actions; they're appended-to, not created.)
16 changes: 0 additions & 16 deletions Dockerfile

This file was deleted.

168 changes: 137 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,152 @@
# GitHub Action to Trigger a Deployment on DeployHQ with a Webhook URL 🚀
# DeployHQ — GitHub Action

This action calls the DeployHQ's [Webhook URL](https://www.deployhq.com/support/deployments/automatic-deployments/custom) created for your DeployHQ account to trigger a deployment on DeployHQ.
Trigger a deployment on [DeployHQ](https://www.deployhq.com/) from a GitHub workflow. Wraps the official [`dhq` CLI](https://github.com/deployhq/deployhq-cli) so customers, agents, and CI all share one tool.

## Usage
> **Looking for the legacy webhook action?** That's v1. Pin `deployhq/deployhq-action@v1` to keep the old behaviour. See [Migration from v1](#migration-from-v1) below.

All sensitive variables should be [set as encrypted secrets](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) in the action's configuration.

### Configuration Variables

| Key | Value | Suggested Type | Required |
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------- | ------------- |
| `DEPLOYHQ_WEBHOOK_URL` | **Required.** Your DeployHQ webhook URL. Can be found in your DeployHQ Dashboard, under "Automatic Deployments" | `secret` | **Yes** |
| `REPO_REVISION` | The revision you wish to deploy. Can also be set to "latest" if you wish to deploy the latest revision in your set branch. If not set, the default value is "latest". | `secret` | **No** |
| `REPO_BRANCH` | The branch your revision is on. If not set, the default value is set to "main". | `secret` | **No** |
| `DEPLOYHQ_EMAIL` | **Required.** Your DeployHQ user. For example, matias@barilla.com. | `secret` | **Yes** |
| `REPO_CLONE_URL` | The path to your repository (as entered in the Deploy UI). If not set, it will be generated with [GitHub Action's default environment variables](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables). Nonetheless, we highly recommend setting this variable to avoid unexpected results. | `secret` | **No** |

### `workflow.yml` Example

Place in a `.yml` file such as this one in your `.github/workflows` folder. [Refer to the documentation on workflow YAML syntax here.](https://help.github.com/en/articles/workflow-syntax-for-github-actions)
## Quick start

```yaml
name: Deploy my website in DeployHQ w/ my webhook URL
on: push
name: Deploy
on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger DeployHQ deployment
uses: deployhq/deployhq-action@v2
with:
api-key: ${{ secrets.DEPLOYHQ_API_KEY }}
account: ${{ secrets.DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.DEPLOYHQ_EMAIL }}
project: my-project
server: production
```

The action installs the pinned `dhq` CLI on the runner, calls `dhq deploy`, waits for the deployment to reach a terminal status, and fails the job if it didn't succeed.

## Inputs

| Name | Required | Default | Description |
|---|---|---|---|
| `api-key` | **yes** | — | DeployHQ API key. Generate one in **Account Settings → API access**. |
| `account` | **yes** | — | Your DeployHQ account subdomain (e.g. `acme` for `acme.deployhq.com`). |
| `email` | **yes** | — | DeployHQ user email associated with the API key. |
| `project` | no | `""` | Project identifier or permalink. Falls back to `DEPLOYHQ_PROJECT` if unset. |
| `server` | no | `""` | Server identifier or name. Fuzzy-matched. Auto-selected if the project has only one server. |
| `revision` | no | `${{ github.sha }}` | Commit SHA to deploy. |
| `branch` | no | `""` | Branch the revision lives on. Auto-resolved from server config if omitted. |
| `wait` | no | `"true"` | Block until the deployment reaches a terminal status. Job exit code reflects the result. |
| `timeout` | no | `"0"` | Max seconds to wait when `wait=true`. `0` waits indefinitely. |
| `dry-run` | no | `"false"` | Preview the deploy without executing it. |
| `full` | no | `"false"` | Deploy the entire branch from the first commit (`--full`). |
| `start-revision` | no | `""` | Start an incremental deploy from this commit. |
| `extra-args` | no | `""` | Additional raw flags appended to `dhq deploy`. Escape hatch for newer CLI flags. |
| `cli-version` | no | pinned | Pin a specific `dhq` CLI release (e.g. `v0.17.1`). Defaults to the version this action was tested against. |

## Outputs

| Name | Description |
|---|---|
| `deployment_id` | DeployHQ deployment identifier (e.g. `dep-abc123`). |
| `deployment_url` | Web URL of the deployment in DeployHQ. |
| `status` | Final status (`completed`/`failed`/`cancelled`/`timeout`) when `wait=true`, else the queued status. |
| `server` | Resolved server identifier. |
| `project` | Resolved project permalink. |

```yaml
- id: deploy
uses: deployhq/deployhq-action@v2
with:
api-key: ${{ secrets.DEPLOYHQ_API_KEY }}
account: ${{ secrets.DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.DEPLOYHQ_EMAIL }}
project: my-project
server: production

- name: Open deployment
if: success()
run: echo "Deployed → ${{ steps.deploy.outputs.deployment_url }}"
```

## Common patterns

### Don't block the workflow on the deploy

```yaml
- uses: deployhq/deployhq-action@v2
with:
api-key: ${{ secrets.DEPLOYHQ_API_KEY }}
account: ${{ secrets.DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.DEPLOYHQ_EMAIL }}
project: my-project
server: production
wait: "false"
```

### Dry-run on pull requests

```yaml
- uses: deployhq/deployhq-action@v2
with:
api-key: ${{ secrets.DEPLOYHQ_API_KEY }}
account: ${{ secrets.DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.DEPLOYHQ_EMAIL }}
project: my-project
server: staging
dry-run: "true"
```

### Pass through a flag the action doesn't expose yet

```yaml
- uses: deployhq/deployhq-action@v2
with:
api-key: ${{ secrets.DEPLOYHQ_API_KEY }}
account: ${{ secrets.DEPLOYHQ_ACCOUNT }}
email: ${{ secrets.DEPLOYHQ_EMAIL }}
project: my-project
server: production
extra-args: "--copy-config --run-build"
```

# Put steps here to build your site, deploy it to a service, etc.
- name: Trigger deployment in DeployHQ w/ webhook URL
uses: deployhq/deployhq-action@main
env:
# All these values should be set as encrypted secrets in your repository settings
DEPLOYHQ_WEBHOOK_URL: ${{ secrets.DEPLOYHQ_WEBHOOK_URL }}
REPO_REVISION: ${{ secrets.REPO_REVISION }}
REPO_BRANCH: ${{ secrets.REPO_BRANCH }}
DEPLOYHQ_EMAIL: ${{ secrets.DEPLOYHQ_EMAIL }}
REPO_CLONE_URL: ${{ secrets.REPO_CLONE_URL }}
## Requirements

- A DeployHQ API key (**Account Settings → API access**).
- Runner with `bash`, `curl`, and `jq` available. GitHub-hosted runners (Ubuntu, macOS, Windows) ship with all three. Self-hosted runners must install `jq`.

## Migration from v1

v1 was a Docker action that POSTed to a webhook URL. v2 is a composite action that calls the `dhq` CLI directly.

| v1 input (env var) | v2 input | Notes |
|---|---|---|
| `DEPLOYHQ_WEBHOOK_URL` | _(removed)_ | Replaced by API key auth. |
| `DEPLOYHQ_EMAIL` | `email` | Now declared as a proper action input. |
| `REPO_REVISION` | `revision` | Defaults to `github.sha` instead of `"latest"`. |
| `REPO_BRANCH` | `branch` | Default `main` removed — CLI auto-resolves from server config. |
| `REPO_CLONE_URL` | _(removed)_ | CLI looks up the repo from the project config. |
| _(n/a)_ | `api-key`, `account` | **New required inputs.** |
| _(n/a)_ | `server`, `project` | Target a specific server/project. |
| _(n/a)_ | `wait`, `timeout`, `dry-run`, `full`, `start-revision`, `extra-args`, `cli-version` | New behaviour controls. |

**To stay on v1**, pin to it:

```yaml
uses: deployhq/deployhq-action@v1
```

**To migrate to v2:**

1. Generate an API key in DeployHQ (**Account Settings → API access**).
2. Add `DEPLOYHQ_API_KEY` and `DEPLOYHQ_ACCOUNT` as repository secrets. (`DEPLOYHQ_EMAIL` you already have.)
3. Switch your workflow to `with:` syntax (see [Quick start](#quick-start)).
4. Set `project:` and `server:` explicitly — the webhook implied these; the API requires them.
5. Remove `DEPLOYHQ_WEBHOOK_URL` from your secrets when no other workflow uses it.

## License

This project is distributed under the [MIT license](LICENSE.md).
MIT. See [LICENSE](LICENSE).
Loading
Loading