Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1717e4c
build: migrate from Poetry to uv with automated releases
seansica Dec 22, 2025
599170d
build: update ruff-pre-commit config
seansica Dec 23, 2025
708e245
style: apply ruff formatting
seansica Dec 23, 2025
0b57c82
Merge branch 'main' into semantic-release
seansica Dec 23, 2025
c6e8807
build: update uv.lock
seansica Dec 23, 2025
5141c5d
build: update read-the-docs config to use uv
seansica Dec 23, 2025
02278d5
ci: small improvements to ci-cd uv build process
seansica Dec 23, 2025
3c496a9
chore(pre-commit): bump commitizen to 4.10.1
seansica Dec 23, 2025
d0494b7
ci: set python version with a matrix
seansica Dec 23, 2025
8dd5a29
chore(ci): upgrade setup-uv to v7
seansica Dec 23, 2025
f981996
build(read-the-docs): migrate to build.jobs
seansica Dec 23, 2025
6e075c2
build: replace hatch build backend with uv
seansica Dec 23, 2025
35c6552
ci: unset git_committer_name and email in PSR step
seansica Dec 23, 2025
634f249
build(just): add upgrade script
seansica Dec 23, 2025
abd4a7b
Apply suggestions from code review
jondricek Dec 24, 2025
1e77de2
chore: add cz-check-good-commit and cz-check-bad-commit scripts to ju…
seansica Feb 13, 2026
5a28257
build(ci): update astral-sh/setup-uv from v5 to v7
seansica Feb 13, 2026
3d43662
build: update uv.lock
seansica Feb 13, 2026
61f4ffd
Merge branch 'main' into semantic-release
seansica Feb 13, 2026
bb955b9
build: remove unused sections from pyproject.toml
seansica Feb 13, 2026
8982a9f
build: add "revert" as permissible conventional commit prefix
seansica Feb 13, 2026
c9ebd39
ci: improve dynamics of setup phase
seansica Feb 13, 2026
a808ca5
ci: update commitlint job to validate the PR title for PRs
seansica Feb 13, 2026
10b1dfb
Merge branch 'semantic-release' of github.com:mitre-attack/mitreattac…
seansica Feb 13, 2026
1a04175
ci: streamline the python setup process
seansica Feb 13, 2026
088f445
ci: streamline the python setup process
seansica Feb 13, 2026
46e9493
ci: enable gh actions to read PR information
seansica Feb 13, 2026
3a49500
ci: split PR linting into separate workflow
seansica Feb 13, 2026
7912993
ci: update download-artifact version
jondricek Feb 13, 2026
5797a2f
docs: update release+contrib documentation
seansica Feb 13, 2026
b14107c
Merge branch 'semantic-release' of github.com:mitre-attack/mitreattac…
seansica Feb 13, 2026
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
192 changes: 192 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
commitlint:
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
with:
fetch-depth: 0

- 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 }}

Comment thread
seansica marked this conversation as resolved.
Outdated
- name: Install dependencies
run: uv sync --all-extras

- name: Check commit messages
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to make sure we make it as easy as possible for external contributors to contribute. This makes me think of this FAQ from the Conventional Commits site.

https://www.conventionalcommits.org/en/v1.0.0/#do-all-my-contributors-need-to-use-the-conventional-commits-specification

Do all my contributors need to use the Conventional Commits specification?
No! If you use a squash based workflow on Git lead maintainers can clean up the commit messages as they’re merged—adding no workload to casual committers. A common workflow for this is to have your git system automatically squash commits from a pull request and present a form for the lead maintainer to enter the proper git commit message for the merge.

I think it might be better to squash commits from pull requests somehow instead and only validate commit messages that we ourselves write, to not make it an overly arduous process for external contribs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. Just pushed a change to (hopefully) enable this:

  # 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

The first step uses an off the shelf GH action, amannn/action-semantic-pull-request@v5 to ensure that the pull request title match the Conventional Commits spec. It only runs on pull_request events.

The latter four steps run on push events, and will check the final/actual commit for pushes to main as a safety measure.

We'll probably want to configure the repo to only allow squash merges (or at least default to them):

  • Settings --> General --> Pull Requests → check only "Allow squash merging" and set the default commit message to "Pull request title".

This will ensure the conventional PR title always becomes the merge commit message that PSR parses.

(We'll of course need to test this.)

uv run cz check --rev-range origin/${{ github.base_ref }}..HEAD
else
uv run cz check --rev-range HEAD~1..HEAD
fi

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 }}

Comment thread
seansica marked this conversation as resolved.
- 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 }}

Comment thread
seansica marked this conversation as resolved.
- 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@v5
with:
version: "latest"

- name: Setup | Install Python
run: uv python install ${{ matrix.python-version }}

Comment thread
seansica marked this conversation as resolved.
- 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
Comment thread
jondricek marked this conversation as resolved.
Outdated
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
60 changes: 0 additions & 60 deletions .github/workflows/lint-publish.yml

This file was deleted.

15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10
hooks:
# Run the linter (disabled for now)
# - id: ruff-check
# args: [--fix]
# Run the formatter
- id: ruff-format

- repo: https://github.com/commitizen-tools/commitizen
rev: v4.10.1
hooks:
- id: commitizen
stages: [commit-msg]
12 changes: 8 additions & 4 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ build:
os: ubuntu-24.04
tools:
python: "3.11"
# For details on how to customize the build process, see:
# https://docs.readthedocs.com/platform/stable/builds.html
# https://docs.readthedocs.com/platform/stable/build-customization.html
jobs:
post_create_environment:
- pip install poetry
post_install:
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs
install:
- pip install uv
- uv sync --extra docs
build:
- uv run sphinx-build -b html docs $READTHEDOCS_OUTPUT/html

sphinx:
configuration: docs/conf.py
56 changes: 56 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Default recipe to list all available commands
default:
@just --list

# Install development dependencies
install:
uv sync --all-extras

Comment thread
seansica marked this conversation as resolved.
# Upgrade all uv dependencies
upgrade:
uv sync --all-extras --upgrade

# Install pre-commit hooks
setup-hooks:
uv run pre-commit install
uv run pre-commit install --hook-type commit-msg

# Run pre-commit hooks on all files
lint:
uv run pre-commit run --all-files

# Run ruff linter
ruff-check:
uv run ruff check

# Run ruff formatter check
ruff-format-check:
uv run ruff format --check

# Run ruff formatter (fix)
ruff-format:
uv run ruff format

# Run tests
test:
uv run pytest

# Run tests with coverage
test-cov:
uv run pytest --cov=mitreattack

# Check commit messages in range (default: last commit)
check-commits rev-range="HEAD~1..HEAD":
uv run cz check --rev-range {{ rev-range }}

# Dry run semantic release (no changes)
release-dry-run:
uv run semantic-release -v --noop version

# Build the package
build:
uv build

# Clean build artifacts
clean:
rm -rf dist/ build/ *.egg-info/
Loading
Loading