Use Poetry with JFrog CLI

Run Poetry commands with Artifactory integration and optional build-info collection.

This topic covers the following tasks:

When to Use

Use jf poetry if your Python project uses Poetry (with pyproject.toml) for dependency management. For pip-based projects, use jf pip. For Pipenv-based projects, use jf pipenv.

Prerequisites

  • Poetry must be installed and available on your $PATH.
    • Recommended: pipx install poetry (automatically adds Poetry to your PATH).
    • pip: pip install poetry — on macOS, pip installs Poetry to ~/Library/Python/X.Y/bin/ which is not on $PATH by default. After installing, run:
      export PATH="$PATH:$(python3 -m site --user-base)/bin"
      Add this line to your shell profile (~/.zshrc or ~/.bashrc) to make it permanent.
  • Run jf poetry-config in the project directory before the first install (Wrapped Mode only).
  • Configure a server with jf config add or jf c add.
  • Authentication to Artifactory is required.
  • For Native Mode, configure Poetry via Artifactory's Set Me Up instructions instead.

Configuration: jf poetry-config

Generate Poetry build configuration for dependency resolution. Run this once per project before your first install.

To configure Poetry for Artifactory:

  1. From your project directory, run jf poetry-config with your Artifactory server ID and PyPI resolution repository (see Non-Interactive Configuration with Flags).
  2. Confirm the generated config under .jfrog/projects/poetry.yaml (see How to Verify).

Synopsis

jf poetry-config [options]

Aliases: poc

Configuration Options

FlagDefaultDescription
--globalfalseSet to true for global configuration (all projects). Specific projects can override.
--repo-resolveRepository for dependencies resolution
--server-id-resolveArtifactory server ID for resolution. Configure with jf config add.

Configuration Examples

View Help

jf poetry-config --help

Non-Interactive Configuration with Flags

jf poetry-config --server-id-resolve=<server-id> --repo-resolve=<repo-name>

Where:

  • <server-id>: The server ID configured using jf config add
  • <repo-name>: The name of the PyPI repository in Artifactory

For example:

jf poetry-config --server-id-resolve=my-server --repo-resolve=pypi-virtual

Why Run Config First?

You must run jf poetry-config before jf poetry install (Wrapped Mode only). The config creates .jfrog/projects/poetry.yaml for dependency resolution through Artifactory. Without it, jf poetry does not know where to fetch packages.

👍

Shortcut

: In CI/CD, pass all flags non-interactively so the config step is fully automated and reproducible.

Configuration Notes

  • Config sets resolution only: jf poetry-config configures dependency resolution but not a deployer repository. To publish, pass --repository directly on jf poetry publish (Wrapped Mode) or use native poetry publish (Native Mode).
  • Run once per project: Re-run when changing the resolution repository.

Expected Output

$ jf poetry-config --server-id-resolve=my-server --repo-resolve=pypi-virtual
[Info] poetry build config successfully created.

How to Verify

After running, confirm the configuration exists:

cat .jfrog/projects/poetry.yaml

Expected file contents:

version: 1
type: poetry
resolver:
    repo: pypi-virtual
    serverId: my-server

Build: jf poetry

Run Poetry commands with Artifactory integration and optional build-info collection.

To run Poetry with Artifactory integration:

  1. Ensure jf poetry-config has been run in the project directory when using Wrapped Mode (see Configuration).
  2. Run jf poetry with the Poetry subcommand and arguments you need, adding --build-name and --build-number when you want build-info (see Build Examples).

Synopsis

jf poetry <poetry-arguments> [options]

Aliases: none

Arguments

ArgumentRequiredDescription
<poetry-arguments>YesArguments and options for the poetry command (for example, install, add)

Build Options

FlagDefaultDescription
--build-nameBuild name for build-info. Requires --build-number.
--build-numberBuild number for build-info. Requires --build-name.
--moduleOptional module name for build-info. Requires --build-name and --build-number.
--projectJFrog Artifactory project key

Build Examples

Help

jf poetry --help
📘

Note

jf poetry --help passes --help through to the native Poetry client and shows Poetry's own help page. JFrog-specific flags (--build-name, --build-number, --module, --project) are intercepted by JFrog CLI and do not appear in Poetry's help output. Refer to the Build Options table above for JFrog-specific flags.

Install with Build-Info

jf poetry install --build-name=my-app --build-number=1

Expected output (Wrapped Mode):

[Info] Running Poetry install.
[Info] Added tool.poetry.source name:"<repo-name>" url:"https://<server>/artifactory/api/pypi/<repo-name>"
[Info] Running Poetry update
Updating dependencies
Resolving dependencies...
Writing lock file
📘

Note

In Wrapped Mode, JFrog CLI automatically injects a [[tool.poetry.source]] entry into your pyproject.toml and may run poetry update to resolve dependencies. This is expected behaviour and will modify poetry.lock. Switch to Native Mode if you require a fully deterministic lockfile.

Add Dependencies

jf poetry add <package-name>

Poetry Execution Modes

JFrog CLI supports two execution modes for Poetry.

Wrapped Mode (Default)

  • Uses jf poetry-config settings
  • Injects tool.poetry.source entries into pyproject.toml
  • May call poetry update during dependency resolution
  • May modify poetry.lock
  • Automatically collects build-info when provided --build-name / --build-number

Native Mode

Enable using:

export JFROG_RUN_NATIVE=true

In Native Mode, JFrog CLI:

  • Delegates to the native Poetry client without injecting configuration
  • Does not inject tool.poetry.source into pyproject.toml
  • Does not call poetry update
  • Does not modify poetry.lock
  • Does not apply jf poetry-config values or JFrog CLI YAML (.jfrog/projects/*.yaml) configuration
  • Relies entirely on Poetry's own configuration (as set via Artifactory Set Me Up)
  • Still collects build-info when --build-name and --build-number are provided via jf poetry
  • Ensures deterministic, reproducible builds
📘

Prerequisite for Native Mode

Your pyproject.toml must contain a [[tool.poetry.source]] entry pointing to your Artifactory PyPI repository for Native Mode to route traffic through Artifactory. This entry can be added manually or will already be present if you previously ran a Wrapped Mode install. You can also add it via Artifactory's Set Me Up instructions.

Install: Wrapped vs Native

Wrapped Mode Install

jf poetry install --build-name=my-python-app --build-number=1.2.0

Wrapped Mode behaviour:

  • May implicitly run poetry update
  • May modify poetry.lock
  • May inject repository sources into pyproject.toml

Use Native Mode if you require full lockfile fidelity.

Native Mode Install

export JFROG_RUN_NATIVE=true

# With build-info collection
jf poetry install --build-name=my-python-app --build-number=1.2.0

# Or directly (no build-info)
poetry install
📘

Note

In Native Mode, you can use either jf poetry install (which delegates to native Poetry and collects build-info when --build-name/--build-number are provided) or poetry install directly (no build-info). Poetry must be configured via Artifactory's Set Me Up instructions to authenticate and resolve from Artifactory.

Publish: Wrapped vs Native

Wrapped Mode Publish

jf poetry publish --repository pypi-local --no-interaction --build-name=my-python-app --build-number=1.2.0
❗️

Important

Use --repository (Poetry's native flag, also available as -r) to specify the target repository. The --no-interaction flag suppresses Poetry's confirmation prompt (Build anyway? (yes/no)) when existing dist files are present — this is required in CI/CD environments where no TTY is available.

Behaviour:

  • May inject tool.poetry.source into pyproject.toml
  • May update dependency metadata

Use Native Mode to prevent any modifications.

Native Mode Publish

export JFROG_RUN_NATIVE=true

# With build-info collection
jf poetry publish --no-interaction --build-name=my-python-app --build-number=1.2.0

# Or directly (no build-info)
poetry build
poetry publish --no-interaction -r <REPO_ALIAS>

Credentials must be configured via Set Me Up.

Environment Variable

VariablePurposeDefault
JFROG_RUN_NATIVEEnables Native Mode and bypasses Wrapped Mode entirelyfalse

FAQ

Q: Does jf poetry-config affect Native Mode?

A: No — Native Mode ignores jf poetry-config and YAML.

Q: Can Native Mode collect build-info?

A: Yes — when you use jf poetry with --build-name and --build-number, build-info is collected even in Native Mode. If you run poetry directly (without the jf prefix), no build-info is collected.

For a cross-tool comparison of Native Mode across Maven, Gradle, npm, and Poetry, see the dedicated Native Mode page.

Universal Package Note

JFrog CLI is a pass-through to Poetry for execution and output. Output format/behaviour belongs to Poetry; if Poetry changes its output in a newer version, JFrog CLI would simply honour that without any custom adjustment.

📘

Note

The jf poetry-config configuration is used only in Wrapped Mode. Native Mode ignores this entirely.


Important Notes

  • Config required first: Run jf poetry-config in the project directory before your first install (Wrapped Mode only).
  • Resolution only: Poetry config currently supports dependency resolution. For publishing, use jf poetry publish (Wrapped Mode) or Poetry's native poetry publish (Native Mode).
  • Poetry commands: All standard Poetry arguments work (install, add, update, lock, and others).
  • Build-info: Use --build-name and --build-number, then publish with jf rt build-publish.

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 Poetry
    run: |
      pip install poetry
      echo "$(python3 -m site --user-base)/bin" >> $GITHUB_PATH
  - name: Configure Poetry
    run: jf poetry-config --server-id-resolve=setup-jfrog-cli-server --repo-resolve=pypi-virtual
  - name: Install dependencies
    run: jf poetry install --build-name=my-python-app --build-number=${{ github.run_number }}
  - name: Publish package
    run: jf poetry publish --repository pypi-local --no-interaction --build-name=my-python-app --build-number=${{ github.run_number }}
  - name: Publish build info
    run: jf rt build-publish my-python-app ${{ github.run_number }}

Troubleshooting

SymptomCauseFix
command not found: poetryPoetry not on $PATH (common after pip install)Add Poetry to PATH: export PATH="$PATH:$(python3 -m site --user-base)/bin". Use pipx install poetry to avoid this permanently.
no config file was foundjf poetry-config was not run (Wrapped Mode)Run jf poetry-config in the project directory
404 on jf poetry installResolution repository does not exist or name is wrongVerify the repo name matches an existing PyPI virtual repository in Artifactory
401 / 403 errorsInvalid credentials or insufficient permissionsRe-run jf config add with a valid access token
poetry.lock modified unexpectedlyWrapped Mode may run poetry update implicitlySwitch to Native Mode (export JFROG_RUN_NATIVE=true) for lockfile fidelity
The option "--repo" does not exist--repo is not a valid flag for jf poetry publishUse --repository (or -r) instead: jf poetry publish --repository <local-repo-name> --no-interaction ...
jf poetry publish hangs on Build anyway? (yes/no)Poetry prompts for confirmation when dist files existAdd --no-interaction to the publish command to suppress the prompt
jf poetry publish fails with "repo not configured"No target repository specifiedPass --repository <local-repo-name> directly on the publish command
[Warn] couldn't extract payload from Access TokenUsing a JFrog Platform reference token (non-JWT)This warning is informational only — commands still authenticate correctly. If using basic auth, provide a username via jf config add.
Native Mode not resolving from ArtifactoryNo [[tool.poetry.source]] in pyproject.tomlAdd a source entry manually or run a Wrapped Mode install first; alternatively, configure Poetry via Artifactory's Set Me Up

Enable debug logging: export JFROG_CLI_LOG_LEVEL=DEBUG


Related Topics