Reusable Workflow Reference

August 3, 2026 ยท View on GitHub

All reusable workflows are in .github/workflows/ and are called via:

uses: OpenVoiceOS/gh-automations/.github/workflows/<name>.yml@dev

Ref: Always use @dev.


publish-alpha.yml

Runs on PR merge to dev. Bumps the version, optionally updates changelog and creates a pre-release tag, then opens a release PR to master.

Source: .github/workflows/publish-alpha.yml

Inputs

InputTypeDefaultDescription
version_filestringversion.pyRelative path to the version.py file inside the repo
branchstringdevSource branch to checkout and commit back to
publish_prereleasebooleanfalseCreate a GitHub pre-release tag after version bump
propose_releasebooleantrueOpen a PR from release-X.Y.ZaN to master
update_changelogbooleanfalseGenerate and commit CHANGELOG.md using github-changelog-generator
changelog_filestringCHANGELOG.mdPath to the changelog file
changelog_max_issuesnumber50Max issues to include in changelog
publish_pypibooleanfalsePublish to PyPI after version bump (built inline within this workflow)
notify_matrixbooleanfalseSend Matrix notification on merged PR
matrix_channelstring!WjxEKjjINpyBRPFgxl:krbel.duckdns.orgMatrix room ID (default: OVOS main channel)
matrix_homeserverstringmatrix.orgMatrix homeserver URL
matrix_messagestring""Custom Matrix message. Empty = default "PR merged" message.
skip_bot_prsbooleantrueSkip version bump for PRs from allcontributors[bot] and pre-commit-ci[bot]. Renovate and Dependabot are intentionally NOT skipped.
runnerstringubuntu-latestRunner label
setup_pystringsetup.pyDeprecated. Accepted but not used. Version is read from version_file.

Outputs

OutputDescription
versionThe new version string (e.g. 1.2.3a4), from bump_version job
changelogChangelog content (only populated when update_changelog: true)

Jobs

JobConditionDescription
bump_versionmerged == true && not a skipped bot || workflow_dispatchDetermines bump type from PR labels, calls update_version.py, commits and pushes to branch via git-auto-commit-action@v5. Skips PRs from allcontributors[bot] and pre-commit-ci[bot] when skip_bot_prs: true.
update_changelogupdate_changelog: true + bump_version succeededCalls github-changelog-generator-action@v2.3, commits result
tag_prereleasepublish_prerelease: true + bump_version succeededCreates GitHub pre-release via ncipollo/release-action@v1
propose_releasepropose_release: true + bump_version succeededCreates release-X.Y.ZaN branch, opens PR to master via GitHub API
publish_pypipublish_pypi: true + bump_version succeededBuilds with python -m build, publishes via pypa/gh-action-pypi-publish@master
notifynotify_matrix: true + bump_version succeeded + PR mergedCalls notify-matrix.yml with a canned message

Bot guard

bump_version only runs when:

  • A PR was merged (github.event.pull_request.merged == true), or
  • Triggered manually (workflow_dispatch)

This prevents spurious runs when a PR is closed without merging.

Typical usage

name: Release Alpha and Propose Stable

on:
  workflow_dispatch:
  pull_request:
    types: [closed]
    branches: [dev]

jobs:
  publish_alpha:
    if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
    uses: OpenVoiceOS/gh-automations/.github/workflows/publish-alpha.yml@dev
    secrets: inherit
    with:
      branch: 'dev'
      version_file: 'my_package/version.py'
      update_changelog: true
      publish_prerelease: true
      propose_release: true
      changelog_max_issues: 100

Notes

  • publish_pypi: true uses pypa/gh-action-pypi-publish@release/v1 (pinned to stable tag).
  • propose_release uses git checkout -B (force-create) and gh pr create with duplicate-check โ€” both steps are idempotent on retry.

publish-stable.yml

Runs on push to master (typically triggered by merging the release PR). Removes the alpha suffix from version.py, commits, then creates a GitHub release tag.

Source: .github/workflows/publish-stable.yml

Inputs

InputTypeDefaultDescription
version_filestringversion.pyRelative path to version.py
branchstringmasterBranch to checkout and commit the stable version to
publish_releasebooleantrueCreate a GitHub release tag
publish_pypibooleanfalsePublish to PyPI after declaring stable
sync_devbooleanfalsePush master โ†’ dev after stable release to keep branches in sync
notify_matrixbooleanfalseSend Matrix notification on stable release
matrix_channelstring!WjxEKjjINpyBRPFgxl:krbel.duckdns.orgMatrix room ID
matrix_homeserverstringmatrix.orgMatrix homeserver URL
matrix_messagestring""Custom message. Empty = default "stable release" message.
runnerstringubuntu-latestRunner label
setup_pystringsetup.pyDeprecated. Accepted but not used.

Outputs

OutputDescription
versionThe stable version string (e.g. 1.2.3), from bump_version job

Jobs

JobConditionDescription
bump_versiongithub.actor != 'github-actions[bot]'Calls remove_alpha.py, commits via git-auto-commit-action@v5
tag_releasepublish_release: true + bump_version succeededCreates GitHub release via ncipollo/release-action@v1
publish_pypipublish_pypi: true + bump_version succeededBuilds and publishes to PyPI (stable)
sync_devsync_dev: true + bump_version succeededPushes master โ†’ dev via ad-m/github-push-action@v0.8.0
notifynotify_matrix: true + bump_version succeededCalls notify-matrix.yml@dev with configurable channel and message

Bot guard

bump_version skips when github.actor == 'github-actions[bot]' (publish-stable.yml:37). This prevents an infinite loop: the version commit pushed by git-auto-commit-action would otherwise re-trigger this workflow on push: master.

The calling repo's publish_stable.yml job also carries this guard (if: github.actor != 'github-actions[bot]') for belt-and-suspenders protection.

Typical usage

name: Stable Release
on:
  push:
    branches: [master]
  workflow_dispatch:

jobs:
  publish_stable:
    if: github.actor != 'github-actions[bot]'
    uses: OpenVoiceOS/gh-automations/.github/workflows/publish-stable.yml@dev
    secrets: inherit
    with:
      branch: 'master'
      version_file: 'my_package/version.py'
      publish_release: true
      sync_dev: true

build-tests.yml

Runs build, install, and optionally tests across a configurable matrix of Python versions. Posts a ๐Ÿ”จ Build Tests section to the PR comment. Also performs a channel compatibility check when package_name and version_file are provided.

Source: .github/workflows/build-tests.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionsstring'["3.10", "3.11", "3.12", "3.13", "3.14"]'JSON array of Python versions to test against
system_depsstring""Extra apt packages to install before building (space-separated). Base packages python3-dev libssl-dev are always installed.
install_extrasstring""pip extras appended when installing the built wheel, e.g. test or dev,test
test_pathstring""Path passed to pytest after install. Leave empty to skip test execution (build/install verification only).
pytest_argsstring""Extra arguments appended to the pytest invocation, e.g. --capture=tee-sys to keep a crashing test's output visible. Same name and meaning as on channel-compat.yml.
test_envstring""Extra environment variables for the test step, as newline-separated KEY=value pairs, e.g. PYTHONFAULTHANDLER=1 and RUST_BACKTRACE=full to surface a native crash's traceback instead of a bare Fatal Python error: Aborted. Appended to $GITHUB_ENV before the test step runs.
pr_commentbooleantruePost a ๐Ÿ”จ Build Tests section to the OVOS PR Checks comment. Only fires on pull_request events.
package_namestring""Package name for the channel compatibility check. If empty, auto-reads from pyproject.toml/setup.py. Both package_name and version_file must resolve for the channel check to run.
version_filestring""Path to version.py in the calling repo (relative to repo root). If empty, auto-detects. Needed for the channel compatibility check.

Jobs

JobDescription
build_testsMatrix job. Runs python -m build, installs the resulting wheel (with extras if specified), optionally runs pytest. Saves per-version result as an artifact.
post_build_reportRuns after the matrix, only on PR events with pr_comment: true. Downloads all result artifacts, runs the channel compatibility check, formats and posts the section:build PR comment.

Channel compatibility check

The post_build_report job checks out OpenVoiceOS/ovos-releases and calls scripts/check_release_channels.py to verify whether the current version of the package is already pinned or constrained in the alpha/testing/stable channel files. This check only runs when both package_name and a readable version_file can be resolved. If either is missing or the version cannot be parsed, the channel check is silently skipped and the rest of the PR comment is still posted.

Typical usage

name: Run Build Tests
on:
  push:
    branches: [master]
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  build_tests:
    uses: OpenVoiceOS/gh-automations/.github/workflows/build-tests.yml@dev
    secrets: inherit
    with:
      python_versions: '["3.10", "3.11", "3.12"]'
      install_extras: 'test'
      test_path: 'test/'
      package_name: 'my-package'
      version_file: 'my_package/version.py'

Notes

  • OPM (plugin detection) inputs were removed from this workflow. Use opm-check.yml for OPM validation.
  • The matrix uses fail-fast: false so all versions are tested even if one fails.

channel-compat.yml

Runs a repo's test suite against the package versions one OVOS distro release channel pins, instead of against current dev siblings.

The distro publishes one constraints file per channel:

ChannelURL
stablehttps://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-stable.txt
testinghttps://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-testing.txt

Those files are what a device actually runs, and they sit well below dev โ€” as of writing, stable pins ovos-workshop >=3.4.0,<3.5.0 and ovos-core >=1.3.1,<1.4.0, testing pins ovos-workshop >=7.0.6,<8.0.0 and ovos-core >=2.1.1,<3.0.0. A change can be green on dev and still break every device on the fleet. This workflow is the gate for that.

Not to be confused with the build-tests.yml channel compatibility check, which asks a different question: whether the package's current version is already listed in the ovos-releases channel files. That one reads release metadata; this one installs and runs code.

The one rule

The channel wins for every package it names, except the repo under test. The PR is the thing being judged, so its own version comes from the checkout. pip has no "constrain everything except X", so the workflow strips the repo's own distribution name (read from pyproject.toml, falling back to setup.py --name) out of the fetched constraints file before installing.

Inputs

InputTypeDefaultDescription
channel_urlstringrequiredRaw URL of the channel constraints file.
channel_namestring""Label used in job output and artifact names. Defaults to the filename with constraints- and .txt stripped.
runnerstringubuntu-latestRunner label.
test_pathstringtest/Path passed to pytest.
python_versionstring3.11Python version.
system_depsstring""Extra apt packages. ovos-padatious needs swig libfann-dev.
pre_install_pipstring""Requirement specs installed before the repo under test, under the channel constraints. Same name and meaning as on build-tests.yml.
install_extrasstringtestExtras used when installing the repo under test.
pytest_argsstring-v --tb=short -rxXAppended to the pytest invocation.
soft_failbooleanfalseReport but do not fail on test failures. GitHub forbids continue-on-error on a job that calls a reusable workflow, so advisory callers set this instead.
timeout_minutesnumber45Job timeout.

Steps

  1. Check out the calling repo at the PR commit.
  2. Fetch the channel constraints file live โ€” never vendored, so the gate moves when the distro moves.
  3. Remove the repo's own line from the constraints.
  4. uv pip install -c channel-constraints.txt .[extras], plus pytest, pytest-timeout, and setuptools<81 (channel-age OVOS packages still import pkg_resources).
  5. Upload the constraints file and a pip freeze as an artifact. A red run weeks later cannot be reproduced from the URL, because the URL has moved on.
  6. Run pytest with OVOS_CHANNEL set to the channel name.

Expect red at first

A channel is behind dev by construction, so the first run on a repo usually fails a pile of tests. That is the finding, not a broken workflow. Call it with soft_fail: true until the baseline is understood, then turn that off. (continue-on-error cannot be used here: GitHub rejects it on a job that calls a reusable workflow.)

ovos-test-harness carries the fully worked version of this: checked-in per-channel known-gap files under test/channel_gaps/, consumed via OVOS_CHANNEL, which strict-xfail exactly the known failures. Known gaps stay visible and green; new breakage is red; a gap the channel has since closed is also red, as the cue to delete the line.

Typical usage

name: Channel Compat
on:
  pull_request:
  workflow_dispatch:

jobs:
  channel-compat:
    strategy:
      fail-fast: false
      matrix:
        include:
          - channel: stable
            url: https://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-stable.txt
          - channel: testing
            url: https://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-testing.txt
    name: ${{ matrix.channel }}
    uses: OpenVoiceOS/gh-automations/.github/workflows/channel-compat.yml@dev
    with:
      channel_url: ${{ matrix.url }}
      channel_name: ${{ matrix.channel }}
      test_path: test/unittests/
      system_deps: swig libfann-dev
      soft_fail: true

Notes

  • UV_PRERELEASE: allow, like the other workflows. The channel caps every OVOS package, so allowing prereleases cannot let the install climb past the channel.
  • One channel per call. Matrix in the caller.

opm-check.yml

Runs OPM (OVOS Plugin Manager) plugin detection and validation on a single Python version. Verifies the plugin is discoverable after wheel install, and optionally after editable install (to catch entry-point registration issues). Posts a ๐Ÿ”Œ Plugin Detection section to the PR comment.

Source: .github/workflows/opm-check.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version to use (OPM detection does not vary by Python version)
system_depsstring""Extra apt packages to install before building (space-separated)
install_extrasstring""pip extras appended when installing the built package, e.g. dev
plugin_typestringautoPlugin type to detect: auto (reads from entry points), skill, tts, stt, wake_word, vad, phal, pipeline, utterance_transformer, tts_transformer, g2p
entry_pointstring""Legacy: a single entry point ID to verify. Prefer entry_points for multi-plugin packages, or leave empty for auto-detection
entry_pointsstring""JSON array of entry point IDs to verify (one OPM check per entry, results aggregated into the PR comment). Use for packages that ship multiple OPM plugins in one wheel
opm_require_foundbooleantrueFail the job if OPM cannot discover the plugin
opm_validate_interfacebooleantrueCheck that the plugin class inherits from the correct abstract base class
opm_test_importbooleantrueTest that the plugin class is importable and measure import time in ms
opm_perf_threshold_msnumber500Import time above this value (ms) is reported as an error
pr_commentbooleantruePost a ๐Ÿ”Œ Plugin Detection section to the OVOS PR Checks comment. Only fires on pull_request events.

Jobs

JobDescription
opm_checkInstalls ovos-plugin-manager, builds the wheel, installs it, then runs check_opm.py with --validate-interface/--test-import flags as configured. If the package is confirmed as an OVOS plugin, re-installs in editable mode and runs a detection-only check (no interface validation, no import test) to catch entry-point registration differences. Uploads opm_result.json and opm_result_editable.json as artifacts.
post_opm_reportDownloads the JSON artifacts, formats a PR comment section with status, plugin metadata, system deps, detected types, a validation table (wheel vs editable, import time, interface, config docs), and issues list. Also calls check_downstream.py to count dependents and appends the downstream impact note if count > 0.

PR comment content

The report is split into two tables:

OPM Detection โ€” one row per plugin type (e.g. skill, tts):

โœ… Plugin Status: PASS

Plugin Info:
- Name: ovos-tts-plugin-example
- Version: 1.2.3a4
- Description: Example TTS plugin for OVOS
- Requires Python: >=3.10

OPM Detection:

| Type | Wheel OPM | Editable OPM | Requires Python |
|------|-----------|--------------|-----------------|
| tts  | โœ…        | โœ…           | โœ… >=3.10       |

Entry Point Validation โ€” one row per named entry point (supports packages that register multiple entry points per type, e.g. a multi-voice TTS):

Entry Point Validation:

| Entry Point | Import | Interface | Config Docs |
|-------------|--------|-----------|-------------|
| ovos-tts-plugin-example | โœ… 42ms | โœ… | โœ… |
| ovos-tts-plugin-example-neural | โœ… 38ms | โœ… | โœ… |

๐Ÿ”— Downstream Impact: 3 package(s) depend on this plugin

Non-plugin repos: โ„น๏ธ Not an OVOS plugin โ€” OPM check skipped.

Typical usage

name: OPM Check
on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  opm_check:
    uses: OpenVoiceOS/gh-automations/.github/workflows/opm-check.yml@dev
    secrets: inherit
    with:
      plugin_type: auto
      opm_require_found: true
      opm_perf_threshold_ms: 500

Notes

  • opm_require_found: true (default) means the job fails if OPM cannot find the plugin. Set opm_require_found: false for repos that may not be OVOS plugins (e.g. utility libraries) where the check should pass silently.
  • The editable OPM check runs only when the wheel check confirms is_ovos_plugin: true in the JSON output, avoiding unnecessary editable install for non-plugin repos.
  • plugin_type: auto reads [project.entry-points."opm.*"] sections from pyproject.toml (or equivalent in setup.py) to detect all plugin types the package declares.
  • Entry point validation is keyed by ep_name (the entry point identifier), not by short_type. A package registering two TTS voices under different entry point names gets both validated independently.
  • requires-python from pyproject.toml is checked against the running Python version. A mismatch is reported as an error in the issues list.

ovoscope.yml

Runs ovoscope end-to-end skill tests on a single Python version. Installs the skill with its test extras (which must include ovoscope), executes pytest against the end-to-end test directory, and posts a ๐Ÿ”Œ Skill Tests (ovoscope) section to the OVOS PR Checks comment.

Source: .github/workflows/ovoscope.yml

Pipeline plugin strategy

PipelinePackageAlways available?
PADACIOSO_PIPELINEovos-workshop (bundled)โœ… Yes
ADAPT_PIPELINEovos-adapt-pipeline-pluginAdd to [test] deps
PADATIOUS_PIPELINEovos-padatious-pipeline-pluginAdd to [test] deps (requires swig)
M2V_PIPELINEovos-m2v-pipelineAdd to [test] deps

Tests that use a missing pipeline are skipped (via is_pipeline_available()). Use require_adapt/require_padatious/require_m2v to fail CI if those pipelines are absent instead of silently skipping.

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version to use
system_depsstring""Extra apt packages to install before testing (space-separated)
install_extrasstringtestpip extras used when installing the package. The extras must pull in ovoscope.
test_pathstringtest/end2end/Path passed to pytest โ€” should point at the end2end directory
require_adaptbooleanfalseFail CI if ovos-adapt-pipeline-plugin is not installed. When false, Adapt tests are skipped if the plugin is absent.
require_padatiousbooleanfalseFail CI if ovos-padatious-pipeline-plugin is not installed. When false, Padatious tests are skipped if absent (requires swig).
require_m2vbooleanfalseFail CI if ovos-m2v-pipeline is not installed. When false, M2V tests are skipped if absent.
pr_commentbooleantruePost a ๐Ÿ”Œ Skill Tests (ovoscope) section to the OVOS PR Checks comment. Only fires on pull_request events.

Jobs

JobDescription
ovoscopeInstalls system deps, installs the package with test extras plus ovoscope and pytest-json-report, runs a pipeline availability check (fails fast if require_* inputs are true and the plugin is absent), executes pytest with --json-report, formats the results, and posts the PR comment section.

Steps

StepDescription
CheckoutChecks out the calling repo
Checkout gh-automations scriptsChecks out OpenVoiceOS/gh-automations@dev into _gh_automations/ (PR events only)
Setup Pythonactions/setup-python@v5
Install System Dependenciesapt-get install the system_deps list (skipped if empty)
Install Package with Test Extrasuv pip install ".[test]" (or the configured extras) plus pytest pytest-json-report ovoscope
Check required pipeline availabilityInline Python reads opm.pipeline entry points and exits 1 if any require_* pipeline is absent
Run ovoscope testspytest --json-report with continue-on-error: true so the PR comment step always runs
Format ovoscope section for PR commentInline Python reads the JSON report and generates ovoscope-section.md grouped by test class
Post ovoscope section to PR commentCalls update_pr_comment.py with --section-id ovoscope
Fail job if tests failedRe-raises the pytest failure after the PR comment is posted

PR comment content

โœ… 9/9 passed

โœ… **TestConfuciusAdaptEN** โ€” 5/5
โœ… **TestConfuciusPadaciosaEN** โ€” 2/2
โœ… **TestConfuciusFixtures** โ€” 2/2

On failure, failing classes expand to a per-test table with longrepr for the first 3 failures.

Typical usage

name: Ovoscope End-to-End Tests
on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  ovoscope:
    uses: OpenVoiceOS/gh-automations/.github/workflows/ovoscope.yml@dev
    secrets: inherit
    with:
      test_path: "test/end2end/"
      require_adapt: true

To require Padatious (C extension โ€” add swig to system_deps):

    with:
      test_path: "test/end2end/"
      system_deps: "swig"
      require_adapt: true
      require_padatious: true

Notes

  • The require_* inputs trigger a pre-test pipeline availability check. If the required plugin is absent the job fails immediately with a clear error message, without running any tests.
  • The pipeline check reads the opm.pipeline entry point group using importlib.metadata โ€” no import of the plugin itself is required.
  • PADACIOSO_PIPELINE (pure Python padacioso) is always available via ovos-workshop; there is no require_padacioso input.
  • Set require_adapt: true in skill repos that test Adapt intents so CI fails explicitly if the Adapt plugin is missing from [test] deps rather than silently skipping those tests.

coverage.yml

Runs pytest --cov, generates a coverage report, posts it to the job summary, uploads the XML as an artifact, and (on pull requests) posts a ๐Ÿ“Š Coverage section in the shared OVOS PR Checks comment.

For deploying HTML coverage reports to GitHub Pages, see coverage-pages.yml.

Source: .github/workflows/coverage.yml

Design choices

  • No codecov bot, no external accounts, no CODECOV_TOKEN.
  • PR comment shows total coverage %, threshold pass/fail, and a collapsible table of under-covered files (files below 80%, or all files if โ‰ค 10). The coverage.xml artifact is available for deep inspection.
  • PR comment is a section in the shared OVOS PR Checks comment โ€” one comment per PR, not a separate coverage comment.
  • Job summary is always written (push, dispatch, and PR events alike).
  • Pages deployment is a separate workflow (coverage-pages.yml) to avoid requiring pages: write / id-token: write permissions from all callers โ€” only repos that opt in need those.

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version to run tests under
system_depsstring""Extra apt packages to install before testing (space-separated)
test_extrasstringdevPyproject extras key declaring test deps. Tried first via uv pip install -e .[<key>]
test_extras_fallbackstringtestExtras key tried if test_extras is not declared. Empty to skip
install_extrasstring""Extra uv install arguments run AFTER the package install (e.g. -r requirements/test.txt, a git URL override)
test_pathstringtest/Path passed to pytest
coverage_sourcestring.--cov=<value> โ€” set to your package directory (e.g. ovos_core) to measure only your own code
min_coveragenumber0Minimum total coverage %. Job fails if below threshold. 0 = disabled.
pr_commentbooleantruePost a ๐Ÿ“Š Coverage section to the shared OVOS PR Checks comment. Only fires on pull_request events.
artifact_namestringcoverage-reportName of the uploaded coverage XML artifact
artifact_retention_daysnumber14Days to retain the artifact

Jobs

Job stepDescription
Checkout + scripts checkoutChecks out the calling repo and (on PR events) the gh-automations scripts
Setup Python + Install DependenciesInstalls pytest, pytest-cov, coverage[toml], ovoscope, and the package itself
Run Tests with Coveragepytest --cov --cov-report=xml --cov-report=json --cov-report=html --cov-report=term-missing. continue-on-error: true so the PR comment posts even when tests fail.
Extract Coverage PercentageReads coverage.json for totals.percent_covered
Write Job SummaryCoverage table written to $GITHUB_STEP_SUMMARY
Format coverage sectionGenerates the PR comment content from coverage.json
Post coverage section to PR commentCalls scripts/update_pr_comment.py to find-or-create-and-update the OVOS PR Checks comment
Upload Coverage XML ArtifactUploads coverage.xml as a workflow artifact
Enforce Minimum Coverage ThresholdFails if min_coverage > 0 and total is below threshold
Fail job if tests failedRe-raises test failure after the PR comment has been posted

Typical usage

name: Coverage
on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

permissions:
  pull-requests: write
  contents: read

jobs:
  coverage:
    uses: OpenVoiceOS/gh-automations/.github/workflows/coverage.yml@dev
    secrets: inherit
    with:
      coverage_source: 'my_package'
      min_coverage: 80

Known issues

  • pr_comment only fires on pull_request events โ€” job summary is written for all events.
  • If all tests are skipped and coverage.xml is never generated, the PR comment will note that coverage data is unavailable rather than failing.

coverage-pages.yml

Runs pytest --cov and deploys the HTML coverage report to GitHub Pages. Designed to run on push to dev (not PRs), so the Pages site always reflects the latest merged code.

Source: .github/workflows/coverage-pages.yml

Design choices

  • Separated from coverage.yml because GitHub Pages deployment requires pages: write and id-token: write permissions. Including those in coverage.yml caused startup_failure in repos that don't enable Pages.
  • Callers must grant pages: write, id-token: write, and contents: read at their workflow level.
  • The repo must have GitHub Pages enabled with source set to GitHub Actions (not gh-pages branch).

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version to run tests under
system_depsstring""Extra apt packages to install before testing (space-separated)
install_extrasstring""Extra uv install arguments run before tests
test_pathstringtest/Path passed to pytest
coverage_sourcestring.--cov=<value> โ€” set to your package directory
gh_pages_subdirstringcoverageSub-directory within the Pages site, e.g. coverage โ†’ https://org.github.io/repo/coverage/. Empty string = deploy at root.

Jobs

Job stepDescription
CheckoutChecks out the calling repo
Setup Python + Install DependenciesInstalls pytest, pytest-cov, coverage[toml], ovoscope, and the package itself
Run Tests with Coveragepytest --cov --cov-report=html:htmlcov. continue-on-error: true so deployment proceeds even with test failures.
Prepare HTML reportCopies htmlcov/ to _pages_output/ (with optional subdirectory)
Upload Pages artifactactions/upload-pages-artifact@v3
Deploy to GitHub Pagesactions/deploy-pages@v4

Typical usage

name: Coverage Pages
on:
  push:
    branches: [dev]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  coverage_pages:
    uses: OpenVoiceOS/gh-automations/.github/workflows/coverage-pages.yml@dev
    secrets: inherit
    with:
      coverage_source: 'my_package'

Prerequisites

  1. Enable GitHub Pages in repo settings โ†’ Source: GitHub Actions
  2. Grant pages: write and id-token: write permissions in the calling workflow

license-check.yml

Checks all installed dependencies for licenses incompatible with the OVOS universal donor policy (Apache 2.0). Uses pilosus/action-pip-license-checker@v3. Also runs pip-licenses to generate a full per-package breakdown shown in a collapsible table in the PR comment.

Source: .github/workflows/license-check.yml

Universal Donor Policy

OVOS packages are Apache 2.0. To preserve this as a universal donor license:

CategoryExamplesDefault action
StrongCopyleftGPL v2, GPL v3Fail โ€” incompatible with Apache 2.0 distribution
NetworkCopyleftAGPLFail โ€” triggered by network use, not just distribution
WeakCopyleftLGPL, EUPLFail (conservative) โ€” safe as-a-library, but flag for review
OtherEULA, customFail โ€” unknown terms = unknown risk
Errornot foundFail โ€” can't audit what you can't see
MPL (Mozilla Public License)MPL-2.0Allowed โ€” file-level copyleft, safe as Apache 2.0 library user

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.14Python version
install_extrasstring""pip extras to install alongside the package, e.g. [extras,linux]
system_depsstring""Extra apt-get packages beyond the base python3-dev libssl-dev
exclude_packagesstring""PCRE regex of package names to exclude from the check
exclude_licensesstring^Mozilla Public License.*PCRE regex of license identifiers to exclude. Default allows MPL.
fail_licensesstringStrongCopyleft,NetworkCopyleft,WeakCopyleft,Other,ErrorComma-separated license categories that cause failure. See policy table above.
warn_onlybooleanfalseWhen true, report violations in the PR comment but do NOT fail the job. Useful for repos in transition.
pr_commentbooleantruePost a โš–๏ธ License Check section to the shared OVOS PR Checks comment. Only fires on pull_request events.

PR comment content

The comment includes:

  • Status header (pass/fail + package count)
  • Violations report (if any) in a code block
  • License distribution summary (e.g. 42ร— MIT, 18ร— Apache Software License, ...)
  • Full per-package breakdown in a collapsible <details> table with columns: Package, Version, License, URL. Packages with violations are flagged with โš ๏ธ.
  • Policy footnote

Typical usage

name: Run License Tests
on:
  push:
    branches: [master]
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  license_tests:
    uses: OpenVoiceOS/gh-automations/.github/workflows/license-check.yml@dev
    with:
      install_extras: '[extras]'
      system_deps: 'swig libfann-dev'
      exclude_packages: '^(tqdm|some-gpl-package).*'

pip-audit.yml

Scans installed dependencies for known CVEs using pypa/gh-action-pip-audit. Optionally uploads a SARIF report to GitHub's Security tab.

Source: .github/workflows/pip-audit.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.14Python version to audit against
install_extrasstring""pip extras to install
system_depsstring""Extra apt-get packages beyond python3-dev
ignore_vulnsstringGHSA-r9hx-vwmv-q579Newline-separated GHSA IDs to ignore. Default ignores GHSA-r9hx-vwmv-q579 (setuptools path traversal โ€” dev-only, not exploitable at OVOS runtime).
warn_onlybooleanfalseWhen true, report vulnerabilities in the PR comment but do NOT fail the job. Useful for repos that want visibility without blocking merges.
pr_commentbooleantruePost a ๐Ÿ”’ Security (pip-audit) section to the shared OVOS PR Checks comment. Only fires on pull_request events.
upload_sarifbooleantrueUpload a SARIF report to GitHub's Security tab (Code scanning alerts). Requires the repo to have GitHub Advanced Security enabled, or be public. Uses github/codeql-action/upload-sarif@v3. continue-on-error: true so the job does not fail for private repos without GHAS.

Typical usage

name: Pip Audit
on:
  push:
    branches: [dev, master]
  workflow_dispatch:

jobs:
  pip_audit:
    uses: OpenVoiceOS/gh-automations/.github/workflows/pip-audit.yml@dev
    with:
      install_extras: '[all]'

release-preview.yml

Reads version.py, predicts the next version from PR labels and/or title using conventional commit prefixes, and posts a ๐Ÿท๏ธ Release Preview section to the OVOS PR Checks comment. Also performs a channel compatibility check when a package name is resolvable.

Source: .github/workflows/release-preview.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.14Python version
package_namestring""Package name for the channel compatibility check. If empty, auto-reads from pyproject.toml/setup.py.
version_filestring""Path to version.py (relative to repo root). If empty, auto-detects.
pr_commentbooleantruePost ๐Ÿท๏ธ Release Preview section to OVOS PR Checks comment

Permissions

pull-requests: write, contents: read

Steps

StepDescription
Checkout + scripts checkoutChecks out the calling repo and (on PR events) the gh-automations scripts
Setup Pythonactions/setup-python@v5
Run release checkcheck_release.py --version-file โ€ฆ --output-json /tmp/release-report.json. Env vars: PR_LABELS_JSON, PR_TITLE. continue-on-error: true.
Format release sectionInline Python reads release-report.json โ†’ release-section.md
Post release section to PR commentCalls update_pr_comment.py with --section-id release
Fail job if release check failedRe-raises only for malformed version.py (parse error)

Bump detection rules

Labels take precedence over PR title. Priority: major > minor > build.

LabelBump
breaking, breaking changemajor
feature, enhancementminor
fix, bug, bugfixbuild
PR title prefixBump
breaking change:, feat!:, fix!:major
feat:, feature:minor
fix:build
docs:, chore:, refactor:, test:, style:, perf:, ci:, build:alpha only
(no prefix)alpha only

PR comment content (with label)

**Current:** `1.2.3a4` โ†’ **Next:** `1.3.0a1`

| Signal | Value |
|--------|-------|
| Label | `feature` |
| PR title | `feat: add multi-language support` |
| Bump | minor |

โœ… PR title follows conventional commit format.

PR comment content (no label, no prefix)

**Current:** `1.2.3a4` โ†’ **Next:** `1.2.3a5`

| Signal | Value |
|--------|-------|
| Label | _(none)_ |
| PR title | `update readme` |
| Bump | alpha |

โš ๏ธ No conventional commit prefix โ€” alpha-only bump.
Suggested: `fix: update the thing` or `feat: update the thing`

No version.py found: โ„น๏ธ No version.py found โ€” release preview not available.

Typical usage

name: Release Preview

on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  release_preview:
    uses: OpenVoiceOS/gh-automations/.github/workflows/release-preview.yml@dev
    secrets: inherit

repo-health.yml

Checks that a repo contains the required files (README, LICENSE, pyproject.toml/setup.py, version.py with valid block markers) and greets first-time contributors.

Source: .github/workflows/repo-health.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
version_filestring""Path to version.py (relative to repo root). If empty, auto-detects root or pkg/version.py.
pr_commentbooleantruePost a ๐Ÿ“‹ Repo Health section to the OVOS PR Checks comment. Only fires on pull_request events.

PR comment content

  • Current version from version.py
  • Per-file status: โœ… present / โŒ required and missing / โš ๏ธ optional and missing
  • Version block marker validation (START/END_VERSION_BLOCK)
  • First-time contributor greeting (separate ๐Ÿ‘‹ Welcome section posted in the same PR comment when author_association is FIRST_TIME_CONTRIBUTOR or FIRST_TIMER)

Typical usage

name: Repo Health
on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  repo_health:
    uses: OpenVoiceOS/gh-automations/.github/workflows/repo-health.yml@dev
    secrets: inherit
    with:
      version_file: 'my_package/version.py'

skill-check.yml

Analyses an OVOS skill repository for locale structure, language coverage, and skill.json validity. Silently passes for non-skill repos by default.

Source: .github/workflows/skill-check.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version
locale_dirstring""Locale root path. Empty = auto-detect.
skip_if_not_skillbooleantrueSilently pass if no ovos.plugin.skill entry point found
fail_on_missing_en_usbooleantrueFail if en-us locale directory is absent
fail_on_invalid_skill_jsonbooleanfalseFail if en-us skill.json is invalid or missing required fields
pr_commentbooleantruePost ๐ŸŽ™๏ธ Skill section to OVOS PR Checks comment

Permissions

pull-requests: write, contents: read

Steps

Follows the canonical 3-phase pattern (continue-on-error โ†’ format โ†’ post โ†’ re-raise):

StepDescription
Checkout + scripts checkoutChecks out the calling repo and (on PR events) the gh-automations scripts
Setup Pythonactions/setup-python@v5
Run skill checkcheck_skill.py --repo-root . --locale-dir โ€ฆ --output-json /tmp/skill-report.json. continue-on-error: true.
Format skill sectionInline Python reads skill-report.json โ†’ skill-section.md
Post skill section to PR commentCalls update_pr_comment.py with --section-id skill
Skip if not an OVOS skill repoExits 0 if is_skill: false and skip_if_not_skill: true
Fail if en-us locale is missingExits 1 if has_en_us: false and fail_on_missing_en_us: true
Fail if skill.json is invalidExits 1 if JSON malformed or required fields missing and fail_on_invalid_skill_json: true
Fail job if skill check failedRe-raises error after comment is posted

PR comment content

๐ŸŽ™๏ธ **ovos-skill-hello-world.openvoiceos** โ€” 14 languages

**en-us:** 2 intents ยท 4 dialogs ยท skill.json โœ…

<details><summary>Translation coverage (13 languages)</summary>

| Language | Coverage |
|----------|----------|
| ca-es | โœ… 100% (6/6) |
| de-de | โš ๏ธ 83.3% (5/6) |

</details>

**Gitlocalize:** โœ… sync script ยท โœ… translations/ ยท โœ… sync workflow

Coverage icons: โœ… โ‰ฅ95% ยท โš ๏ธ 50โ€“94% ยท โŒ <50%. Non-skill repos: โ„น๏ธ Not an OVOS skill repo โ€” check skipped.

Typical usage

name: Skill Check

on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  skill_check:
    uses: OpenVoiceOS/gh-automations/.github/workflows/skill-check.yml@dev
    secrets: inherit

locale-check.yml

Verifies that locale folders are correctly included in the package build. Checks both pyproject.toml configuration ([tool.setuptools.package-data]) and the build manifest (SOURCES.txt).

Source: .github/workflows/locale-check.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.11Python version
locale_pathstring""Override locale path (relative to repo root). Empty = auto-detect.
pr_commentbooleantruePost ๐ŸŒ Locale Build section to OVOS PR Checks comment

Permissions

pull-requests: write, contents: read

Steps

Follows the canonical 3-phase pattern (continue-on-error โ†’ format โ†’ post โ†’ re-raise):

StepDescription
Checkout + scripts checkoutChecks out the calling repo and (on PR events) the gh-automations scripts
Setup Pythonactions/setup-python@v5
Install build dependenciesuv pip install build setuptools
Run build to generate SOURCES.txtuv build --no-isolation. continue-on-error: true.
Run locale build checkcheck_locale_build.py --repo-root . --locale-path โ€ฆ --output-json /tmp/locale-report.json --verbose. continue-on-error: true.
Format locale sectionInline Python reads locale-report.json โ†’ locale-section.md
Post locale section to PR commentCalls update_pr_comment.py with --section-id locale

PR comment content

๐ŸŒ Locale Build

โœ… Locale properly configured (95 files, 9 languages)

**Locale directories found:**
- `ovos_skill_ggwave/locale`

**Localization coverage:**
- `ovos_skill_ggwave/locale`: 95 files in 9 languages (en-us, pt-br, es-es, de-de, fr-fr...)

**pyproject.toml:** โœ… `[tool.setuptools.package-data.ovos_skill_ggwave]` includes locale
  - `locale/*/*.voc`
  - `locale/*/*.dialog`
  - `locale/*/*.entity`
  - `locale/*/*.intent`
  - `locale/*/*.json`

**Build manifest:** โœ… 95 locale files included in package

Status logic

ConditionStatusSummary
No locale folder foundpassโ„น๏ธ No locale folder found โ€” localization not used
Locale found, not in pyproject.tomlfailโŒ Locale folder not properly configured for packaging
Locale in pyproject.toml, not in buildwarningโš ๏ธ Locale configured but build verification pending
Locale in bothpassโœ… Locale properly configured (X files, Y languages)

Typical usage

name: Locale Build Check

on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  locale_check:
    uses: OpenVoiceOS/gh-automations/.github/workflows/locale-check.yml@dev
    secrets: inherit

When to use

Add this workflow to any OVOS repo that includes locale files:

  • Skills: Use skill-check.yml (includes locale coverage analysis) โ€” no need for both
  • Core/Plugins: Use locale-check.yml to verify locale files are packaged correctly
  • Libraries without locale: No workflow needed

Script API

The underlying check_locale_build.py script can also be run standalone:

python scripts/check_locale_build.py \
  --repo-root . \
  --locale-path "" \
  --output-json /tmp/locale-report.json \
  --verbose

Exit code is always 0; check the JSON status field (pass/warning/fail) for programmatic use.


downstream-check.yml

Reports which packages in the ovos-releases alpha constraints depend on a given package. Uses pipdeptree and commits the sorted report to the repo, so repeated runs only generate a new commit when the actual dependency tree changes.

Source: .github/workflows/downstream-check.yml

Inputs

InputTypeDefaultDescription
package_namestring(required)PyPI package name to track (e.g. ovos-utils)
constraints_urlstringhttps://raw.githubusercontent.com/OpenVoiceOS/ovos-releases/refs/heads/main/constraints-alpha.txtConstraints file URL to install from
output_filestringdownstream_report.txtReport output path (relative to repo root)
commit_branchstringdevBranch to commit the report to
python_versionstring3.11Python version
runnerstringubuntu-latestRunner label

Typical usage

name: Track Downstream Dependencies
on:
  push:
    branches: [dev]
  schedule:
    - cron: "0 0 * * *"
  workflow_dispatch:

jobs:
  check_downstream:
    uses: OpenVoiceOS/gh-automations/.github/workflows/downstream-check.yml@dev
    secrets: inherit
    with:
      package_name: 'ovos-utils'

python-support.yml (legacy)

Runs an install matrix across Python versions and install modes (regular + editable). Optionally checks OPM detection using a legacy entry_point ID. Posts a ๐Ÿ Python Support section to the PR comment.

Source: .github/workflows/python-support.yml

Legacy status: Most repos now use build-tests.yml which provides the same build/install matrix without the editable-mode complexity, plus pytest integration. For OPM detection, use opm-check.yml. Retain python-support.yml only for repos that specifically need editable-mode compatibility testing.

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
package_namestring""Package name for the channel compatibility check
python_versionsstring'["3.10", "3.11", "3.12", "3.13", "3.14"]'JSON array of Python versions
version_filestring""Path to version.py for the channel compatibility check
install_modesstring'["regular", "editable"]'JSON array of install modes
install_extrasstring""pip extras to install
system_depsstring""Extra apt packages beyond python3-dev libssl-dev
entry_pointstring""Legacy OPM entry point ID to verify (only used when set)
pr_commentbooleantruePost ๐Ÿ Python Support section to OVOS PR Checks comment

spec-lint.yml

Runs ovos-spec-lint against a skill's locale/ folder to validate template syntax (OVOS-INTENT-1) and file naming/layout (OVOS-INTENT-2). Posts a ๐Ÿงช Spec Lint section to the shared OVOS PR Checks comment.

Source: .github/workflows/spec-lint.yml

Inputs

InputTypeDefaultDescription
runnerstringubuntu-latestRunner label
python_versionstring3.14Python version
locale_pathstringlocalePath to the locale folder (or a single <lang>/ directory)
spec_versionstring""OVOS spec version to target (0/1/2/3). Empty = linter default
strictbooleanfalseTreat warnings as errors (--strict)
skip_if_no_localebooleantrueSilently pass if locale_path does not exist
ovos_spec_tools_specstringovos-spec-toolsPip requirement spec (pin a version or use a git URL)
pr_commentbooleantruePost the section to the PR comment (PR events only)

Typical usage

name: Spec Lint
on:
  pull_request:
    branches: [dev]
  workflow_dispatch:

jobs:
  spec_lint:
    uses: OpenVoiceOS/gh-automations/.github/workflows/spec-lint.yml@dev
    secrets: inherit

The job exits non-zero only when ovos-spec-lint reports errors. Use strict: true to also fail on warnings. Use skip_if_no_locale: false to fail loudly when the folder is missing.


notify-matrix.yml

Sends a message to the OVOS Matrix channel. Uses fadenb/matrix-chat-message.

Source: .github/workflows/notify-matrix.yml

Inputs

InputTypeDefaultDescription
messagestring(required)Message text to send
homeserverstringmatrix.orgMatrix homeserver URL
channelstring!WjxEKjjINpyBRPFgxl:krbel.duckdns.orgMatrix room ID

Secrets

SecretDescription
MATRIX_TOKENMatrix access token (inherited via secrets: inherit)

Typical usage

  notify:
    if: github.event.pull_request.merged == true
    needs: publish_alpha
    uses: OpenVoiceOS/gh-automations/.github/workflows/notify-matrix.yml@dev
    secrets: inherit
    with:
      message: "new ${{ github.event.repository.name }} PR merged! https://github.com/${{ github.repository }}/pull/${{ github.event.number }}"

PR Checks Comment Pattern

The following workflows post their results as named sections in a single shared PR comment rather than separate bot comments:

WorkflowSection IDSection title
repo-health.ymlhealth๐Ÿ“‹ Repo Health
repo-health.ymlwelcome๐Ÿ‘‹ Welcome (first-time contributors only)
release-preview.ymlrelease๐Ÿท๏ธ Release Preview
pip-audit.ymlsecurity๐Ÿ”’ Security (pip-audit)
license-check.ymllicenseโš–๏ธ License Check
python-support.ymlpython_support๐Ÿ Python Support
build-tests.ymlbuild๐Ÿ”จ Build Tests
opm-check.ymlopm๐Ÿ”Œ Plugin Detection
coverage.ymlcoverage๐Ÿ“Š Coverage
skill-check.ymlskill๐ŸŽ™๏ธ Skill

The comment is identified by the HTML marker <!-- ovos-pr-checks --> in its body. Each workflow manages its own section:

<!-- ovos-pr-checks -->
## OVOS PR Checks

<!-- section:health -->
### ๐Ÿ“‹ Repo Health
โœ… All required files present.
...
<!-- /section:health -->

<!-- section:build -->
### ๐Ÿ”จ Build Tests
โœ… All versions pass
...
<!-- /section:build -->

<!-- section:opm -->
### ๐Ÿ”Œ Plugin Detection
โœ… Plugin Status: PASS
...
<!-- /section:opm -->

<!-- section:ovoscope -->
### ๐Ÿ”Œ Skill Tests (ovoscope)
โœ… 9/9 passed
...
<!-- /section:ovoscope -->

<!-- section:coverage -->
### ๐Ÿ“Š Coverage
โœ… **87.3%** total coverage
...
<!-- /section:coverage -->

<!-- section:license -->
### โš–๏ธ License Check
โœ… No license violations found (42 packages).
...
<!-- /section:license -->

<!-- section:security -->
### ๐Ÿ”’ Security (pip-audit)
โœ… No known vulnerabilities found.
...
<!-- /section:security -->

<!-- section:release -->
### ๐Ÿท๏ธ Release Preview
**Current:** `0.0.1a4` โ†’ **Next:** `0.0.2a1`
...
<!-- /section:release -->

Sections appear as each workflow completes. Reruns update only the relevant section without touching others.

The aggregation logic lives in scripts/update_pr_comment.py โ€” see the Scripts Reference below.

Adding a new section

To add a new check type to the aggregated comment from any workflow:

  1. Generate the section content as a markdown file (e.g. /tmp/my-section.md).
  2. Check out gh-automations and call the script:
- name: Checkout gh-automations scripts
  if: github.event_name == 'pull_request'
  uses: actions/checkout@v4
  with:
    repository: OpenVoiceOS/gh-automations
    ref: dev
    path: _gh_automations/

- name: Post section to PR comment
  if: github.event_name == 'pull_request'
  run: |
    python3 _gh_automations/scripts/update_pr_comment.py \
      --repo "${{ github.repository }}" \
      --pr "${{ github.event.pull_request.number }}" \
      --section-id "my-check" \
      --title "๐Ÿ” My Check" \
      --content-file /tmp/my-section.md
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The permissions: pull-requests: write must be declared on the calling job.


Scripts Reference

The following Python scripts are checked out from this repo at workflow run time and are not installed as a Python package.

scripts/_version_utils.py

Shared version-block parsing utilities imported by all other scripts.

Key functions:

  • read_version(version_file: str) -> tuple[int, int, int, int] โ€” scripts/_version_utils.py:18 โ€” parses START_VERSION_BLOCK/END_VERSION_BLOCK, returns (major, minor, build, alpha)
  • format_version(major, minor, build, alpha) -> str โ€” scripts/_version_utils.py:51 โ€” formats PEP 440 string
  • write_version_block(version_file, major, minor, build, alpha) โ€” scripts/_version_utils.py:70 โ€” rewrites only the block, preserving all surrounding content

scripts/update_version.py

Bumps the version in a version.py file.

Key function: update_version(part: str, version_file: str) -> str โ€” scripts/update_version.py:18

usage: update_version.py <part> --version-file <path>

part: major | minor | build | alpha

Bump rules (implemented at scripts/update_version.py:37-52):

PartEffect
majorMAJOR += 1, MINOR = 0, BUILD = 0, ALPHA = 1
minorMINOR += 1, BUILD = 0, ALPHA = 1
buildBUILD += 1, ALPHA = 1
alphaALPHA += 1; if currently stable (ALPHA == 0): BUILD += 1 first

scripts/remove_alpha.py

Sets VERSION_ALPHA = 0 in a version.py file (declares stable).

Key function: update_alpha(version_file: str) โ€” scripts/remove_alpha.py:10

usage: remove_alpha.py --version-file <path>

scripts/get_version.py

Reads and prints the version string from a version.py file. Works without installing the package.

Key function: get_version(version_file: str) -> str โ€” scripts/get_version.py:5

usage: get_version.py --version-file <path>

Output example: 1.2.3a4 or 1.2.3

scripts/check_downstream.py

Reports which installed packages depend on a given package, using pipdeptree. Output is sorted deterministically so repeated runs only generate a git commit when the actual dependency tree changes.

Key function: get_downstream(package_name: str) -> str โ€” scripts/check_downstream.py:61

Helper: sort_pipdeptree_output(text: str) -> str โ€” scripts/check_downstream.py:53

usage: check_downstream.py --package <name> --output <file>

Requires pipdeptree to be installed in the environment before calling.

scripts/check_opm.py

Detects and validates OVOS plugins via OPM. Supports multi-plugin-type repos. Outputs a structured JSON report.

Key functions:

  • auto_detect_plugin_types() โ€” scripts/check_opm.py:308 โ€” scans [project.entry-points."opm.*"] in pyproject.toml or setup.py
  • validate_plugin_import(module_path, class_name) โ€” scripts/check_opm.py:132 โ€” imports the class, measures time in ms, detects missing dependencies
  • check_plugin_interface(plugin_cls, short_type) โ€” scripts/check_opm.py:152 โ€” verifies issubclass() against the correct abstract base (10 types including g2p)
  • extract_metadata() โ€” scripts/check_opm.py:54 โ€” reads name, version, authors, description, homepage, requires_python
  • extract_system_deps() โ€” scripts/check_opm.py:108 โ€” reads [tool.ovos.build] system-dependencies
  • validate_config_docs(repo_root) โ€” scripts/check_opm.py:176 โ€” searches for settingsmeta.json
  • collect_issues(result) โ€” scripts/check_opm.py:217 โ€” aggregates issues list
  • compute_status(issues) โ€” scripts/check_opm.py:292 โ€” returns pass, warning, or fail
  • check_opm(plugin_type, entry_point, output_json, ...) โ€” scripts/check_opm.py:406 โ€” main entry point
usage: check_opm.py \
    [--plugin-type auto|skill|tts|stt|wake_word|vad|phal|pipeline|utterance_transformer|tts_transformer|g2p] \
    [--entry-point <id>] \
    [--output-json <path>] \
    [--validate-interface | --no-validate-interface] \
    [--test-import | --no-test-import] \
    [--perf-threshold-ms <ms>]

scripts/check_skill.py

Analyses a checked-out OVOS skill repository. Outputs a JSON report. Stdlib only.

Key functions:

  • is_skill_repo(repo_root) โ€” scripts/check_skill.py:39
  • find_locale_dir(repo_root, override="") โ€” scripts/check_skill.py:52
  • check_translation_completeness(locale_dir, en_us_files) โ€” scripts/check_skill.py:157
  • run_checks(repo_root, locale_dir_override="") โ€” scripts/check_skill.py:220
usage: check_skill.py [--repo-root .] [--locale-dir ""] [--output-json /tmp/skill-report.json]

scripts/check_release.py

Reads version.py, predicts next version from PR labels/title. Stdlib only.

Key functions:

  • detect_bump_part(labels, pr_title) โ€” scripts/check_release.py:74
  • compute_next_version(major, minor, build, alpha, part) โ€” scripts/check_release.py:120
  • run_checks(version_file, pr_labels_json, pr_title) โ€” scripts/check_release.py:196
usage: check_release.py --version-file version.py \
    [--pr-labels-json "[]"] \
    [--pr-title ""] \
    [--output-json /tmp/release-report.json]

env vars (override CLI): PR_LABELS_JSON, PR_TITLE

scripts/update_pr_comment.py

Manages the shared OVOS PR Checks comment on a pull request. Finds the comment by the invisible HTML marker <!-- ovos-pr-checks -->, then replaces or appends the named section. Creates the comment if it doesn't exist yet.

Uses only Python stdlib (urllib, json, re) โ€” no extra dependencies.

Key logic:

  • find_ovos_comment(repo, pr_number) โ€” paginates the PR comments API to find the marker โ€” scripts/update_pr_comment.py:56
  • insert_or_replace_section(body, section_id, title, content) โ€” regex replace within <!-- section:X --> โ€ฆ <!-- /section:X --> delimiters โ€” scripts/update_pr_comment.py:81
usage: update_pr_comment.py \
    --repo owner/repo \
    --pr 123 \
    --section-id coverage \
    --title "๐Ÿ“Š Coverage" \
    --content-file /tmp/section.md

environment: GITHUB_TOKEN   (required)