This directory holds the CI/CD workflows for the Viam documentation site, plus the Python helper scripts that some of them call. This document describes what each workflow does, when it runs, whether it blocks a pull request, what external services or secrets it depends on, and any known maintenance concerns.
For a one-line summary table, see the Continuous integration section of the top-level README.
- Trigger: what causes the workflow to run.
- Blocking: whether a failure blocks a pull request from merging.
"Informational" workflows set
continue-on-error: trueand never block. Scheduled workflows have no PR to block; they open a deduplicated GitHub issue on failure instead (see Failure notifications). - Secrets: repository secrets the workflow needs. Several scheduled jobs authenticate to a shared Viam test organization.
Note
Some scheduled jobs run code samples and SDK checks against a live Viam
organization using the TEST_ORG_ID, VIAM_API_KEY, and related secrets.
That org's data state (members, roles, locations, machines) directly affects
whether those jobs pass. See Test-org dependency.
- Purpose: Builds the production Hugo site and pushes search-index artifacts (tutorials and modular-resource models) into Typesense.
- Trigger: Push to
main; manual (workflow_dispatch). - Blocking: N/A (runs after merge to
main). - What it does:
buildjob runsmake build-prodwith Hugo 0.152.2 extended and uploads two Typesense JSON artifacts;upsert-tutorialsandupsert-modular-resourcesjobs runupload_tutorials.pyandget_modular_resources.pyto sync those artifacts into the search cluster. - Tests: None—build plus search-index sync only.
- Secrets:
TYPESENSE_TUTORIALS_API_KEY,TYPESENSE_API_KEY_R,VIAM_API_KEY,VIAM_API_KEY_ID,TEST_ORG_ID. - Notes: The
deployjob is commented out, so this workflow does not deploy to GitHub Pages—production hosting is handled by Netlify (see the Netlify badge in the README). Action versions are old (actions/checkout@v3,actions/configure-pages@v2).
- Purpose: Builds the site and checks internal links on every pull request.
- Trigger:
pull_request(all PRs). - Blocking: Yes (
continue-on-error: false). - What it does:
make build-dist-pr, thenwjdp/htmltest-actionwith.htmltest-local.yml(local links only). - Secrets: None.
- Notes: The header comment is copy-pasted from
run-htmltest.ymland is misleading.wjdp/htmltest-action@masteris pinned to a moving branch.
- Purpose: Weekly broken-link check that includes external links.
- Trigger: Schedule—
0 10 * * 2(Tuesdays 10:00 UTC). - Blocking: N/A (scheduled); opens a deduplicated GitHub issue labeled
ci-failureon failure (see Failure notifications). - What it does: Builds the site and runs
wjdp/htmltest-actionwith.htmltest.yml(the external-links-inclusive config); uploadshtmltest.log. - Secrets: None (uses the automatic
GITHUB_TOKENto open the failure issue). - Notes: Same moving-branch pin on
wjdp/htmltest-action@master. External link failures are expected to be noisy (third-party sites change/expire).
- Purpose: Runs the Vale prose style linter and reports violations as GitHub checks through reviewdog.
- Trigger:
pull_request(opened,synchronize). - Blocking: Yes—
fail_on_error: true,level: error. This is the prose-style gate referenced inCLAUDE.md. - Secrets:
GITHUB_TOKEN(automatic). - Notes: The Python 3.8 / venv setup steps are dead weight—Vale is a Go
binary and the venv is never used. Python 3.8 is end-of-life. The action is
pinned to the
reviewdogbranch rather than a version.
- Purpose: Spell-checks
docs/for common misspellings. - Trigger:
pull_request(opened,synchronize). - Blocking: Yes (action fails on misspellings).
- Secrets: None. Relies on
.codespellignorein the repo root.
- Purpose: Lints Markdown structure in
docs/against.markdownlint.yaml. - Trigger:
pull_request; manual. - Blocking: Informational (
continue-on-error: true). - Secrets: None.
- Notes: Uses the unmaintained
ruzickap/action-my-markdown-linter@v1andactions/checkout@v2(deprecated).
- Purpose: Runs Prettier in check mode over changed
docs/**/*.mdfiles. - Trigger:
pull_request; push tomain. - Blocking: Informational (
continue-on-error: true). - Secrets: None.
- Notes: The workflow name says "JS files" but it checks Markdown. Prettier
is pinned to
3.2.5, which can drift from the repo devDependency and cause CI/local mismatches.
- Purpose: Lints Python code blocks embedded in
docs/**/*.mdwithflake8-markdown. - Trigger:
pull_request; manual. - Blocking: Informational (
continue-on-error: true). - Secrets: None.
- Notes: Same dead Python-3.8/venv boilerplate as
vale-lint.yml(source env/bin/activateruns in its own shell and has no effect).
- Purpose: Runs every Python, Go, and TypeScript example under
static/include/examples/against a liveviam-serverand a live Viam org to verify the documented SDK samples still execute without error. - Trigger: Schedule—
0 9 * * 1(Mondays 09:00 UTC); push tomaintouchingstatic/include/examples/**or this workflow; manual. - Blocking: N/A (scheduled/push); opens a deduplicated GitHub issue labeled
ci-failureon failure (see Failure notifications). - What it does: Fetches a machine config from
app.viam.com, starts the stableviam-serverAppImage in the background, then runs each*.py,*.go, and*.tssample in turn, tallying pass/fail. Each language step records its failure count and a final "Evaluate results" step fails the job if any language had a failure, so one language failing no longer hides the others. - Tests: Yes—end-to-end execution of the real samples against live APIs.
- Secrets:
TEST_MACHINE_KEY,VIAM_API_KEY,VIAM_API_KEY_ID,TEST_ORG_ID,VIAM_API_KEY_DATA_REGIONS,VIAM_API_KEY_ID_DATA_REGIONS,TEST_EMAIL. - Notes / current status: The
viam-serverAppImage is re-pulled as "stable" each run, so a server release can change the test surface with no repo change. A literal machineid/key_idis hardcoded in the server-start step; if that machine or key is deleted the whole job fails at startup. See Test-org dependency.
- Purpose: Detects when the Viam SDKs gain or remove API methods that the docs' generated API reference has not accounted for.
- Trigger: Schedule—
0 10 * * 3(Wednesdays 10:00 UTC); manual. - Blocking: N/A—job-level
continue-on-error: true; opens a deduplicated GitHub issue labeledci-failureon failure (its only real signal; see Failure notifications). - What it does:
make coveragetestrunsupdate_sdk_methods.py --coverage, which scrapes the four SDK doc sites and the upstream gRPC protos and diffs them againstsdk_protos_map.csv; unmapped or missing methods fail the check. - Tests: Documentation-coverage validation (no live robot calls).
- Secrets: None (uses the automatic
GITHUB_TOKENto open the failure issue). - Notes: The cron comment says "weekdays" but the schedule is Wednesday
only. Because it scrapes external doc-site HTML, upstream layout changes can
break parsing and open spurious tickets. The
concurrency.groupreferences a PR number that is null for scheduled runs, so unrelated runs share one group.
- Purpose: When a PR opens, adds the
safe to buildlabel if the author is aviamroboticsorg member; otherwise posts a welcome comment. - Trigger:
pull_request_target(opened). - Blocking: No.
- Secrets:
PR_TOKEN. - Notes: The job condition checks for
synchronize/reopened, but the trigger only subscribes toopened, so those branches never fire. Any membership-check API error (not just non-membership) falls through to the contributor-comment path. Usesactions/github-script@v6.
- Purpose: On PRs, detects renamed/moved
.mdfiles and posts a sticky comment reminding the author to add redirect aliases. - Trigger:
pull_request_target(labeled,synchronize). - Blocking: No.
- Secrets:
PR_TOKEN. - Notes: The detection step uses the deprecated
::set-outputworkflow command, which GitHub has disabled, so the output is not populated and the comment step's gate never fires—the reminder is effectively dead and should be migrated to$GITHUB_OUTPUT.
- Purpose: On pushes to
mainthat touchdocs/, triggers Inkeep to re-sync the docs source for AI search/chat. - Trigger: Push to
main, pathsdocs/**. - Blocking: N/A.
- Secrets:
INKEEP_API_KEY(plusGITHUB_TOKEN). - Notes: Has both a top-level
pathsfilter and a redundant in-jobdorny/paths-filtercheck. Hardcoded InkeepsourceId.
Scheduled jobs have no PR to block, so they report failures by opening a GitHub
issue. Each of test-code-snippets.yml, check-methods.yml, and
run-htmltest.yml ends with a Report failure step (gated if: failure()) that
calls the local composite action .github/actions/report-ci-failure:
- It opens an issue titled
CI failure: <job name>labeledci-failure, with a link to the failing run. - If an open
ci-failureissue for the same job already exists, it adds a comment with the new run link instead of opening a duplicate, so repeated weekly failures collapse into one tracking issue. - It authenticates with the automatic
GITHUB_TOKEN; the workflows grantissues: write. No external service or extra secret is required.
This replaced the previous atlassian/gajira-* Jira integration, which had
stopped authenticating and left the scheduled jobs unmonitored. A scheduled
Claude Code session then triages open ci-failure issues daily—opening a fix PR
when the cause is clear, or commenting on the issue when it is not. See
CI failure triage for the full flow, setup, and the
triage prompt.
These Python files are invoked by the workflows above, not run on their own:
update_sdk_methods.py(+parse_python.py,parse_go.py,parse_typescript.py,parse_flutter.py,parser_utils.py)—the engine for the docs' autogenerated SDK API reference. It maps every Viam component, service, app, and robot API to its upstream*_grpc.pb.godefinition and scrapes each SDK's doc site to generate thestatic/include/.../generated/include files. In--coveragemode it instead reports method gaps (used bycheck-methods.yml).sdk_protos_map.csvis the hand-maintained proto-to-method mapping it reads.get_modular_resources.py—reads the modular-resources Typesense artifact and the Viam app registry and upserts models into the search cluster (used bydocs.yml).upload_tutorials.py—upserts the tutorials Typesense artifact into the search cluster (used bydocs.yml).requirements.txt—Python dependencies for thedocs.ymlindex-sync jobs (viam-sdk,asyncio,typesense).
test-code-snippets.yml (and the index-sync jobs in docs.yml) authenticate
to a shared Viam test organization identified by the TEST_ORG_ID secret. The
samples create, modify, and delete real resources (locations, API keys, roles,
datasets, data pipelines) in that org, and some assert on its existing state.
Practical consequences:
- The org's display name is reset to
docs-scheduled-testson each run of the orgs sample, and the samples reference a hardcoded location ID (pg5q3j3h95). The org that owns that location is the test org. fleet-api/fleet-management-api-orgs.pyrequires the org to have at least one member with no existing authorizations, so a fresh location-owner role can be added to them. If every member is already an owner, the sample fails.- Several samples make live API calls that can return transient
INTERNALerrors; these can cause intermittent (flaky) failures unrelated to docs changes.
Because the secret values are not readable, identify the live org through these
fingerprints (location pg5q3j3h95, org name docs-scheduled-tests) rather
than from the secret itself.
- Action versions are old across the board—
actions/checkout@v2/v3,setup-python@v4,github-script@v6, and twowjdp/htmltest-action@master(moving-branch) pins. Bumping these is a safe, batchable cleanup. - Two PR-automation workflows have dead logic—
alias-reminder.yml(::set-output) andpr-labeler.yml(event/condition mismatch). - Reliance on external HTML scraping and live services makes the scheduled jobs fragile; failures there are often environmental, not docs regressions.
- End-of-life Python 3.8 appears in
vale-lint.ymlandpython-lint.yml(in unused venv steps).
Work items to get the broken and degraded jobs back to a healthy, monitored state. Roughly ordered by impact.
Three scheduled jobs (test-code-snippets.yml, check-methods.yml,
run-htmltest.yml) previously reported failures only by opening a Jira ticket,
and the Jira steps were themselves failing—so these jobs ran unmonitored.
- Replace the broken
atlassian/gajira-*steps with the.github/actions/report-ci-failurecomposite action, which opens a deduplicated GitHub issue labeledci-failure. See Failure notifications. - Add a triage path: a Claude Code Remote routine picks up
ci-failureissues and opens a fix PR or comments.
- Add a member with no roles to the
docs-scheduled-teststest org sofleet-api/fleet-management-api-orgs.pycan grant a fresh location-owner role. (Sample-side change tracked in #5106, now merged.) - Re-run the job and confirm it goes green end to end.
- Isolate the Python, Go, and TypeScript runs so one language failing no longer hides the others (each records its failure count; a final "Evaluate results" step fails the job if any language failed).
- Replace the hardcoded machine
id/key_idin the server-start step with secrets so a rotated machine or key does not break startup. - Add resilience to the
data-pipelinesteardown deletes, which can return transientINTERNALerrors, so flaky backend responses do not fail the run.
- Migrate the moved-files detection from the disabled
::set-outputcommand to$GITHUB_OUTPUTso the reminder comment posts again. - Verify the reminder fires on a test PR that renames a
.mdfile.
- Confirm what consumes the
safe to buildlabel (likely Netlify deploy-preview gating); document the consumer or remove the workflow if nothing uses it. - Resolve the trigger/condition mismatch: either subscribe to
synchronizeandreopened, or drop them from the jobif. - Stop the membership-check error path from posting the contributor comment
on API errors (rate limits), and remove the leftover
console.logdebug.
- Decide whether coverage gaps should fail the run; if so, remove the
job-level
continue-on-error: trueonce the Jira signal is reliable. - Fix the
concurrency.groupso scheduled and manual runs do not cancel each other (it currently keys on a null PR number). - Correct the cron comment ("weekdays" → Wednesday only).
- Bump stale action versions:
actions/checkout@v2/v3,actions/setup-python@v4,actions/github-script@v6,actions/configure-pages@v2. - Pin
wjdp/htmltest-action@masterto a released version or commit SHA in both htmltest workflows. - Remove the unused Python 3.8 venv steps from
vale-lint.ymlandpython-lint.yml; movepython-lintoff end-of-life Python. - Delete the commented-out
deployjob fromdocs.yml(Netlify handles deployment). - Decide whether
markdown-lint.yml,prettier-lint.yml, andpython-lint.ymlshould block PRs or be removed; as informational-only jobs they duplicate the local pre-commit checks and never gate a merge. - Fix the misleading
name/header comments (prettier-lint.ymlsays "JS files"; the htmltest headers are copy-pasted).