Publish Python Packages with Twine Using JFrog CLI
Run twine commands to publish Python packages to Artifactory with optional build-info collection.
When to Use
Use jf twine to publish Python packages (wheels, sdists) to Artifactory. This is the upload counterpart to jf pip (which installs packages).
Typical Python publish workflow:
# 0. Install build tools (one-time)
pip3 install build twine
# 1. Build the package
python3 -m build
# 2. Upload to Artifactory with build-info
jf twine upload dist/* --build-name=my-python-lib --build-number=$BUILD_NUMBER
# 3. Publish build info
jf rt build-publish my-python-lib $BUILD_NUMBERPrerequisites
-
Install twine separately:
pip3 install twinemacOS note:
pip3 installplaces thetwinebinary in~/Library/Python/X.Y/bin/, which is not in$PATHby default. Add it once with:export PATH="$PATH:$(python3 -m site --user-base)/bin"Add this line to your
~/.zshrcor~/.bashrcto make it permanent. -
Configure a JFrog server with
jf config addorjf c add. -
Configure the deployer (and optionally resolver) repository before running
jf twine. Runjf pip-configwith all required flags:jf pip-config \ --server-id-resolve=<server-id> \ --repo-resolve=<pypi-virtual-repo> \ --server-id-deploy=<server-id> \ --repo-deploy=<pypi-local-repo>This writes a
.jfrog/projects/pip.yamlfile thatjf twinereads at runtime. You do not need a separatejf twine-configcommand. -
Build your package before uploading:
python3 -m build
Build: jf twine
jf twineRun twine commands to publish Python packages to Artifactory with optional build-info collection.
To publish Python packages with Twine:
- Complete Prerequisites (including
jf pip-configand building the package withpython3 -m build). - Run
jf twinewith the Twine subcommand (for example,checkorupload) and optional build-info flags (see Build Examples).
Synopsis
jf twine <twine-arguments> [command options]
Aliases: none
Arguments
| Argument | Required | Description |
|---|---|---|
<twine-arguments> | Yes | Arguments and options for the twine command (for example, upload, check) |
Build Options
| Flag | Default | Description |
|---|---|---|
--build-name | — | Build name for build-info. Requires --build-number. |
--build-number | — | Build number for build-info. Requires --build-name. |
--module | — | Optional module name for build-info. Requires --build-name and --build-number. |
--project | — | JFrog Artifactory project key |
Build Examples
Help
jf twine --helpCheck Package
Run jf twine check before uploading to validate package metadata. This command runs entirely locally — no Artifactory authentication is required.
jf twine check dist/*Expected output:
Checking dist/mypackage-1.0.0-py3-none-any.whl: PASSED
Checking dist/mypackage-1.0.0.tar.gz: PASSED
Upload with Build-Info
jf twine upload dist/* --build-name=<build-name> --build-number=<build-number>Where:
- <build-name>: A name for the build (for example,
my-python-lib) - <build-number>: A number or identifier for the build run (for example,
1)
For example:
jf twine upload dist/* --build-name=my-python-lib --build-number=1The glob dist/* expands to include both .whl and .tar.gz files automatically.
Expected output (twine verbose output followed by JFrog build-info collection):
Uploading distributions to https://<your-instance>.jfrog.io/artifactory/api/pypi/<repo>
Uploading mypackage-1.0.0-py3-none-any.whl
Uploading mypackage-1.0.0.tar.gz
Important Notes
- pip-config or pipenv-config covers twine: The
jf twinecommand reads its deployment repository from either ajf pip-configorjf pipenv-configconfiguration — whichever exists in your project. If both are present, the CLI checks forpipconfig first, thenpipenv. You do not need a separatejf twine-configcommand. If neither config is found, you will see:no config file was found! Before running the 'jf twine' command on a project for the first time, the project should be configured with the 'jf pip-config OR pipenv-config' command. - Twine must be installed separately (
pip3 install twine). See the macOS PATH note in Prerequisites. - The
checksubcommand validates package metadata locally before upload — no Artifactory authentication is required. Use it to catch packaging issues early.
CI/CD Example (GitHub Actions)
# .github/workflows/build.yml
steps:
- uses: actions/checkout@v4
- name: Setup JFrog CLI
uses: jfrog/setup-jfrog-cli@v4
env:
JF_URL: ${{ vars.JF_URL }}
JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install build tools
run: pip3 install build twine
- name: Configure pip
run: jf pip-config --server-id-resolve=setup-jfrog-cli-server --repo-resolve=pypi-virtual --server-id-deploy=setup-jfrog-cli-server --repo-deploy=pypi-local
- name: Build package
run: python3 -m build
- name: Check package
run: jf twine check dist/*
- name: Upload to Artifactory
run: jf twine upload dist/* --build-name=my-python-lib --build-number=${{ github.run_number }}
- name: Publish build info
run: jf rt build-publish my-python-lib ${{ github.run_number }}Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
no config file was found | jf pip-config was not run, or was run from a different directory than jf twine | Run jf pip-config with --repo-deploy set from your project root; jf twine must be run from the same directory (see Prerequisites) |
401 on upload — Token failed verification: parse, username shown as <empty> | Server uses a reference (non-JWT) token and no username is set in the server config | Re-run jf config add and enter a username — twine requires basic auth (username + token) and will fail silently if the username is empty |
| 401 / 403 on upload — wrong credentials or missing permissions | Invalid credentials or user lacks deploy permissions on the target repo | Re-run jf config add to update credentials; verify the user has deploy permissions on the PyPI local repo |
[Warn] couldn't extract payload from Access Token | Server uses a reference (non-JWT) token | Expected — safe to ignore for jf twine check (local only). For jf twine upload, ensure a username is configured via jf config add |
twine not found / exec: "twine": executable file not found in $PATH | Twine is not installed, or its binary is not in $PATH | Run pip3 install twine. On macOS, also add export PATH="$PATH:$(python3 -m site --user-base)/bin" to your shell profile |
No module named build | The build package is not installed | Run pip3 install build |
NotOpenSSLWarning on every twine call (macOS) | macOS ships with LibreSSL; urllib3 v2 requires OpenSSL 1.1.1+ | Safe to ignore; or install Python via Homebrew (brew install python) to use an OpenSSL-linked build |
"file not found" on jf twine upload dist/* | Package was not built first | Run python3 -m build before jf twine upload |
| Upload succeeds but package not visible | Uploaded to wrong repository | Confirm --repo-deploy in your pip/pipenv config points to a PyPI local repository |
Enable debug logging: export JFROG_CLI_LOG_LEVEL=DEBUG
Related Topics
- Build Tools Overview — Capabilities matrix and tool reference
- Native Mode — Supported packages with Native Mode
Updated about 1 month ago
