Skip to content

docs: OSSF best practice badge percentage #227

docs: OSSF best practice badge percentage

docs: OSSF best practice badge percentage #227

Workflow file for this run

name: Release
on:
push:
branches: [ 'main', 'master' ]
workflow_dispatch:
inputs:
release_force:
# see https://python-semantic-release.readthedocs.io/en/latest/github-action.html#command-line-options
description: |
Force release be one of: [major | minor | patch]
Leave empty for auto-detect based on commit messages.
type: choice
options:
- "" # auto - no force
- major # force major
- minor # force minor
- patch # force patch
default: ""
required: false
prerelease_token:
description: 'The "prerelease identifier" to use as a prefix for the "prerelease" part of a semver. Like the rc in `1.2.0-rc.8`.'
type: choice
options:
- rc
- beta
- alpha
default: rc
required: false
prerelease:
description: "Is a pre-release"
type: boolean
default: false
required: false
concurrency:
group: release
cancel-in-progress: false # prevent hiccups with semantic-release
env:
REPORTS_DIR: CI_reports
DIST_DIR: dist
DIST_ARTIFACT: python-dist
PYTHON_VERSION_DEFAULT: "3.12"
POETRY_VERSION: "1.8.1"
jobs:
quicktest:
runs-on: ubuntu-latest
steps:
- name: Checkout code
# see https://github.com/actions/checkout
uses: actions/checkout@v4
- name: Setup Python Environment
# see https://github.com/actions/setup-python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION_DEFAULT }}
architecture: 'x64'
- name: Install poetry
# see https://github.com/marketplace/actions/setup-poetry
uses: Gr1N/setup-poetry@v9
with:
poetry-version: ${{ env.POETRY_VERSION }}
- name: Install dependencies
run: poetry install --no-root
- name: Run tox
run: poetry run tox run -e py -s false
release-PyPI:
outputs:
released: ${{ steps.release.outputs.released }} # "true" if a release was made, "false" otherwise
version: ${{ steps.release.outputs.version }} # The newly released version if one was made, otherwise the current version
tag: ${{ steps.release.outputs.tag }} # The Git tag corresponding to the version output
needs:
- quicktest
# https://github.community/t/how-do-i-specify-job-dependency-running-in-another-workflow/16482
# limit this to being run on regular commits, not the commits that semantic-release will create
# but also allow manual workflow dispatch
if: "!contains(github.event.head_commit.message, 'chore(release):')"
runs-on: ubuntu-latest
permissions:
# NOTE: this enables trusted publishing.
# See https://github.com/pypa/gh-action-pypi-publish/tree/release/v1#trusted-publishing
# and https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
id-token: write
contents: write
steps:
- name: Checkout code
# see https://github.com/actions/checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup python
# see https://github.com/actions/setup-python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION_DEFAULT }}
architecture: 'x64'
- name: Install and configure Poetry
# See https://github.com/marketplace/actions/install-poetry-action
uses: snok/install-poetry@v1
with:
version: ${{ env.POETRY_VERSION }}
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Install dependencies
run: poetry install --no-root
- name: View poetry version
run: poetry --version
- name: Python Semantic Release
id: release
# see https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html
# see https://github.com/python-semantic-release/python-semantic-release
uses: python-semantic-release/python-semantic-release@v9.1.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
force: ${{ github.event.inputs.release_force }}
prerelease: ${{ github.event.inputs.prerelease }}
prerelease_token: ${{ github.event.inputs.prerelease_token }}
- name: Publish package distributions to PyPI
if: |
!failure() && !cancelled() &&
steps.release.outputs.released == 'true'
# see https://github.com/pypa/gh-action-pypi-publish
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
- name: Publish package distributions to GitHub Releases
if: |
!failure() && !cancelled() &&
steps.release.outputs.released == 'true'
# see https://github.com/python-semantic-release/upload-to-gh-release
uses: python-semantic-release/upload-to-gh-release@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
- name: Artifact python dist
if: |
!failure() && !cancelled() &&
steps.release.outputs.released == 'true'
# see https://github.com/actions/upload-artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.DIST_ARTIFACT }}
path: ${{ env.DIST_DIR }}/
if-no-files-found: error
# Dist results are required for further processing.
# Therefore, make sure that python-semantic-release is configuret to keep dist.
# see https://python-semantic-release.readthedocs.io/en/latest/configuration.html?highlight=remove_dist#remove-dist
release-container-image:
name: "Release: DockerHub & GitHubContainerRegistry"
needs:
- release-PyPI
if: |
!failure() && !cancelled() &&
needs.release-PyPI.result == 'success' &&
needs.release-PyPI.outputs.released == 'true' &&
needs.release-PyPI.outputs.version &&
needs.release-PyPI.outputs.tag
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.release-PyPI.outputs.version }}
ARTIFACT_DOCKER_SBOM: 'docker-image-bom'
DOCKERHUB_REPO: cyclonedx/cyclonedx-python
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/cyclonedx-python
steps:
- name: lowercase image tags
run: |
set -uex
echo "DOCKERHUB_REPO=${DOCKERHUB_REPO@L}" >> "${GITHUB_ENV}"
echo "GHCR_REPO=${GHCR_REPO@L}" >> "${GITHUB_ENV}"
- name: Checkout code (${{ env.TAG }})
# see https://github.com/actions/checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.release-PyPI.outputs.tag }}
- name: setup dirs
run: |
mkdir "$REPORTS_DIR"
mkdir "$DIST_DIR"
- name: Fetch python dist artifact
# see https://github.com/actions/download-artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.DIST_ARTIFACT }}
path: ${{ env.DIST_DIR }}/
- name: Build Container Image (${{ env.VERSION }})
run: >
docker build -f Dockerfile
--label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}"
--label "org.opencontainers.image.revision=${GITHUB_SHA}"
--label "org.opencontainers.image.version=${VERSION}"
--build-arg "VERSION=${VERSION}"
-t "$DOCKERHUB_REPO:$VERSION"
-t "$DOCKERHUB_REPO:latest"
-t "$GHCR_REPO:$VERSION"
-t "$GHCR_REPO:latest"
.
- name: Build own SBoM (XML)
run: >
docker run --rm "$DOCKERHUB_REPO:$VERSION"
environment
-vvv
--output-format XML
> "$REPORTS_DIR/$ARTIFACT_DOCKER_SBOM.bom.xml"
- name: Build own SBoM (JSON)
run: >
docker run --rm "$DOCKERHUB_REPO:$VERSION"
environment
-vvv
--output-format JSON
> "${REPORTS_DIR}/${ARTIFACT_DOCKER_SBOM}.bom.json"
- name: Artifact reports
if: ${{ ! cancelled() }}
# see https://github.com/actions/upload-artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_DOCKER_SBOM }}
path: ${{ env.REPORTS_DIR }}/*.bom.*
if-no-files-found: error
# publish AFTER the boms were build, as the bom-generation is kind of a test if the image works
- name: Login to DockerHub
# see hhttps://github.com/docker/login-action?tab=readme-ov-file#docker-hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Publish Container Image to DockerHub for version "${{ env.VERSION }}"
run: docker push "$DOCKERHUB_REPO:$VERSION"
- name: Publish Container Image to DockerHub as "latest"
if: ${{ github.event.inputs.prerelease != 'false' }}
run: docker push "$DOCKERHUB_REPO:latest"
# endregion
# region publish to GHCR
- name: Login to GHCR
# see https://github.com/docker/login-action#github-container-registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish Container Image to GHCR for version "${{ env.VERSION }}"
run: docker push "$GHCR_REPO:$VERSION"
- name: Publish Container Image to GHCR as "latest"
if: ${{ github.event.inputs.prerelease != 'false' }}
run: docker push "$GHCR_REPO:latest"
# endregion
# TODO: publish all files in $REPORTS_DIR as release assets - see https://github.com/actions/upload-release-asset
- name: Destroy Container image
# run regardless of outcome
if: ${{ always() }}
run: >
docker rmi -f
"$DOCKERHUB_REPO:$VERSION"
"$DOCKERHUB_REPO:latest"
"$GHCR_REPO:$VERSION"
"$GHCR_REPO:latest"