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
53 changes: 52 additions & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Shared [reusable workflows](https://docs.github.com/en/actions/sharing-automatio

The `OTFABRIC_READ_TOKEN` secret gives access to private `github.com/otfabric/*` modules. In `go-ci.yml` it is needed when a module has private dependencies, and by the policy job when `.otfabric/policy.yaml` exists. The token must be able to read `otfabric/engineering`.

**Permissions.** The organisation's default `GITHUB_TOKEN` is read-only, and a caller must grant at least what a called workflow requests, or the run fails at startup with no job started. Grant `contents: read` and `id-token: write` for `go-ci.yml` (Codecov OIDC), and `contents: write` for the release workflows.
**Permissions.** The organisation's default `GITHUB_TOKEN` is read-only, and a caller must grant at least what a called workflow requests, or the run fails at startup with no job started. Grant `contents: read` and `id-token: write` for `go-ci.yml` (Codecov OIDC), `contents: read` for `docs-check.yml`, and `contents: write` for the release workflows.

**Versions.** `v3` turns on govulncheck, tests `cmd/`, uses atomic coverage and starts the matrix at the go directive. `v2` and `v2.1.0` were moved to the same commit by the maintainer on 2026-10-30, so they have these defaults too.

Expand All @@ -19,6 +19,7 @@ Reusable workflows share whole pipelines (the caller's job is `uses:` one of the
| [`go-ci.yml`](go-ci.yml) | Lint, static analysis, coverage, and multi-version test matrix |
| [`go-package-release.yml`](go-package-release.yml) | Semver-tagged release for Go libraries / packages |
| [`go-binary-release.yml`](go-binary-release.yml) | Cross-compiled binary release with SHA-256 checksums |
| [`docs-check.yml`](docs-check.yml) | [docsync](https://github.com/otfabric/docsync) and otfabric-policy, on every push, Markdown-only ones included |

## Which workflows to use

Expand Down Expand Up @@ -148,6 +149,56 @@ Both release workflows support an optional `RELEASE.md` file in the repo root. W

Sections are delimited by `---`. Only the section matching the tag being released is extracted.

## Markdown-only changes

A push or pull request that changes only Markdown needs no build, tests or
interop run. The heavy workflows of a repository skip it:

```yaml
on:
push:
branches: [main]
paths-ignore: ['**/*.md']
pull_request:
paths-ignore: ['**/*.md']
```

A workflow that checks a generated Markdown file (for example `OBJECTS.md`)
lists it instead: `paths: ['**', '!**/*.md', 'OBJECTS.md']`. Tag pushes and
`workflow_dispatch` are never filtered.

The documents are still checked: `docs-check.yml` runs on every push and pull
request, with no path filter, so the Docs and Policy checks always report. A
branch protection rule must require those, never a job of a workflow that a
path filter can skip: a skipped required workflow leaves the check pending.

```yaml
# .github/workflows/docs.yml
name: Docs
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
docs:
uses: otfabric/.github/.github/workflows/docs-check.yml@v3
secrets: inherit
```

| Input | Default | Description |
|-------|---------|-------------|
| `module-dir` | `.` | Directory of the Go module docsync checks |
| `docsync-version` | `v0.1.0` | Tag of `otfabric/docsync` to run |
| `mode` | `enforce` | `warn` turns findings into warnings; a docsync error fails in both modes |
| `run-policy` | `true` | Run `otfabric-policy`, as in `go-ci.yml` |
| `policy-version` | `v1.3.0` | Tag of `otfabric/engineering` whose `otfabric-policy` to run |
| `timeout-minutes` | `15` | Job timeout |

With the Policy job here, a repository that calls both can set
`run-policy: false` in `go-ci.yml`.

## Inputs reference

Each workflow's inputs are fully documented with descriptions inside the YAML files themselves. Key inputs worth highlighting:
Expand Down
237 changes: 237 additions & 0 deletions .github/workflows/docs-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
#
# Docs Check — Reusable Workflow
#
# Checks that a Go module's documents say what its code does, with
# otfabric/docsync, and runs otfabric-policy. It is cheap, so it runs on every
# push and pull request, including those that change Markdown only, which the
# heavy workflows skip with paths-ignore. That makes its jobs the ones a
# branch protection rule can require.
#
# Jobs:
# docsync — docsync check in module-dir, annotations and a summary.
# policy — Optional (run-policy), otfabric-policy on repositories with
# .otfabric/policy.yaml, as in go-ci.yml.
#
# Caller example (.github/workflows/docs.yml):
#
# name: Docs
# on:
# push:
# branches: [main]
# pull_request:
# permissions:
# contents: read
# jobs:
# docs:
# uses: otfabric/.github/.github/workflows/docs-check.yml@v3
# secrets: inherit

name: Docs Check

on:
workflow_call:
inputs:
module-dir:
description: "Directory of the Go module to check."
required: false
type: string
default: "."
docsync-version:
description: "Tag of otfabric/docsync to run."
required: false
type: string
default: "v0.1.0"
mode:
description: >-
enforce: findings fail the job. warn: findings are annotations and
the job passes. A docsync error (exit code 2 or more) fails the job
in both modes.
required: false
type: string
default: "enforce"
timeout-minutes:
description: "Job timeout in minutes."
required: false
type: number
default: 15
run-policy:
description: >-
Run otfabric-policy on the repository root, in its own job, when
.otfabric/policy.yaml exists. Needs OTFABRIC_READ_TOKEN.
required: false
type: boolean
default: true
policy-version:
description: "Tag of otfabric/engineering whose otfabric-policy to run."
required: false
type: string
default: "v1.3.0"
secrets:
OTFABRIC_READ_TOKEN:
description: >-
PAT with read access to private github.com/otfabric/* modules.
docsync type-checks the documents' Go blocks against the module, so
it loads the module's dependencies; the policy job fetches
otfabric-policy. Forks and Dependabot never have it.
required: false

permissions:
contents: read

env:
GOPRIVATE: github.com/otfabric/*

concurrency:
group: docs-check-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ inputs.module-dir }}
cancel-in-progress: true

jobs:
docsync:
name: Docs
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout-minutes }}
permissions:
contents: read
defaults:
run:
working-directory: ${{ inputs.module-dir }}

steps:
# The tags and history: DS-SEC compares SECURITY.md with the latest
# tag, DS-REL compares RELEASE.md with the commit dates.
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false

# docsync needs Go 1.26; the module's go directive only sets a floor,
# and the type check of the documents runs on the toolchain it is given.
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: stable
cache: true
cache-dependency-path: |
${{ inputs.module-dir }}/go.sum

- name: Configure private module access
shell: bash
env:
TOKEN: ${{ secrets.OTFABRIC_READ_TOKEN }}
run: |
if [ -n "${TOKEN}" ]; then
git config --global url."https://x-access-token:${TOKEN}@github.com/otfabric/".insteadOf "https://github.com/otfabric/"
fi

# Installed, not run with go run, which turns every exit code into 1.
- name: Install docsync
shell: bash
env:
GOWORK: "off"
VERSION: ${{ inputs.docsync-version }}
run: GOBIN="$RUNNER_TEMP/bin" go install "github.com/otfabric/docsync@${VERSION}"

# One run gives the annotations and the summary, so the module is loaded
# once. Exit code 1 means findings, 2 and above an error of docsync or
# of the configuration.
- name: docsync
shell: bash
env:
GOWORK: "off"
VERSION: ${{ inputs.docsync-version }}
MODE: ${{ inputs.mode }}
run: |
set +e -uo pipefail
case "$MODE" in enforce|warn) ;; *) echo "::error::mode must be enforce or warn, not '$MODE'"; exit 2 ;; esac
"$RUNNER_TEMP/bin/docsync" check --format github . > "$RUNNER_TEMP/docsync.txt"
code=$?
if [ "$MODE" = warn ]; then
sed 's/^::error /::warning /' "$RUNNER_TEMP/docsync.txt"
else
cat "$RUNNER_TEMP/docsync.txt"
fi
count=$(grep -c '^::error ' "$RUNNER_TEMP/docsync.txt" || true)
{
echo "### docsync ${VERSION}"
echo
if [ "$code" -eq 0 ]; then
echo "The documents agree with the code."
elif [ "$code" -eq 1 ]; then
echo "$count findings (mode: $MODE). Run \`go run github.com/otfabric/docsync@${VERSION} check .\` locally for the list."
else
echo "docsync failed with exit code $code; see the log."
fi
} >> "$GITHUB_STEP_SUMMARY"
if [ "$code" -eq 1 ] && [ "$MODE" = warn ]; then
exit 0
fi
exit "$code"

# As in go-ci.yml: the check is about the repository, so it runs at the
# root. A secret cannot be read in a job-level if, so a step decides.
policy:
name: Policy
if: inputs.run-policy
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout-minutes }}
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v7
with:
persist-credentials: false

- name: Decide whether the policy check runs
id: gate
shell: bash
env:
TOKEN: ${{ secrets.OTFABRIC_READ_TOKEN }}
EVENT: ${{ github.event_name }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
run=false
if [ ! -f .otfabric/policy.yaml ]; then
echo "::notice::otfabric-policy skipped: no .otfabric/policy.yaml"
elif [ "$EVENT" = pull_request ] && [ "$HEAD_REPO" != "$REPO" ]; then
echo "::notice::otfabric-policy skipped: pull request from a fork has no secrets"
elif [ -n "$TOKEN" ]; then
run=true
elif [ "$ACTOR" = 'dependabot[bot]' ]; then
echo "::notice::otfabric-policy skipped: Dependabot runs have no Actions secrets"
else
echo "::error::OTFABRIC_READ_TOKEN is empty, so otfabric-policy cannot be fetched. Pass it with 'secrets: inherit', or set run-policy: false."
exit 1
fi
echo "run=$run" >> "$GITHUB_OUTPUT"

- name: Set up Go
if: steps.gate.outputs.run == 'true'
uses: actions/setup-go@v7
with:
go-version: stable
cache: false

- name: Configure private module access
if: steps.gate.outputs.run == 'true'
shell: bash
env:
TOKEN: ${{ secrets.OTFABRIC_READ_TOKEN }}
run: git config --global url."https://x-access-token:${TOKEN}@github.com/otfabric/engineering".insteadOf "https://github.com/otfabric/engineering"

- name: otfabric-policy
if: steps.gate.outputs.run == 'true'
shell: bash
env:
GOWORK: "off"
GOPRIVATE: github.com/otfabric/engineering
VERSION: ${{ inputs.policy-version }}
run: |
set -euo pipefail
go run "github.com/otfabric/engineering/cmd/otfabric-policy@${VERSION}" check -format markdown . | tee -a "$GITHUB_STEP_SUMMARY"
31 changes: 31 additions & 0 deletions .github/workflows/selftest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@ jobs:
run-govulncheck: false
run-policy: false

# docs-check.yml: a module whose documents agree with its code, enforced,
# with the policy job on.
docs-check-pass:
uses: ./.github/workflows/docs-check.yml
with:
module-dir: testdata/docs-check/pass
mode: enforce
policy-version: v1.2.0
secrets:
OTFABRIC_READ_TOKEN: ${{ secrets.OTFABRIC_READ_TOKEN }}

# A module with a finding passes in warn mode.
docs-check-warn:
uses: ./.github/workflows/docs-check.yml
with:
module-dir: testdata/docs-check/fail
mode: warn
run-policy: false

matrix-floor:
name: Matrix floor cases
runs-on: ubuntu-latest
Expand Down Expand Up @@ -112,6 +131,18 @@ jobs:
- uses: actions/checkout@v7
- run: scripts/selftest/policy.sh

docs-check-modes:
name: Docs check cases
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: stable
cache: false
- run: scripts/selftest/docs-check.sh

release-notes:
name: Release notes extraction
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## What this is

The organisation's shared GitHub configuration: the reusable workflows `go-ci.yml`, `go-package-release.yml` and `go-binary-release.yml`, the composite actions for the `*-interop` repositories, the organisation profile page, and the selftest of the workflows. There is no product and no Go module of its own; the Go code under `testdata/` is fixtures. Every OT Fabric repository depends on this one, so a mistake here reaches all of them. Profile: **infrastructure**.
The organisation's shared GitHub configuration: the reusable workflows `go-ci.yml`, `docs-check.yml`, `go-package-release.yml` and `go-binary-release.yml`, the composite actions for the `*-interop` repositories, the organisation profile page, and the selftest of the workflows. There is no product and no Go module of its own; the Go code under `testdata/` is fixtures. Every OT Fabric repository depends on this one, so a mistake here reaches all of them. Profile: **infrastructure**.

## Organisation standards

Expand Down Expand Up @@ -34,6 +34,7 @@ This repository follows **otfabric/engineering policy 1.2**, profile **infrastru
| anything | `go run ./cmd/otfabric-policy check <this repository>` from otfabric/engineering |
| `go-ci.yml` policy steps | `scripts/selftest/policy.sh` |
| release notes extraction | `scripts/selftest/release-notes.sh` |
| `docs-check.yml` docsync step | `scripts/selftest/docs-check.sh` |
| a workflow | the Selftest workflow, on the pull request |

There is no Makefile.
Expand Down
Loading
Loading