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
11 changes: 11 additions & 0 deletions docs/.vitepress/config/theme.mts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ const defaultSidebar = [
{ text: "Zed", link: "/integrations/editors/zed" },
],
},
{
text: "CI Integrations",
collapsed: false,
items: [
{ text: "Overview", link: "/integrations/ci" },
{ text: "GitHub Actions", link: "/integrations/ci/github-actions" },
{ text: "GitLab CI", link: "/integrations/ci/gitlab" },
{ text: "Bitbucket Pipelines", link: "/integrations/ci/bitbucket" },
{ text: "Reviewdog", link: "/integrations/ci/reviewdog" },
],
},
{
text: "Language Bindings",
collapsed: false,
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/integrations/ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Using Herb in CI

Run the Herb [Linter](/projects/linter), [Formatter](/projects/formatter), and parser [analyzer](/bindings/ruby/reference) as part of your CI pipeline to keep HTML+ERB templates consistent and catch regressions on every push.

## Available Integrations

- **[GitHub Actions](/integrations/ci/github-actions)** - Inline PR annotations are enabled automatically
- **[GitLab CI](/integrations/ci/gitlab)** - Runs in any Node or Ruby image; optional Code Quality report
- **[Bitbucket Pipelines](/integrations/ci/bitbucket)** - Parallel lint, format, and analyze steps
- **[Reviewdog](/integrations/ci/reviewdog)** - Post linter findings as inline review comments

See the [Linter](/projects/linter) and [Formatter](/projects/formatter) docs for the full list of CLI flags available in each snippet.
33 changes: 33 additions & 0 deletions docs/docs/integrations/ci/bitbucket.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: Using Herb with Bitbucket Pipelines
---

# Bitbucket Pipelines

Run lint, format check, and analyzer as parallel steps.

::: warning Formatter is in experimental preview
`@herb-tools/formatter` is in early development, and `--check` will fail on any codebase that hasn't already been run through `herb-format`. Run `npx --yes @herb-tools/formatter app/views` once and commit the result before enabling the format step.
:::

```yaml [bitbucket-pipelines.yml]
image: node:20

pipelines:
default:
- parallel:
- step:
name: Herb Lint
script:
- npx --yes @herb-tools/linter --fail-level warning
- step:
name: Herb Format Check
script:
- npx --yes @herb-tools/formatter --check app/views
- step:
name: Herb Analyze
image: ruby:3.3
script:
- gem install herb
- herb analyze .
```
82 changes: 82 additions & 0 deletions docs/docs/integrations/ci/github-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: Using Herb with GitHub Actions
---

# GitHub Actions

The linter auto-detects GitHub Actions via the `GITHUB_ACTIONS` environment variable and emits inline PR annotations by default — no extra flag required.

## Lint + Format check

```yaml [.github/workflows/herb.yml]
name: Herb

on:
push:
branches: [main]
pull_request:

jobs:
herb:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Lint HTML+ERB templates
run: npx --yes @herb-tools/linter

- name: Check HTML+ERB formatting
run: npx --yes @herb-tools/formatter --check app/views
```

The formatter requires an explicit path in CI — without one it reads from stdin, which `--check` rejects. Adjust `app/views` to match where your templates live.

::: warning Formatter is in experimental preview
`@herb-tools/formatter` prints an experimental-preview banner on every invocation, and `--check` will fail on any codebase that hasn't already been run through `herb-format`. Before wiring this step into CI, see [Adopting the formatter](#adopting-the-formatter) below.
:::

## Adopting the formatter

`--check` only passes on an already-formatted tree. Run the formatter once, commit the result in its own change, then enable the CI step:

```bash
npx --yes @herb-tools/formatter app/views
```

Review the diff, commit it separately from unrelated changes, and only then add the `--check` step to your workflow.

## Stricter lint gate

Fail the build on warnings in addition to errors:

```yaml
- name: Lint HTML+ERB templates
run: npx --yes @herb-tools/linter --fail-level warning
```

## Parser analysis (Ruby)

`herb analyze` reports how many templates parse cleanly and exits non-zero when any issue is detected (see `lib/herb/cli.rb`), so it can gate the build on its own.

Append these steps to the `herb` job above to also run the parser analyzer, or put them in a separate job if you prefer to parallelize:

```yaml
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Analyze HTML+ERB templates
run: bundle exec herb analyze .
```

::: tip
If your project doesn't have `herb` in its `Gemfile`, replace `bundle exec herb` with `gem install herb && herb analyze .`, and drop `bundler-cache: true` from `ruby/setup-ruby` unless another step in the job needs it.
:::

## See also

- [Reviewdog](/integrations/ci/reviewdog) — post linter findings as inline PR review comments
60 changes: 60 additions & 0 deletions docs/docs/integrations/ci/gitlab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Using Herb with GitLab CI
---

# GitLab CI

GitLab doesn't consume GitHub-style annotations, so pass `--no-github` to keep output readable in job logs.

## Lint, format, and analyze

```yaml [.gitlab-ci.yml]
herb:lint:
image: node:20
script:
- npx --yes @herb-tools/linter --no-github --fail-level warning

herb:format:
image: node:20
script:
- npx --yes @herb-tools/formatter --check app/views

herb:analyze:
image: ruby:3.3
script:
- gem install herb
- herb analyze .
```

`herb analyze` exits non-zero when issues are detected, so no extra gating is needed.

::: warning Formatter is in experimental preview
`@herb-tools/formatter` is in early development, and `--check` will fail on any codebase that hasn't already been run through `herb-format`. Run `npx --yes @herb-tools/formatter app/views` once and commit the result before enabling the `herb:format` job.
:::

## Code Quality report

GitLab's [Code Quality report](https://docs.gitlab.com/ee/ci/testing/code_quality.html) expects a specific JSON schema (`description`, `check_name`, `fingerprint`, `severity`, `location.path`, `location.lines.begin`). The linter's `--json` output isn't in that shape, so transform it with `jq`:

```yaml
herb:lint:
image: node:20
before_script:
- apt-get update && apt-get install -y jq
script:
- npx --yes @herb-tools/linter --no-github --json > herb-lint.raw.json || true
- |
jq '[ .offenses[] | {
description: .message,
check_name: .code,
fingerprint: (.filename + ":" + .code + ":" + (.location.start.line|tostring) + ":" + (.location.start.column|tostring)),
severity: (if .severity == "error" then "major" elif .severity == "warning" then "minor" else "info" end),
location: { path: .filename, lines: { begin: .location.start.line } }
}]' herb-lint.raw.json > gl-code-quality-report.json
artifacts:
when: always
reports:
codequality: gl-code-quality-report.json
```

The `|| true` keeps the job running so the `jq` step still emits the report when the linter exits non-zero. Fail the pipeline on findings in a separate job if you want gating.
70 changes: 70 additions & 0 deletions docs/docs/integrations/ci/reviewdog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Using Herb with Reviewdog
---

# Reviewdog

[Reviewdog](https://github.com/reviewdog/reviewdog) posts linter findings as inline review comments on pull and merge requests across GitHub, GitLab, and other providers.

The linter's `--format=simple` output is human-oriented (file on one line, then indented `line:col message`) and isn't well-suited to reviewdog's `errorformat` parser. Use `--json` and transform it to [`rdjson`](https://github.com/reviewdog/reviewdog#rdjson) with `jq` instead — the JSON shape is documented in the [Linter README](/projects/linter) and source of truth for the transform below.

## GitHub Actions

```yaml [.github/workflows/herb-reviewdog.yml]
name: Herb (reviewdog)

on: [pull_request]

permissions:
contents: read
pull-requests: write
checks: write

jobs:
herb:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: reviewdog/action-setup@v1

- name: Run Herb linter via reviewdog
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx --yes @herb-tools/linter --no-github --json \
| jq '{
source: { name: "herb-lint" },
diagnostics: [ .offenses[] | {
message: .message,
location: {
path: .filename,
range: {
start: { line: .location.start.line, column: .location.start.column },
end: { line: .location.end.line, column: .location.end.column }
}
},
severity: (.severity | ascii_upcase),
code: { value: .code }
}]
}' \
| reviewdog -f=rdjson -name="herb-lint" -reporter=github-pr-review -fail-level=error
```

::: tip
The linter exits non-zero when offenses are found, but reviewdog's own `-fail-level` is what decides whether the CI step fails. Bash pipelines return the last command's exit code by default, so reviewdog's exit code wins.
:::

## Severity mapping

The linter emits `error`, `warning`, `info`, and `hint` severities. Reviewdog's `rdjson` accepts `ERROR`, `WARNING`, and `INFO`. The jq snippet above uppercases the severity string directly, which works for `error`/`warning`/`info`; `hint` is not a valid rdjson severity and will be rejected. If you use hint-level rules, map them explicitly:

```jq
severity: (
if .severity == "error" then "ERROR"
elif .severity == "warning" then "WARNING"
else "INFO" end
)
```