| name | ov-github-actions-ci |
| description | Author, edit, debug, and review the OpenVINO GitHub Actions CI infrastructure — regular (non-agentic) workflows under .github/workflows, reusable job_*.yml workflows, custom composite actions under .github/actions, CI helper scripts under .github/scripts, Dockerfiles under .github/dockerfiles, and the Smart CI / labeler / components configuration. Use when a user wants to add or change a build/test job or step, create or modify a reusable workflow, write or fix a custom action, adjust runners/containers/caches, wire up Smart CI for a component, pin action versions, fix workflow permissions/security, or debug a failing CI workflow's YAML. Do NOT use for gh-aw agentic workflows (*.md with gh-aw frontmatter — use ov-agentic-workflows), for diagnosing a specific product/test failure's root cause in C++/Python code, or for non-CI GitHub configuration. |
Work on OpenVINO GitHub Actions CI
Guides changes to the regular GitHub Actions CI in this repository: validation and reusable
workflows, custom actions, CI scripts, Dockerfiles, and the Smart CI configuration that drives them.
Read the CI developer docs under docs/dev/ci/github_actions
first — they are the authoritative, repo-specific reference and this skill is a checklist on top of
them. Keep them in sync when behavior changes (see Skill self-improvement). The key pages:
For framework syntax, use the official GitHub Actions documentation. If user requires an out-of-scope feature, consult the official documentation.
Out of scope: gh-aw agentic workflows (.github/workflows/*.md + *.lock.yml, e.g. ci-doctor)
— use the ov-agentic-workflows skill instead.
Repository layout
- Workflows —
.github/workflows/
- Validation workflows: named after the OS/config, e.g.
ubuntu_22.yml,
windows_vs2022_release.yml, mac_arm64.yml, linux_arm64.yml, android.yml. Entry points with
on: triggers; they wire together Build + test jobs.
- Reusable workflows:
job_*.yml (e.g. job_python_unit_tests.yml, job_cxx_unit_tests.yml).
Called via uses: ./.github/workflows/job_*.yml with on: workflow_call: inputs. Not triggered
directly.
- Custom actions —
.github/actions/ (composite action.yml): setup_python, system_info,
smart-ci, handle_docker, openvino_provider, store_artifacts/restore_artifacts, cache, etc.
- CI scripts —
.github/scripts/ (Python helpers: workflow_rerun/, external_pr_labeller.py,
check_copyright.py, ...).
- Dockerfiles —
.github/dockerfiles/ov_build/** and ov_test/**, plus the docker_tag file.
- Smart CI config —
.github/labeler.yml (path globs → component/label) and
.github/components.yml (component dependency graph).
- Scans/meta —
workflows_scans.yml (CodeQL actions + semgrep on workflow changes),
dependency_review.yml.
Golden rules
- Pin third-party actions to a full commit SHA, with the version in a trailing comment. First-party
./.github/actions/* are referenced by path, not pinned.
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- Least-privilege
permissions:. Start from permissions: read-all (workflow level) and grant the
minimum extra scope at the job level only where needed. Never widen without a reason.
- Follow security.md — it is the source of truth.
Key rules: never use
pull_request_target, never hard-code secrets, and treat all github.* /
github.event.* user-controlled values as untrusted (route them through env: or action inputs, never
interpolate directly into run: shell). Ping the CI task force for anything involving secrets or
elevated triggers.
- Put shared job logic in a reusable
job_*.yml, not copy-paste. If the same job appears in more
than one validation workflow, it should be a workflow_call reusable workflow parameterized by
runner, image/container, and affected-components.
- Respect Smart CI. Test jobs/steps that validate a specific component must gate on
fromJSON(needs.smart_ci.outputs.affected_components).<COMPONENT>.{build,test} and list Smart_CI in
needs. Do not make an expensive job run unconditionally.
- Custom Docker images come from
handle_docker, not hard-coded tags. Reference build/test images
as ${{ fromJSON(needs.docker.outputs.images).ov_build.<name> }} and add Docker to needs. Plain
passthrough images must use the ACR mirror openvinogithubactions.azurecr.io/..., never docker.io.
- Keep changes scoped and validate before finishing (see Validation).
.github/** is owned by
@openvinotoolkit/openvino-ci-maintainers and changes there are label category: CI
(workflows also get github_actions).
Key conventions
- Runner selection (
runs-on) — self-hosted Azure pools aks-{os}-{cores}-cores-{ram}gb[-arm] for
heavy build/test; GitHub-hosted (ubuntu-22.04, ...) for light jobs (labelers, style). GPU jobs use
[ self-hosted, gpu|igpu|dgpu ] and must run in Docker. Azure aks-* runners are required to
pull from the ACR / use custom images. Match cores to parallelism (see runners.md).
- Containers — most self-hosted jobs run in a
container:. Mount the shared drive with
volumes: [ /mount:/mount ] and add ${{ github.workspace }}:${{ github.workspace }} where the
workspace must be identical inside/outside the container.
- Caches — GHA cache (
actions/cache, ≤10 GB) for small deps; shared drive (/mount/..., e.g.
PIP_CACHE_PATH: /mount/caches/pip/linux) for large assets on Linux self-hosted; sccache → Azure
Blob for C/C++ build cache (needs SCCACHE_AZURE_* env + CMAKE_*_COMPILER_LAUNCHER: sccache +
SCCACHE_AZURE_KEY_PREFIX).
- Artifacts —
Build job packs and uploads; test jobs needs: Build and download. Follow the
existing store_artifacts/restore_artifacts actions and artifact-name conventions in the workflow.
timeout-minutes — always set a sensible per-job timeout.
Common tasks
Add a test (choose the smallest unit)
Follow adding_tests.md:
- New tests inside an existing suite → no workflow change needed.
- New step in an existing job → add a
name + run step, gate with
if: fromJSON(inputs.affected-components).<COMPONENT>.test when component-specific.
- New job → add to the right validation workflow (or a
job_*.yml); set needs: [Build, Smart_CI],
runs-on, container, timeout-minutes, and a Smart CI if:.
Add / edit a reusable workflow (job_*.yml)
- Give it
on: workflow_call: with typed inputs (runner, image, affected-components,
python-version, ...) and permissions: read-all.
- Reference it from validation workflows via
uses: ./.github/workflows/job_<name>.yml with with: and
needs: [ Build, Smart_CI ].
- Keep the input contract minimal and documented via
description:.
Wire up Smart CI for a component
- Map source paths → label in
.github/labeler.yml ('category: X': [globs]).
- Declare dependents in
.github/components.yml under revalidate: (build+test) / build: (build
only); use [] for none, or 'all' to force full validation. Dependencies are not transitive.
- Add
Smart_CI to the validating job's needs and gate with
if: fromJSON(needs.smart_ci.outputs.affected_components).<COMPONENT>.{build,test}. Keep the same
condition on every step/job in the dependency chain — a skipped step feeding an ungated dependent
leaves it running against missing outputs.
- Aggregate results into the
Overall_Status job (it needs: the real jobs and reports one required
check). A workflow that must be required cannot use a paths: filter — a filtered-out run reports
no status and blocks the merge queue; rely on Smart CI + Overall_Status instead.
Add / change a custom action
- Composite
action.yml under .github/actions/<name>/. Declare inputs (with description,
required, default) and runs: using: composite. Every run step needs an explicit shell:.
- Reference untrusted input via
env: inside the step, not inline interpolation.
- Update custom_actions.md if the action is
user-facing.
Choosing the implementation environment
- Python is the default and preferred implementation language for a custom action's logic. Use it for
any action that does not need extensive access to the GitHub (Actions) API — file/artifact
handling, environment setup, packaging, running tools, parsing, etc.
- Use JavaScript/TypeScript only when the action uses the GitHub (Actions) API extensively — the
Octokit/
@actions/* toolkit gives first-class typed access to it. The bundled
.github/actions/cache action is the reference example of a
JS-based action.
- When in doubt, prefer Python and keep API interaction minimal.
Add / use a custom Docker image
Follow docker_images.md: add a Dockerfile under
.github/dockerfiles/{ov_build,ov_test}/<platform>/, ensure a Docker job runs handle_docker with the
image path in images:, add Docker to consumers' needs, and set
image: ${{ fromJSON(needs.docker.outputs.images).<group>.<name> }}. When adding a new env-setup script,
add it under category: docker_env in labeler.yml, exclude it from .dockerignore, and bump
.github/dockerfiles/docker_tag (handle_docker prompts you).
Change a runner or container
Pick the pool from runners.md; keep container
volumes/options (shared drive, sccache) consistent with sibling jobs. GPU → Docker + self-hosted label.
Validation before finishing
- YAML/lint: run
actionlint if available (actionlint .github/workflows/<file>.yml); otherwise
sanity-check YAML parses. Mirror what workflows_scans.yml (CodeQL actions + semgrep) and
dependency_review.yml enforce — those run on any .github/workflows/** change.
- Pinning: every third-party
uses: is a full SHA + version comment.
- Permissions: workflow defaults to least privilege; extra scopes are job-scoped and justified.
- Smart CI: new component-specific jobs/steps are gated and
Smart_CI is in needs.
- Scope: only intended files changed; no stray
docker_tag/labeler/components edits unless required.
- Do not hand-edit any
*.lock.yml — those belong to agentic workflows.
Pitfalls to check
- Unpinned or tag-pinned third-party action — supply-chain risk; CodeQL/semgrep will flag it.
- Untrusted input in
run: — ${{ github.event.* }} interpolated into shell is an injection vector;
route through env:.
- Over-broad
permissions: — especially write scopes at workflow level.
- Job that ignores Smart CI — burns limited self-hosted/GPU capacity on unaffected PRs.
- Smart CI condition mismatch across a dependency chain — a step skipped by a Smart CI
if: whose
dependent step/job lacks the same condition runs against missing outputs/artifacts. Gate the whole
chain consistently.
paths: filter on a required workflow — filtered-out runs report no status and hang the merge
queue; use Smart CI + Overall_Status instead of paths:.
- Hard-coded
docker.io / non-ACR image on aks-* — pulls fail or hit rate limits; use the ACR
mirror or handle_docker output.
- Missing
Docker/Smart_CI/Build in needs — races or missing artifacts/inputs.
- Editing a
job_*.yml input contract without updating every caller's with: block.
- Docker env change without
docker_tag bump — the image won't rebuild; handle_docker fails the check.
- Missing
timeout-minutes — a hung job can occupy a runner indefinitely.
- GPU job without Docker — required on persistent GPU runners.
Skill self-improvement
Keep this skill and the CI docs in sync with reality. When a change reveals a new rule, pattern, or
footgun:
- New reusable workflow / custom action / Dockerfile convention — note it under Common tasks and,
if user-facing, in the matching page under
docs/dev/ci/github_actions/.
- New footgun (a pinning/permission/Smart CI/Docker/cache mistake that bit you) — add it to
Pitfalls.
- A rule becomes obsolete (a workflow removed, a runner pool renamed, a path moved) — update or
remove the stale entry instead of leaving it.
- Keep it concise — prefer linking to the (updated) doc over duplicating detail; this file is a short
actionable checklist, not a second copy of the documentation.
- This directory is reachable via both
.agents/skills/ and .claude/skills/ (the former is a symlink to
the latter), so a single edit updates both — do not create a duplicate copy.