Reusable Workflow Reference: PR Comment Pattern and Scripts¶
In a nutshell
This page documents machinery shared across every reusable workflow: the pattern that lets many workflows post their result as a named section in a single shared PR comment, and the Python scripts each workflow checks out at run time to do its work. For the coverage and security workflows, see Coverage and Security Workflows. For lint, type-check, docs, and notification workflows, see Lint and Docs Workflows. Start with the gh-automations overview for the big picture, or the full Workflow Reference index.
The bot prefixes every PR-comment section title with an emoji tag. This page names those titles in backticks without the emoji, except in the one raw example under PR Checks Comment Pattern.
PR Checks Comment Pattern¶
The following workflows post their results as named sections in a single shared PR comment rather than separate bot comments. The bot prefixes every section title with an emoji tag. The tables below give the title text and name that tag in words.
| Workflow | Section title | Tag emoji |
|---|---|---|
repo-health.yml |
Repo Health (+ Welcome for first-time contributors) |
clipboard (+ waving hand) |
release-preview.yml |
Release Preview |
label |
pip-audit.yml |
Security (pip-audit) |
lock |
license-check.yml |
License Check |
balance scale |
build-tests.yml |
Build Tests |
hammer |
opm-check.yml |
Plugin Detection |
plug |
| Workflow | Section title | Tag emoji |
|---|---|---|
ovoscope.yml |
Skill Tests (ovoscope) (+ Bus Coverage) |
plug (+ bus) |
intent-case-tests.yml |
Intent-Case Accuracy |
target |
tts-intelligibility.yml |
TTS Intelligibility |
speaking head |
coverage.yml |
Coverage |
bar chart |
skill-check.yml |
Skill |
microphone |
type-check.yml |
Type Check |
magnifying glass |
docs-check.yml |
Docs |
books |
lint.yml |
lint results | none |
python-support.yml (deprecated) |
Python Support |
snake |
The HTML marker <!-- ovos-pr-checks --> identifies the comment 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:
-
Generate the section content as a markdown file (e.g.
/tmp/my-section.md). -
Check out gh-automations and call the script:
- name: Checkout gh-automations scripts
if: github.event_name == 'pull_request'
uses: actions/checkout@v7
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], parsesSTART_VERSION_BLOCK/END_VERSION_BLOCK, returns(major, minor, build, alpha) -
format_version(major, minor, build, alpha) -> str, formats PEP 440 string -
write_version_block(version_file, major, minor, build, alpha), 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
Bump rules:
| Part | Effect |
|---|---|
major |
MAJOR += 1, MINOR = 0, BUILD = 0, ALPHA = 1 |
minor |
MINOR += 1, BUILD = 0, ALPHA = 1 |
build |
BUILD += 1, ALPHA = 1 |
| Part | Effect |
|---|---|
alpha |
ALPHA += 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/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
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 and uploaded as a workflow artifact. The older commit-to-repo behavior is gone. The commit_branch input is deprecated and ignored.
Key function: get_downstream(package_name: str) -> str
Helper: sort_pipdeptree_output(text: str) -> str
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(), scans[project.entry-points."opm.*"]inpyproject.tomlorsetup.py -
validate_plugin_import(module_path, class_name), imports the class, measures time in ms, detects missing dependencies -
check_plugin_interface(plugin_cls, short_type), verifiesissubclass()against the correct abstract base (~30 types, including theagents.*family,vc, andwake_word.verifier) -
extract_metadata(), reads name, version, authors, description, homepage, requires_python -
extract_system_deps(), reads[tool.ovos.build] system-dependencies -
validate_config_docs(repo_root), searches forsettingsmeta.json -
collect_issues(result), aggregates issues list -
compute_status(issues), returnspass,warning, orfail -
check_opm(plugin_type, entry_point, output_json, ...), 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) -
find_locale_dir(repo_root, override="") -
check_translation_completeness(locale_dir, en_us_files) -
run_checks(repo_root, locale_dir_override="")
scripts/check_release.py¶
Reads version.py, predicts next version from PR labels/title. Stdlib only.
Key functions:
-
detect_bump_part(labels, pr_title) -
compute_next_version(major, minor, build, alpha, part) -
run_checks(version_file, pr_labels_json, pr_title)
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_comments(repo, pr_number), paginates the PR comments API and returns the list of comments carrying the marker -
insert_or_replace_section(body, section_id, title, content), regex replace within<!-- section:X --> โฆ <!-- /section:X -->delimiters
usage: update_pr_comment.py \
--repo owner/repo \
--pr 123 \
--section-id coverage \
--title "๐ Coverage" \
--content-file /tmp/section.md
environment: GITHUB_TOKEN (required)
Source code: OpenVoiceOS/gh-automations.
Read next: gh-automations Quality Workflows Related: gh-automations Overview ยท Coverage and Security Workflows ยท Lint and Docs Workflows ยท Workflow Reference