# GitHub Actions

aux4 ships an official GitHub Action, [`aux4/action@v1`](https://github.com/aux4/action), that lints, tests, and publishes aux4 packages straight from your CI pipeline. A single action handles all three stages of the [package](/packaging) lifecycle — you pick the stage with the `command` input.

```yaml
- uses: aux4/action@v1
  with:
    command: lint      # or: test, publish
```

The action installs [aux4](/install) and [pkger](/package-manager) for you, so the only prerequisite is checking out your repository first with `actions/checkout`.

## Setup

Linting and testing need no configuration. Publishing to the [aux4 Hub](https://hub.aux4.io) authenticates with a durable **publishing token** — a client id/secret pair you create once, then use to mint a fresh, short-lived hub token on every run. Pasting a plain access token into a secret would break CI as soon as it expires; the client id/secret does not expire, and each run mints its own token from it.

- **Create a publishing token.** On [user.aux4.io](https://user.aux4.io), go to your account → **Scopes** → open your scope → **Tokens** → **Create token**, choose **Publish to Hub**, and copy the **Client ID** and **Client Secret** from the one-time dialog (the secret is shown only once). This requires a paid Hub plan and ownership of the scope.
- **Store both as repository secrets** — e.g. `AUX4_CLIENT_ID` and `AUX4_CLIENT_SECRET` — under your repository's **Settings → Secrets and variables → Actions**. For a whole organization, add them once as organization secrets.
- **`GITHUB_TOKEN`** — provided automatically by GitHub Actions. Grant it write access so the action can push the version bump, create the tag, and publish the release:

```yaml
permissions:
  contents: write
```

The publish job mints a fresh hub token from the client id/secret and passes it to the action as `aux4_token` — see [Publish](#publish) and the [Full Workflow Example](#full-workflow-example).

## Lint

Validates your `.aux4` files for structural correctness, naming conventions, reference integrity, and best practices using [`aux4/lint`](https://hub.aux4.io/r/public/packages/aux4/lint).

```yaml
- uses: aux4/action@v1
  with:
    command: lint
    strict: 'true'
    resolve: 'true'
```

| Input | Description | Default |
|-------|-------------|---------|
| `strict` | Treats `-local` versions as errors | `false` |
| `resolve` | Validates `aux4` command calls against installed packages | `false` |
| `format` | Output format (`text` or `json`) | `text` |

When using `resolve`, list any packages your commands call via the `packages` input so they are installed before linting:

```yaml
- uses: aux4/action@v1
  with:
    command: lint
    resolve: 'true'
    packages: aux4/config aux4/api
```

## Test

Runs your [`.test.md`](/testing) suite with the [`aux4/test`](https://hub.aux4.io/r/public/packages/aux4/test) framework. The action packages your project, installs it, and runs the tests against the installed package — the same way an end user would run it.

```yaml
- uses: aux4/action@v1
  with:
    command: test
    build_command: npm run build
    coverage_threshold: '80'
```

| Input | Description | Default |
|-------|-------------|---------|
| `test_directory` | Directory containing test files | `test` |
| `build_command` | Build command to run before testing (e.g. `npm run build`, `aux4 builder linux`) | |
| `coverage_threshold` | Minimum step coverage % (0–100). Fails if below. `0` disables. | `0` |

When `coverage_threshold` is greater than `0`, a coverage check runs after the tests pass and fails the job if coverage is below the threshold:

```text
Coverage 50% is below threshold 80%
```

For Go packages, set `build_command` to the platform builder (`aux4 builder linux`, `aux4 builder darwin`) and run the job on the matching runner. For JS packages, use `build_command: npm run build`.

## Publish

Bumps the version, builds the package, publishes it to hub.aux4.io, then commits the bump, tags the release, and creates a GitHub Release.

Mint a fresh hub token from your `AUX4_CLIENT_ID` / `AUX4_CLIENT_SECRET` secrets in a step just before the action, then pass it in as `aux4_token`:

```yaml
- name: Mint hub token
  id: mint
  env:
    CLIENT_ID: ${{ secrets.AUX4_CLIENT_ID }}
    CLIENT_SECRET: ${{ secrets.AUX4_CLIENT_SECRET }}
  run: |
    TOK=$(curl -sf -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d grant_type=client_credentials https://sso.aux4.io/token | jq -r '.access_token // empty')
    if [ -z "$TOK" ]; then echo "::error::failed to mint hub token"; exit 1; fi
    echo "::add-mask::$TOK"
    echo "token=$TOK" >> "$GITHUB_OUTPUT"

- uses: aux4/action@v1
  with:
    command: publish
    level: patch
    aux4_token: ${{ steps.mint.outputs.token }}
    github_token: ${{ secrets.GITHUB_TOKEN }}
```

| Input | Description | Default |
|-------|-------------|---------|
| `level` | Release level (`patch`, `minor`, `major`) | `patch` |
| `aux4_token` | Freshly minted hub token (from the mint step), not a stored access token | *required* |
| `github_token` | GitHub token for creating releases | *required* |
| `aux4_image` | Docker image used to build and publish | `aux4/aux4:latest` |

The version in your `package/.aux4` is incremented automatically according to `level` — do not bump it by hand. The publish step pushes the bumped `.aux4` and a `v<version>` tag back to your branch.

### Outputs

| Output | Description |
|--------|-------------|
| `version` | The new package version |
| `scope` | The package scope |
| `name` | The package name |

## Common Inputs

These inputs apply to every command:

| Input | Description | Default |
|-------|-------------|---------|
| `command` | Command to run: `lint`, `test`, or `publish` | *required* |
| `working_directory` | Working directory containing the repository | `.` |
| `package_directory` | Directory containing the `.aux4` file | `package` |
| `system_packages` | Space-separated system packages to install (apt/brew) | |
| `npm_packages` | Space-separated npm packages to install globally | |
| `packages` | Space-separated aux4 packages to install | |
| `build_command` | Build command to run before the action | |

## Recommended Pipeline

Run lint first, tests in parallel across platforms once lint passes, and publish only after every check succeeds:

```text
lint → test-linux ─┐
                    ├→ publish
lint → test-darwin ─┘
```

## Full Workflow Example

A complete `.github/workflows/publish.yml` that lints, tests on Linux and macOS, and publishes on push to `main` — with a manual `workflow_dispatch` for choosing the release level:

```yaml
name: Publish Package

on:
  push:
    branches:
      - main
    paths:
      - '**'
      - '!.github/**'
      - '!README.md'
      - '!LICENSE'
      - '!.gitignore'

  workflow_dispatch:
    inputs:
      level:
        description: 'Release level (patch, minor, major)'
        required: true
        default: 'patch'
        type: choice
        options:
          - patch
          - minor
          - major

concurrency:
  group: publish-package
  cancel-in-progress: false

permissions:
  contents: write

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - name: Build
        run: npm run build

      - uses: aux4/action@v1
        with:
          command: lint
          strict: 'true'

  test-linux:
    needs: [lint]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - uses: aux4/action@v1
        with:
          command: test
          build_command: npm run build

  test-darwin:
    needs: [lint]
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - uses: aux4/action@v1
        with:
          command: test
          build_command: npm run build

  publish:
    needs: [test-linux, test-darwin]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - name: Build
        run: npm run build

      - name: Mint hub token
        id: mint
        env:
          CLIENT_ID: ${{ secrets.AUX4_CLIENT_ID }}
          CLIENT_SECRET: ${{ secrets.AUX4_CLIENT_SECRET }}
        run: |
          TOK=$(curl -sf -u "$CLIENT_ID:$CLIENT_SECRET" \
            -d grant_type=client_credentials https://sso.aux4.io/token | jq -r '.access_token // empty')
          if [ -z "$TOK" ]; then echo "::error::failed to mint hub token"; exit 1; fi
          echo "::add-mask::$TOK"
          echo "token=$TOK" >> "$GITHUB_OUTPUT"

      - uses: aux4/action@v1
        with:
          command: publish
          level: ${{ inputs.level || 'patch' }}
          aux4_token: ${{ steps.mint.outputs.token }}
          github_token: ${{ secrets.GITHUB_TOKEN }}
```

> The `publish` job needs `fetch-depth: 0` so the action can read the full history and push tags. Keep `concurrency` set so two pushes never race to publish the same version.

## Continuous Integration Only

For branches and pull requests where you want the checks without publishing, run just `lint` and `test` in a separate `ci.yml`:

```yaml
name: CI

on:
  pull_request:
  push:
    branches-ignore:
      - main

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: aux4/action@v1
        with:
          command: lint
          strict: 'true'

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: aux4/action@v1
        with:
          command: test
          build_command: npm run build
```

## Standalone Actions

`aux4/action@v1` is the recommended entry point, but each stage is also available as a focused action if you prefer to compose them yourself:

- [`aux4/lint-package-action@v1`](https://github.com/aux4/lint-package-action) — lint only
- [`aux4/test-package-action@v1`](https://github.com/aux4/test-package-action) — test only
- [`aux4/publish-package-action@v1`](https://github.com/aux4/publish-package-action) — build, publish, and release only

They accept the same stage-specific inputs described above.

---

## See Also

- [Packaging](/packaging)
- [Package Manager](/package-manager)
- [Testing](/testing)
- [Commands](/commands)
- [Docker](/docker)
