Publishing to JFrog Artifactory¶
Index · Publishing to PyPI · Publishing Docker Images · v1.0.0 Publish Execution Plan - 2026-09-05
Artifactory is a mirror of the public release, not a replacement for it. The canonical channels stay PyPI (testo-core) and GHCR (testo-runner); Artifactory exists so consumers behind a corporate proxy can pull from an internal registry.
Workflow: .github/workflows/artifactory-publish.yml.
Design: inert until configured¶
Every job is gated on if: vars.ARTIFACTORY_URL != ''. Until that repository variable exists the workflow no-ops rather than failing. This is deliberate — the workflow can land before the Artifactory repos exist without putting the v1.0.0 release at risk.
One-time setup (Artifactory admin required)¶
- Create a PyPI-type local repository, e.g.
testo-pypi-local. - Create a Docker local repository, e.g.
testo-docker-local. Note whether your instance uses subdomain or repository-path Docker access — that determinesARTIFACTORY_DOCKER_HOST. - Create an identity token for a service user with deploy rights on both repos. Use a token, never an account password.
One-time setup (GitHub repo settings)¶
Repository variables (Settings → Secrets and variables → Actions → Variables):
| Variable | Example |
|---|---|
ARTIFACTORY_URL |
https://acme.jfrog.io |
ARTIFACTORY_PYPI_REPO |
testo-pypi-local |
ARTIFACTORY_DOCKER_REPO |
testo-docker-local |
ARTIFACTORY_DOCKER_HOST |
acme.jfrog.io |
Repository secrets:
| Secret | Value |
|---|---|
ARTIFACTORY_USERNAME |
service user |
ARTIFACTORY_TOKEN |
identity token |
Setting ARTIFACTORY_URL is what switches the workflow on.
Triggering¶
- Automatic: fires on
release: published, alongsidepublish.ymlanddocker-publish.yml. - Manual:
workflow_dispatchwith aversioninput (e.g.1.0.0) — use this to publish to Artifactory after a release that predates the configuration, which is exactly the v1.0.0 case if Artifactory is provisioned late.
Both paths strip a leading v: the git tag is v1.0.0, the package version is 1.0.0.
Consuming¶
pip install --index-url https://<user>:<token>@acme.jfrog.io/artifactory/api/pypi/testo-pypi-local/simple testo-core==1.0.0
docker pull acme.jfrog.io/testo-docker-local/testo-runner:1.0.0
Prefer a ~/.netrc entry or pip config over inlining the token in the index URL.
Alternative: JFrog OIDC¶
jfrog/setup-jfrog-cli supports OIDC, removing the stored token — the same posture publish.yml already uses for PyPI. It needs an OIDC integration configured on the Artifactory side, so the token path is the documented default. Switch once the integration exists; the workflow's variable contract does not change.
Known gaps¶
- No cleanup policy on the local repos yet — every release accumulates.
- Image is not re-scanned before the Artifactory push; it is the same digest
docker-publish.ymlalready scanned with Trivy.