Skip to content
Merged
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
87 changes: 87 additions & 0 deletions .github/workflows/connector-docs-verify.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
on:
workflow_call:
inputs:
ref:
required: true
type: string

permissions:
contents: read
pull-requests: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout workflow repo
uses: actions/checkout@v5
with:
repository: ${{ job.workflow_repository }}
ref: ${{ job.workflow_sha }}
path: _workflow
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: _workflow/tools/mdx-lint/package-lock.json

- name: Check docs change
id: docs-change
env:
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
REPOSITORY: ${{ github.event.repository.full_name }}
run: node _workflow/tools/connector-docs/check-docs-change.mjs

- name: Install MDX lint dependencies
if: steps.docs-change.outputs.validate != 'false'
run: |
npm ci --ignore-scripts --silent --prefix _workflow/tools/mdx-lint 2>&1 | tail -n 1

- name: Checkout caller repo
if: steps.docs-change.outputs.validate != 'false'
uses: actions/checkout@v5
with:
repository: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
ref: ${{ inputs.ref }}
path: _caller
persist-credentials: false

- name: Resolve docs validation
if: steps.docs-change.outputs.validate != 'false'
id: docs
shell: bash
env:
PRECHECK: ${{ steps.docs-change.outputs.validate }}
run: |
set -euo pipefail

if [ "$PRECHECK" = "unknown" ] && [ ! -f "_caller/docs/connector.mdx" ]; then
echo "validate=false" >> "$GITHUB_OUTPUT"
echo "docs/connector.mdx missing; skipping MDX validation"
exit 0
fi

if [ ! -f "_caller/docs/connector.mdx" ]; then
echo "::error file=docs/connector.mdx::docs/connector.mdx changed but is missing at the checked-out ref."
exit 1
fi

echo "validate=true" >> "$GITHUB_OUTPUT"

- name: Validate MDX documentation
if: steps.docs.outputs.validate == 'true'
shell: bash
run: |
node _workflow/tools/mdx-lint/mdx-lint.mjs < _caller/docs/connector.mdx
LINT_RC=$?

if [ "$LINT_RC" -eq 0 ]; then
echo "MDX validation passed"
else
echo "::error file=docs/connector.mdx::MDX validation failed. See log output above for details."
exit 1
fi
79 changes: 23 additions & 56 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,32 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout workflow repo
uses: actions/checkout@v5
with:
repository: ${{ job.workflow_repository }}
ref: ${{ job.workflow_sha }}
path: _workflow
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: _workflow/tools/mdx-lint/package-lock.json

- name: Install MDX lint dependencies
run: |
npm ci --ignore-scripts --silent --prefix _workflow/tools/mdx-lint 2>&1 | tail -n 1

- name: Checkout caller repo
uses: actions/checkout@v5
with:
repository: ${{ github.event.repository.full_name }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
ref: ${{ inputs.ref }}
path: _caller
persist-credentials: false

- name: Check for docs/connector.mdx
id: check-docs
Expand All @@ -99,64 +119,11 @@ jobs:
echo "No docs/connector.mdx found, skipping MDX validation"
fi

- name: Setup Node
if: steps.check-docs.outputs.has_docs == 'true'
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install MDX lint dependencies
if: steps.check-docs.outputs.has_docs == 'true'
run: |
mkdir -p /tmp/mdx-lint && cd /tmp/mdx-lint
npm init -y --silent > /dev/null 2>&1
npm install --silent @mdx-js/mdx@3 remark-gfm@4 remark-frontmatter@5 2>&1 | tail -n 1

- name: Validate MDX documentation
if: steps.check-docs.outputs.has_docs == 'true'
shell: bash {0}
shell: bash
run: |
# Inline MDX lint: compile-only (no eval), with component allowlist.
# Use bash {0} (no -e) so we can capture the exit code ourselves.
# Write the lint script to the install directory so ESM resolution works
cat > /tmp/mdx-lint/mdx-lint.mjs << 'LINT_EOF'
import { compile } from "@mdx-js/mdx";
import remarkGfm from "remark-gfm";
import remarkFrontmatter from "remark-frontmatter";

const ALLOWED = new Set([
"Tip","Warning","Note","Info","Icon",
"Frame","Card","Tabs","Tab","Steps","Step",
]);

let content = "";
for await (const chunk of process.stdin) content += chunk;
if (!content.trim()) process.exit(0);

let compiled;
try {
compiled = String(await compile(content, {
outputFormat: "function-body",
remarkPlugins: [remarkGfm, remarkFrontmatter],
}));
} catch (err) {
console.error("mdx-lint: " + err.message);
process.exit(1);
}

const refs = [...compiled.matchAll(/_missingMdxReference\("([^"]+)"/g)]
.map(m => m[1])
.filter(name => !ALLOWED.has(name));
const unique = [...new Set(refs)];
if (unique.length > 0) {
for (const name of unique) {
console.error("mdx-lint: Unknown component <" + name + ">. Allowed: " + [...ALLOWED].join(", "));
}
process.exit(1);
}
LINT_EOF

node /tmp/mdx-lint/mdx-lint.mjs < _caller/docs/connector.mdx
node _workflow/tools/mdx-lint/mdx-lint.mjs < _caller/docs/connector.mdx
LINT_RC=$?

if [ "$LINT_RC" -eq 0 ]; then
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,37 @@ jobs:
| `run_tests` | No | `true` | Run `go test` |
| `connector` | No | `""` | Connector name — triggers [regression testing](docs/verify-workflow.md#regression) when set |

## Connector Docs Verify Workflow

Runs a standalone `docs/connector.mdx` validation check for connector repositories. See [detailed documentation](docs/connector-docs-verify.md) for behavior and rollout notes.

The workflow is safe to require on every connector pull request because it always reports a status. It skips validation when `docs/connector.mdx` is unchanged, fails if that file was changed and removed, and validates MDX safety when the file changed.

The reusable workflow checks out its own validator at the exact workflow commit, installs locked dependencies with npm lifecycle scripts disabled, and checks out caller code without persisted credentials.

### Usage

```yaml
name: Connector Docs

on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- main

jobs:
connector-docs:
uses: ConductorOne/github-workflows/.github/workflows/connector-docs-verify.yaml@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
```

When used with the job id `connector-docs`, the required status check context is `connector-docs / validate`.

Roll out the caller workflow before marking the check required. The shared workflow ref, such as `v4`, must include `connector-docs-verify.yaml` before connector repos call it.

## Available Actions

### Get Baton
Expand Down
102 changes: 102 additions & 0 deletions docs/connector-docs-verify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Connector Docs Verify Workflow

The `connector-docs-verify.yaml` workflow runs a standalone
`docs/connector.mdx` validation check for connector repositories.

## Overview

This workflow is meant to be safe as a required status check on every connector
pull request:

1. It checks out this workflow repository at the exact workflow commit.
2. It checks whether `docs/connector.mdx` changed in the pull request.
3. It installs the checked-in MDX validator dependencies with npm lifecycle
scripts disabled.
4. It checks out the caller repository at the requested ref with persisted
credentials disabled.
5. If the file is unchanged, the job exits successfully.
6. If the file changed, the job validates that the file still exists and passes
MDX safety checks.

The required check context is stable when the caller job id is
`connector-docs`:

```text
connector-docs / validate
```

## MDX Checks

The validator compiles MDX without evaluating PR content and rejects unsafe
constructs by walking the MDX AST before compilation:

- empty documentation
- NUL bytes or byte-order marks
- MDX imports or exports outside code fences
- MDX expression braces outside code fences
- event-handler attributes
- dangerous URL schemes after simple entity decoding
- unsupported JSX components

Allowed JSX components:

- `Card`
- `Check`
- `Frame`
- `Icon`
- `Info`
- `Note`
- `Step`
- `Steps`
- `Tab`
- `Tabs`
- `Tip`
- `Warning`

Keep the allowlist aligned with the connector registry renderer and server-side
documentation validators.

## Usage

```yaml
name: Connector Docs

on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- main

jobs:
connector-docs:
uses: ConductorOne/github-workflows/.github/workflows/connector-docs-verify.yaml@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
```

## Rollout Notes

Do not add workflow-level `paths` filters to the caller workflow when this
check is required by branch rules. A required check that never runs leaves pull
requests stuck waiting.

The workflow itself handles the path check and reports success when
`docs/connector.mdx` is unchanged.

Use a staged rollout:

1. Merge this workflow and update the shared workflow ref used by connector
repos, such as the `v4` tag, so callers can resolve
`connector-docs-verify.yaml`.
2. Add the caller workflow to connector repos without requiring the status
check yet.
3. Confirm `connector-docs / validate` appears and passes on both docs and
non-doc pull requests.
4. Require `connector-docs / validate` in branch rules only after the check is
present on targeted repos.

The existing `verify.yaml` docs job reuses the same MDX validator for
compatibility. Use this standalone workflow as the required docs safety gate
because it always reports the `connector-docs / validate` check that branch
rules target.
11 changes: 10 additions & 1 deletion docs/verify-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ When a pull request is opened or code is pushed to main, the shared verify workf

1. Runs `golangci-lint` on the connector code
2. Runs `go test` (optional, enabled by default)
3. Runs baton-regression verification (optional, when `connector` is provided)
3. Validates `docs/connector.mdx` when the file exists
4. Runs baton-regression verification (optional, when `connector` is provided)

## Jobs

Expand All @@ -20,6 +21,14 @@ Checks out the caller repo and runs `golangci-lint` with a 6-minute timeout. If

Runs `go test -v -covermode=count -json ./...` and annotates results. Skipped if `run_tests: false`.

### docs

Validates `docs/connector.mdx` when the file exists. The validator compiles MDX
without evaluating PR content and rejects unsupported JSX components.

For branch rules that need a standalone docs safety check, prefer the stricter
[`connector-docs-verify.yaml`](connector-docs-verify.md) workflow.

### regression

Runs the [baton-regression](https://github.com/ConductorOne/baton-regression) verification when `connector` is non-empty. The workflow is hosted in this repo but checks out baton-regression source from main at runtime. The regression job:
Expand Down
Loading