Skip to content

Migrate to uv and integrate PSR for automated releases #9

Migrate to uv and integrate PSR for automated releases

Migrate to uv and integrate PSR for automated releases #9

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
# For PRs, validate the PR title (which becomes the squash commit message).
# This avoids burdening external contributors with conventional commit enforcement
# on every individual commit. See: https://www.conventionalcommits.org/en/v1.0.0/
#
# For pushes to main, validate the final commit message (the squash merge commit)
# using commitizen as a safety net.
commitlint:
runs-on: ubuntu-latest
steps:
# PR: validate PR title follows conventional commit format
- name: Validate PR title
if: github.event_name == 'pull_request'
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# For work-in-progress PRs you can typically use draft pull requests from GitHub.
# This action allows us to use the special "[WIP]" prefix to indicate a draft state
# without actually flagging the PR as a draft.
# Example:
# `[WIP] feat: Add support for Node.js 18` <--- will not be validated!
wip: true
# Push to main: validate the squash merge commit message directly
- name: Checkout
if: github.event_name == 'push'
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: Install uv
if: github.event_name == 'push'
uses: astral-sh/setup-uv@v7
with:
version: "latest"
- name: Install Python and dependencies
if: github.event_name == 'push'
run: |
uv python install 3.11
uv sync --all-extras
- name: Validate commit message
if: github.event_name == 'push'
run: uv run cz check --rev-range HEAD~1..HEAD
lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"] # TODO see https://github.com/mitre-attack/mitreattack-python/issues/176
steps:
- uses: actions/checkout@v6
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --all-extras
- name: Lint with ruff
run: uv run ruff check --output-format github
- name: Check formatting with ruff
run: uv run ruff format --check
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"] # TODO see https://github.com/mitre-attack/mitreattack-python/issues/176
steps:
- uses: actions/checkout@v6
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --all-extras
- name: Run pytest
run: uv run pytest --cov=mitreattack
release:
needs: [commitlint, lint, test]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
matrix:
python-version: ["3.11"] # TODO see https://github.com/mitre-attack/mitreattack-python/issues/176
runs-on: ubuntu-latest
# The concurrency block prevents multiple release jobs from running simultaneously for the same branch.
# This is particularly useful for releases since you typically want sequential deployments (finish
# current release before starting next) rather than canceling in-progress releases or running them
# in parallel.
concurrency:
# Creates a concurrency group keyed by workflow name + "release" + branch name (e.g., "Continuous Delivery-release-main")
group: ${{ github.workflow }}-release-${{ github.ref_name }}
# If a release is already running and a new push triggers another, the new job waits rather than canceling the running one
# If you changed cancel-in-progress to true, a new push would cancel the currently running release job.
cancel-in-progress: false
permissions:
contents: write
steps:
# Note: We checkout the repository at the branch that triggered the workflow.
# Python Semantic Release will automatically convert shallow clones to full clones
# if needed to ensure proper history evaluation. However, we forcefully reset the
# branch to the workflow sha because it is possible that the branch was updated
# while the workflow was running, which prevents accidentally releasing un-evaluated
# changes.
- name: Setup | Checkout Repository on Release Branch
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.ref_name }}
- name: Setup | Force release branch to be at workflow sha
run: |
git reset --hard ${{ github.sha }}
- name: Setup | Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
- name: Setup | Install Python
run: uv python install ${{ matrix.python-version }}
- name: Setup | Install dependencies
run: uv sync --all-extras
- name: Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@v10.5.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# NOTE: git_committer_name and git_committer_email are optional
# We omit them because, if set, they must be associated with the provided token
# and we don't really care to have a specific committer for automated releases.
- name: Upload to GitHub Release Assets
uses: python-semantic-release/publish-action@v10.5.3
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
- name: Upload distribution artifacts
uses: actions/upload-artifact@v6
if: steps.release.outputs.released == 'true'
with:
name: distribution-artifacts
path: dist
if-no-files-found: error
outputs:
released: ${{ steps.release.outputs.released || 'false' }}
publish:
# 1. Separate out the deploy step from the publish step to run each step at
# the least amount of token privilege
# 2. Also, deployments can fail, and its better to have a separate job if you need to retry
# and it won't require reversing the release.
needs: release
if: needs.release.outputs.released == 'true'
runs-on: ubuntu-latest
environment: release
permissions:
contents: read
id-token: write
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
id: artifact-download
with:
name: distribution-artifacts
path: dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
print-hash: true
verbose: true