Skip to content
Merged
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
55 changes: 0 additions & 55 deletions .github/workflows/publish.yml

This file was deleted.

192 changes: 192 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
name: Release

on:
push:
tags:
- "v*.*.*"

workflow_dispatch:
inputs:
confirm_master:
description: "Type 'master' to confirm release from master branch"
required: true
type: string

jobs:
validate:
name: Validate Release
runs-on: ubuntu-latest

outputs:
version: ${{ steps.version.outputs.version }}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate Branch (Tag Push)
if: github.event_name == 'push'
run: |
TAG_COMMIT=$(git rev-list -n 1 ${{ github.ref_name }})

if ! git merge-base --is-ancestor $TAG_COMMIT origin/master; then
echo "❌ ERROR: Tag ${{ github.ref_name }} is not on master branch"
echo "Tags must be created from commits that are on master."
exit 1
fi

echo "✅ Tag ${{ github.ref_name }} is on master branch"

- name: Validate Branch (Manual Dispatch)
if: github.event_name == 'workflow_dispatch'
run: |
if [ "${{ github.ref_name }}" != "master" ]; then
echo "❌ ERROR: Manual release must be run from master branch"
echo "Current branch: ${{ github.ref_name }}"
exit 1
fi

if [ "${{ inputs.confirm_master }}" != "master" ]; then
echo "❌ ERROR: Confirmation failed"
echo "You must type 'master' to confirm release"
exit 1
fi

echo "✅ Manual release confirmed on master branch"

- name: Extract Version
id: version
run: |
if [ "${{ github.event_name }}" == "push" ]; then
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
else
VERSION=$(grep -oP '^version = "\K[^"]+' pyproject.toml)
fi

echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "📦 Version: $VERSION"

- name: Validate Version Format
run: |
VERSION="${{ steps.version.outputs.version }}"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "❌ ERROR: Invalid version format: $VERSION"
echo "Expected: X.Y.Z or X.Y.Z-suffix"
exit 1
fi
echo "✅ Version format valid: $VERSION"

build:
name: Build Package
runs-on: ubuntu-latest
needs: [validate]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Update Version
run: |
sed -i 's/version = ".*"/version = "${{ needs.validate.outputs.version }}"/' pyproject.toml

- name: Build Package
run: uv build

- name: Verify Package
run: |
echo "📦 Built packages:"
ls -la dist/

- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 5

test-install:
name: Test Install (py${{ matrix.python-version }})
runs-on: ubuntu-latest
needs: [build]

strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]

steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Install Package
run: |
pip install dist/*.whl
python -c "from pyssertive.http import FluentHttpAssertClient; print('✅ pyssertive installed successfully')"

publish-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: [validate, build, test-install]
environment: pypi

permissions:
id-token: write

steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
print-hash: true

github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [validate, publish-pypi]
if: github.event_name == 'push'

permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: Release ${{ needs.validate.outputs.version }}
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
generate_release_notes: true
files: dist/*
117 changes: 105 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,127 @@ name: Test

on:
push:
branches:
- master
branches: [master]
pull_request:
branches: [master]
types: [opened, synchronize, reopened]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install dependencies
run: uv sync --all-extras

- name: Check formatting
run: uv run ruff format --check src/ tests/

- name: Check linting
run: uv run ruff check src/ tests/

typecheck:
name: Type Check
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install dependencies
run: uv sync --all-extras

- name: Run mypy
run: uv run mypy src/pyssertive
continue-on-error: true

test:
name: Test (py${{ matrix.python-version }}, dj${{ matrix.django-version }})
runs-on: ubuntu-latest
needs: [lint]

strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.12', '3.13']
include:
- python-version: "3.11"
django-version: "4.2"
- python-version: "3.12"
django-version: "5.0"
- python-version: "3.13"
django-version: "5.1"

steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Install dependencies
run: uv sync --all-extras
run: |
uv sync --all-extras
uv pip install "Django~=${{ matrix.django-version }}.0"

- name: Lint
run: uv run ruff check src/ tests/
- name: Run tests
run: uv run pytest --cov=pyssertive --cov-report=term-missing --cov-fail-under=100

ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [lint, typecheck, test]
if: always()

steps:
- name: Check all jobs
run: |
echo "===== Job Results ====="
echo "Lint: ${{ needs.lint.result }}"
echo "Typecheck: ${{ needs.typecheck.result }}"
echo "Test: ${{ needs.test.result }}"
echo "======================="

if [ "${{ needs.lint.result }}" != "success" ]; then
echo "❌ Lint failed"
exit 1
fi

if [ "${{ needs.test.result }}" != "success" ]; then
echo "❌ Tests failed"
exit 1
fi

if [ "${{ needs.typecheck.result }}" == "failure" ]; then
echo "⚠️ Typecheck failed (non-blocking)"
fi

- name: Test
run: uv run pytest
echo "✅ All required checks passed!"
Loading
Loading