Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .shellspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# kcov (coverage) options
--kcov-options "--include-pattern=build-poetry,get-build-number,pr_cleanup,promote,build-gradle,config-maven,build-maven,config-npm,build-npm,build-yarn,shared,config-gradle,config-pip,update-release-channel,report-ci-metrics"
--kcov-options "--include-pattern=build-poetry,config-poetry,get-build-number,pr_cleanup,promote,build-gradle,config-maven,build-maven,config-npm,build-npm,build-yarn,shared,config-gradle,config-pip,update-release-channel,report-ci-metrics"
# --kcov-options "--exclude-pattern=.github,.idea,.git"

# define minimum coverage (fail otherwise)
Expand Down
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ These badges show the status of workflows in dummy repositories that use (or sho
- [`get-build-number`](#get-build-number)
- [`config-maven`](#config-maven)
- [`build-maven`](#build-maven)
- [`config-poetry`](#config-poetry)
- [`build-poetry`](#build-poetry)
- [`config-gradle`](#config-gradle)
- [`build-gradle`](#build-gradle)
Expand Down Expand Up @@ -369,10 +370,81 @@ See also [`config-maven`](#config-maven) output environment variables.

---

## `config-poetry`

Configure Poetry build environment with build number, JFrog authentication, and caching.

This action configures Poetry to pull packages from the internal JFrog Artifactory registry instead of the public PyPI.

> **Note:** This action automatically calls [`get-build-number`](#get-build-number) to manage the build number.

### Requirements

#### Required GitHub Permissions

- `id-token: write`
- `contents: write`

#### Required Vault Permissions

- `public-reader` or `private-reader`: Artifactory role for reading dependencies

#### Other Dependencies

The Python and Poetry tools must be pre-installed. Use of `mise` is recommended.

### Usage

```yaml
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: SonarSource/ci-github-actions/config-poetry@v1
- run: poetry install
```

### Inputs

| Input | Description | Default |
|---------------------------|-----------------------------------------------------------------------------|----------------------------------------------------------------------|
| `working-directory` | Relative path under github.workspace to execute the build in | `.` |
| `artifactory-reader-role` | Suffix for the Artifactory reader role in Vault | `private-reader` for private repos, `public-reader` for public repos |
| `artifactory-pypi-repo` | PyPI virtual repository to resolve dependencies from | `sonarsource-pypi` |
| `repox-url` | URL for Repox | `https://repox.jfrog.io` |
| `poetry-virtualenvs-path` | Path to the Poetry virtual environments, relative to GitHub workspace | `.cache/pypoetry/virtualenvs` |
| `poetry-cache-dir` | Path to the Poetry cache directory, relative to GitHub workspace | `.cache/pypoetry` |
| `disable-caching` | Whether to disable Poetry caching entirely | `false` |

### Outputs

| Output | Description |
|----------------|---------------------------------------------------------------------------|
| `BUILD_NUMBER` | The current build number. Also set as environment variable `BUILD_NUMBER` |
| `current-version` | The project version from pyproject.toml (before replacement). Also set as environment variable `CURRENT_VERSION` |
| `project-version` | The project version with build number (after replacement). Also set as environment variable `PROJECT_VERSION` |

### Output Environment Variables

| Environment Variable | Description |
|------------------------------------|--------------------------|
| `BUILD_NUMBER` | The current build number |
| `CURRENT_VERSION` | The project version from pyproject.toml (before replacement) |
| `PROJECT_VERSION` | The project version with build number (after replacement) |
| `POETRY_HTTP_BASIC_REPOX_USERNAME` | Repox username for Poetry |
| `POETRY_HTTP_BASIC_REPOX_PASSWORD` | Repox access token for Poetry |

See also [`get-build-number`](#get-build-number) output environment variables.

---

## `build-poetry`

Build, analyze, and publish a Python project using Poetry with SonarQube integration and Artifactory deployment.

> **Note:** This action automatically calls [`config-poetry`](#config-poetry) to set up the Poetry environment.

### Requirements

#### Required GitHub Permissions
Expand Down
48 changes: 28 additions & 20 deletions build-poetry/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ inputs:
outputs:
project-version:
description: The project version from pyproject.toml with BUILD_NUMBER
value: ${{ steps.build.outputs.project-version }}
value: ${{ steps.config.outputs.project-version }}
BUILD_NUMBER:
description: The build number, incremented or reused if already cached
value: ${{ steps.get_build_number.outputs.BUILD_NUMBER }}
value: ${{ steps.config.outputs.BUILD_NUMBER }}
deployed:
description: Whether artifacts were deployed
value: ${{ steps.build.outputs.deployed }}
Expand All @@ -87,6 +87,7 @@ runs:

mkdir -p ".actions"
ln -sf "$host_actions_root/get-build-number" .actions/get-build-number
ln -sf "$host_actions_root/config-poetry" .actions/config-poetry
ln -sf "$host_actions_root/shared" .actions/shared
ls -la .actions/*
echo "::endgroup::"
Expand All @@ -103,29 +104,29 @@ runs:
echo "ARTIFACTORY_DEPLOYER_ROLE=${ARTIFACTORY_DEPLOYER_ROLE}" >> "$GITHUB_ENV"
cp "$ACTION_PATH_BUILD_POETRY/mise.local.toml" mise.local.toml

- uses: ./.actions/get-build-number
id: get_build_number
with:
host-actions-root: ${{ steps.set-path.outputs.host_actions_root }}
- name: Cache local Poetry cache
uses: SonarSource/gh-action_cache@0666e03d7b6480758214db522a3aac989d67b026 # v1.7.0
if: inputs.disable-caching == 'false'
with:
path: ${{ github.workspace }}/${{ inputs.poetry-cache-dir }}
key: poetry-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
restore-keys: poetry-${{ runner.os }}-
- uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
version: 2026.5.9

- uses: ./.actions/config-poetry
id: config
with:
host-actions-root: ${{ steps.set-path.outputs.host_actions_root }}
artifactory-reader-role: ${{ env.ARTIFACTORY_READER_ROLE }}
repox-url: ${{ inputs.repox-url }}
working-directory: ${{ inputs.working-directory }}
poetry-cache-dir: ${{ inputs.poetry-cache-dir }}
poetry-virtualenvs-path: ${{ inputs.poetry-virtualenvs-path }}
disable-caching: ${{ inputs.disable-caching }}

- uses: SonarSource/vault-action-wrapper@0a3114fe1230b784c35b53b099f9ab1f1e538cc7 # 3.5.0
if: inputs.deploy != 'false' && inputs.run-shadow-scans != 'true'
id: artifactory
with:
url: ${{ contains(inputs.repox-url, 'dev.sonar.build') && 'https://vault.dev.sonar.build' || 'https://vault.sonar.build' }}
# yamllint disable rule:line-length
secrets: |
development/artifactory/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_READER_ROLE }} access_token | ARTIFACTORY_ACCESS_TOKEN;
development/artifactory/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_READER_ROLE }} username | ARTIFACTORY_USERNAME;
${{ inputs.deploy != 'false' && inputs.run-shadow-scans != 'true' && format('development/artifactory/token/{{REPO_OWNER_NAME_DASH}}-{0} access_token | ARTIFACTORY_DEPLOY_ACCESS_TOKEN;', env.ARTIFACTORY_DEPLOYER_ROLE) || '' }}
${{ format('development/artifactory/token/{{REPO_OWNER_NAME_DASH}}-{0} access_token | ARTIFACTORY_DEPLOY_ACCESS_TOKEN;', env.ARTIFACTORY_DEPLOYER_ROLE) }}
# yamllint enable rule:line-length
- uses: SonarSource/vault-action-wrapper@0a3114fe1230b784c35b53b099f9ab1f1e538cc7 # 3.5.0
id: secrets
Expand All @@ -152,12 +153,12 @@ runs:
ARTIFACTORY_URL: ${{ format('{0}/artifactory', inputs.repox-url) }}
DEPLOY: ${{ inputs.deploy }}
DEPLOY_PULL_REQUEST: ${{ inputs.deploy-pull-request }}
ARTIFACTORY_PYPI_REPO: ${{ inputs.public == 'true' && 'sonarsource-pypi' || 'sonarsource-pypi' }} # FIXME: sonarsource-pypi-public
ARTIFACTORY_DEPLOY_REPO: ${{ inputs.artifactory-deploy-repo != '' && inputs.artifactory-deploy-repo ||
github.event.repository.visibility == 'public' && 'sonarsource-pypi-public-qa' || 'sonarsource-pypi-private-qa' }}
ARTIFACTORY_ACCESS_TOKEN: ${{ fromJSON(steps.artifactory.outputs.vault).ARTIFACTORY_ACCESS_TOKEN }}
ARTIFACTORY_USERNAME: ${{ fromJSON(steps.artifactory.outputs.vault).ARTIFACTORY_USERNAME }}
ARTIFACTORY_DEPLOY_ACCESS_TOKEN: ${{ fromJSON(steps.artifactory.outputs.vault).ARTIFACTORY_DEPLOY_ACCESS_TOKEN }}
# yamllint disable rule:line-length
ARTIFACTORY_DEPLOY_ACCESS_TOKEN: ${{ steps.artifactory.outputs.vault &&
fromJSON(steps.artifactory.outputs.vault).ARTIFACTORY_DEPLOY_ACCESS_TOKEN || '' }}
# yamllint enable rule:line-length
POETRY_VIRTUALENVS_PATH: ${{ github.workspace }}/${{ inputs.poetry-virtualenvs-path }}
POETRY_CACHE_DIR: ${{ github.workspace }}/${{ inputs.poetry-cache-dir }}

Expand Down Expand Up @@ -215,3 +216,10 @@ runs:
echo "🐸 [Browse build \`${build_name}:${BUILD_NUMBER}\` in Artifactory](${ARTIFACTORY_BROWSE_URL})" >> $GITHUB_STEP_SUMMARY
"$ACTION_PATH_BUILD_POETRY/../shared/generate-jfrog-summary.sh" repox
fi

- name: Clean up local action symlinks
if: always()
shell: bash
run: |
rm -f .actions/get-build-number .actions/config-poetry .actions/shared
rmdir .actions 2>/dev/null || true
50 changes: 2 additions & 48 deletions build-poetry/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# Required inputs (must be explicitly provided):
# - BUILD_NUMBER: Build number for versioning
# - ARTIFACTORY_URL: URL to Artifactory repository
# - ARTIFACTORY_PYPI_REPO: Repository to install dependencies from
# - ARTIFACTORY_ACCESS_TOKEN: Access token to read Repox repositories
# - ARTIFACTORY_DEPLOY_REPO: Deployment repository name
# - ARTIFACTORY_DEPLOY_ACCESS_TOKEN: Access token to deploy to the repository
# - DEFAULT_BRANCH: Default branch name (e.g. main)
Expand Down Expand Up @@ -45,7 +43,7 @@ set -euo pipefail
# shellcheck source=../shared/common-functions.sh
source "$(dirname "${BASH_SOURCE[0]}")/../shared/common-functions.sh"

: "${ARTIFACTORY_URL:?}" "${ARTIFACTORY_PYPI_REPO:?}" "${ARTIFACTORY_ACCESS_TOKEN:?}" "${ARTIFACTORY_USERNAME:?}" "${RUN_SHADOW_SCANS:?}"
: "${ARTIFACTORY_URL:?}" "${RUN_SHADOW_SCANS:?}"
: "${ARTIFACTORY_DEPLOY_REPO:?}" "${DEPLOY_PULL_REQUEST:=false}"
: "${GITHUB_REF_NAME:?}" "${BUILD_NUMBER:?}" "${GITHUB_REPOSITORY:?}" "${GITHUB_EVENT_NAME:?}" "${GITHUB_EVENT_PATH:?}"
: "${PULL_REQUEST?}" "${DEFAULT_BRANCH:?}" "${GITHUB_ENV:?}" "${GITHUB_OUTPUT:?}" "${GITHUB_SHA:?}" "${GITHUB_RUN_ID:?}"
Expand Down Expand Up @@ -163,36 +161,6 @@ set_build_env() {
git_fetch_unshallow
}

set_project_version() {
local current_version release_version digit_count

if ! current_version=$(poetry version -s); then
echo "::error title=Invalid project version::Could not get version from Poetry project ('poetry version -s')" >&2
echo "$current_version" >&2
return 1
fi
export CURRENT_VERSION=$current_version

release_version=${current_version%".dev"*}
# In case of 2 digits, we need to add a '0' as the 3rd digit.
digit_count=$(echo "${release_version//./ }" | wc -w)
if [[ "$digit_count" -lt 3 ]]; then
release_version="$release_version.0"
fi
if [[ "$digit_count" -gt 3 && $release_version =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
release_version="${BASH_REMATCH[0]}"
echo "::warning title=Version truncated::Version was truncated to $release_version because it had more than 3 digits" >&2
fi
release_version="$release_version.${BUILD_NUMBER}"

echo "Replacing version $current_version with $release_version"
poetry version "$release_version"
echo "project-version=$release_version" >> "$GITHUB_OUTPUT"
echo "PROJECT_VERSION=$release_version" >> "$GITHUB_ENV"
echo "PROJECT_VERSION=$release_version"
export PROJECT_VERSION=$release_version
}

# Determine build configuration based on branch type
get_build_config() {
local enable_sonar enable_deploy
Expand Down Expand Up @@ -262,16 +230,6 @@ get_build_config() {
export BUILD_SONAR_ARGS="${sonar_args[*]:-}"
}

jfrog_poetry_install() {
jf config remove repox > /dev/null 2>&1 || true # Ignore inexistent configuration
jf config add repox --url "${ARTIFACTORY_URL%/artifactory*}" --artifactory-url "$ARTIFACTORY_URL" --access-token "$ARTIFACTORY_ACCESS_TOKEN"
jf config use repox
jf poetry-config --server-id-resolve repox --repo-resolve "$ARTIFACTORY_PYPI_REPO"
export POETRY_HTTP_BASIC_REPOX_USERNAME="$ARTIFACTORY_USERNAME"
export POETRY_HTTP_BASIC_REPOX_PASSWORD="$ARTIFACTORY_ACCESS_TOKEN"
poetry install
}

jfrog_poetry_publish() {
jf config remove repox > /dev/null 2>&1 || true # Ignore inexistent configuration
jf config add repox --url "${ARTIFACTORY_URL%/artifactory*}" --artifactory-url "$ARTIFACTORY_URL" --access-token "$ARTIFACTORY_DEPLOY_ACCESS_TOKEN"
Expand All @@ -294,15 +252,11 @@ build_poetry() {
echo "Pull Request: ${PULL_REQUEST}"
echo "Deploy Pull Request: ${DEPLOY_PULL_REQUEST}"

echo "::group::Set project version"
set_project_version
echo "::endgroup::"

get_build_config

echo "::group::Install dependencies"
echo "Installing dependencies..."
jfrog_poetry_install
poetry install
echo "::endgroup::"

echo "::group::Build project"
Expand Down
Loading
Loading