# GitHub Action

> A GitHub Action that publishes on every push, with PR previews, custom domains and deployments that clean themselves up.

Source: https://docs.shipstatic.com/action

A GitHub Action that puts your static site live on every push. Supports PR previews, custom domains, and self-expiring deployments — and you can try it before creating an account. [Marketplace](https://github.com/marketplace/actions/shipstatic) / [GitHub](https://github.com/shipstatic/action) / [example recipes](https://github.com/shipstatic/action-example)

```yaml
- uses: shipstatic/action@v2
  with:
    token: ${{ secrets.SHIP_TOKEN }}   # your API key, stored as a secret
    path: ./dist
```

## Start without an account

In your repository, create a file called `.github/workflows/deploy.yml`:

```yaml
name: Deploy
on: push

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: npm ci && npm run build
      - uses: shipstatic/action@v2
        with:
          path: ./dist
```

Commit, push, and open the run in the **Actions** tab: the run's summary shows your site's address, live the moment the job goes green. Deploys made without a `token` are public and expire — the summary shows the exact date, and a **claim** link that keeps the site permanently on a free account. (Deploying a plain folder with no build step? Delete the `run:` line and use `path: .`.)

## Deploy to your account

1. Get your [API Key](https://docs.shipstatic.com/api-key) at [my.shipstatic.com/api-key](https://my.shipstatic.com/api-key).
2. In your repository: **Settings → Secrets and variables → Actions → New repository secret**. Name it `SHIP_TOKEN`, paste the key.
3. Add `token:` to the deploy step:

```yaml
      - uses: shipstatic/action@v2
        with:
          token: ${{ secrets.SHIP_TOKEN }}
          path: ./dist
```

Deployments now land in your account and never expire.

## Your domain, previews included

The production pattern in one workflow — pushes to `main` deploy to your domain, pull requests get previews with a comment:

```yaml
name: Deploy
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: npm ci && npm run build
      - uses: shipstatic/action@v2
        with:
          token: ${{ secrets.SHIP_TOKEN }}
          path: ./dist
          domain: ${{ github.event_name == 'push' && 'www.example.com' || '' }}
```

When `domain` is set, the deploy and the link are one operation: the site is live at that address when the action finishes, and the `url` output is your domain's URL. The expression above links the domain on push and skips it for pull requests, so PRs stay previews. A `domain` with no `token` is refused before anything is uploaded — a domain belongs to an account.

## PR previews

On a pull request the action posts the deployment URL as a comment, updated in place on every push rather than adding a new one. This needs no configuration — `github-token` defaults to the workflow's own token — only the `pull-requests: write` permission. Pass `github-token: ''` to turn comments off.

For previews that clean themselves up, add a `ttl`.

## Self-expiring deployments

`ttl` gives a deployment a lifetime in seconds, after which the platform reclaims it — previews that need no cleanup workflow, no `pull_request: closed` handler:

```yaml
ttl: 604800   # one week
```

It needs a `token` (a deployment made without one already expires on its own schedule) and cannot be combined with `domain`: a domain should not point at something about to be reclaimed. The `expires` output carries the deadline.

## GitHub Deployments

Declare an [`environment:`](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) on your job and GitHub records the deployment itself, with the environment you named. The action does not write one.

## Authentication

One slot. `token` accepts either credential the platform issues: an [API Key](https://docs.shipstatic.com/api-key) (`ship-…`) or a [deploy token](https://docs.shipstatic.com/tokens) (`deploy-…`). Store it as a repository secret and pass it as `token`.

Linking a `domain` needs an **API key** specifically: a deploy token is scoped to creating deployments and nothing else.

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `token` | No | - | Your [API Key](https://docs.shipstatic.com/api-key) (`ship-…`), or a [deploy token](https://docs.shipstatic.com/tokens) (`deploy-…`) — one slot takes either. Omit to deploy anonymously |
| `api-url` | No | *the CLI's* | API endpoint. Leave unset for production |
| `path` | No | `.` | Directory to deploy |
| `domain` | No | - | Domain to serve the deployment at — deployed and linked in one step (requires an API key in `token`) |
| `ttl` | No | - | Seconds until the deployment expires and is cleaned up automatically (requires `token`; cannot be combined with `domain`) |
| `password` | No | - | Password-protect the deployment (6–128 characters). Visitors are prompted to unlock before viewing |
| `labels` | No | - | Comma-separated labels, added to the commit short SHA the action applies automatically |
| `idempotency-key` | No | *derived* | Override the derived key. Defaults to this workflow run + job, so re-running a job replays the original deployment instead of deploying twice |
| `github-token` | No | *the workflow's own token* | GitHub token for the PR comment. Pass an empty string to post no comment |

## Outputs

| Output | Description |
|--------|-------------|
| `deployment` | Deployment host (e.g. `happy-cat-abc1234.shipstatic.com`) |
| `url` | URL of the deployed site — your domain's URL when `domain` is set, otherwise the deployment's own (e.g. `https://happy-cat-abc1234.shipstatic.com`) |
| `claim` | Claim URL for public deploys — visit while signed in to keep the site permanently. Empty for authenticated deploys |
| `expires` | Expiry as a unix timestamp in seconds. Set for public deploys and for any deployment given a `ttl` |

Every deploy also writes a summary to the workflow run page — the deployment and its URL, plus the claim link and expiry where the deployment has them. Nothing to configure, and nothing to add.

Use outputs in subsequent steps:

```yaml
- name: Notify
  run: echo "Deployed to ${{ steps.deploy.outputs.url }}"
```

## Permissions

The PR comment is the only thing the action needs a permission for:

```yaml
permissions:
  contents: read
  pull-requests: write
```

## Examples

Five annotated, copy-pasteable workflows in the [action-example](https://github.com/shipstatic/action-example) repo — no account, API key, custom domain, PR previews, and password protection. Each file explains every line to a first-time reader.

## Architecture

Composite action wrapping the [CLI](https://docs.shipstatic.com/cli). Installs `@shipstatic/ship` at runtime, runs `ship` commands, and parses JSON output. No custom code beyond shell scripts.
