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
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "2.6.0"
current_version = "2.6.1"
commit = false
tag = false

Expand Down
45 changes: 16 additions & 29 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
name: Checks
on: [push, pull_request]
name: Pull request checks
on:
pull_request:
workflow_call:
permissions:
contents: read
jobs:
python:

bumpversion-check:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
name: Validate .bumpversion.toml
steps:
- uses: actions/checkout@v6
- uses: pvarki/config-ci-library/.github/actions/validate-bumpversion@main

python-qa:
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -21,7 +32,9 @@ jobs:
- run: uv run --locked pytest
- run: uv run --locked bandit -r src --skip B101
- run: uv build

containers:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -34,29 +47,3 @@ jobs:
env:
VARIANTS: ${{ matrix.variant }}
TARGETS: ${{ matrix.target }}
tag:
needs: [python, containers]
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
permissions:
contents: write
concurrency:
group: version-tag
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Tag package version
shell: bash
run: |
set -euo pipefail
version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
git check-ref-format "refs/tags/$version"
existing=$(git ls-remote --tags origin "refs/tags/$version")
if [ -n "$existing" ]; then
echo "Tag $version already exists; leaving it unchanged."
exit 0
fi
git push origin "HEAD:refs/tags/$version"
46 changes: 46 additions & 0 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Master checks and release
on:
push:
branches: [master]
permissions:
contents: read
jobs:
checks:
uses: ./.github/workflows/check.yml
release:
needs: checks
runs-on: ubuntu-latest
permissions:
contents: write
concurrency:
group: version-release
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Tag package version
shell: bash
run: |
set -euo pipefail
version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
git check-ref-format "refs/tags/$version"
existing=$(git ls-remote --tags origin "refs/tags/$version")
if [ -n "$existing" ]; then
echo "Tag $version already exists; leaving it unchanged."
exit 0
fi
git push origin "HEAD:refs/tags/$version"
- name: Create GitHub release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
if gh release view "$version" > /dev/null 2>&1; then
echo "Release $version already exists; leaving it unchanged."
exit 0
fi
gh release create "$version" --verify-tag --generate-notes --title "$version"
12 changes: 7 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,13 @@ those files and ``uv.lock`` together. It does not automatically commit, tag,
publish or push. Builds retain ``LICENSE`` and ``py.typed`` and exclude the
local, untracked ``HANDOFF.md`` from source distributions.

After a merge or direct push to ``main``, successful Python and container checks
automatically create a Git tag matching ``project.version`` (for example,
``2.6.0``). Existing tags are left unchanged, so bump the version before merging
a new release. The tagging job uses ``GITHUB_TOKEN`` with ``contents: write``;
it does not publish packages or create GitHub releases.
Pull requests run Python and container checks without release permissions.
After a merge or direct push to ``master``, a separate workflow runs the same
checks, then creates a Git tag and GitHub release matching ``project.version``
(for example, ``2.6.0``), with generated release notes. Existing tags and releases
are left unchanged, so bump the version before merging a new release. A missing
release is created even if its tag already exists. The release job uses
``GITHUB_TOKEN`` with ``contents: write``; it does not publish packages.

Containers
----------
Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "scpi"
version = "2.6.0"
version = "2.6.1"
description = "Transport-independent SCPI command sender/parser and device base classes"
authors = [{name = "Eero af Heurlin", email = "eero.afheurlin@iki.fi"}]
license = "LGPL-2.1-or-later"
Expand All @@ -17,14 +17,14 @@ Repository = "https://github.com/rambo/python-scpi/"

[dependency-groups]
dev = [
"pytest>=8.3,<9.0",
"coverage>=7.8,<8.0",
"pytest-cov>=6.1,<7.0",
"pytest>=9.0.3,<10.0",
"coverage>=7.16.2,<8.0",
"pytest-cov>=7.1,<8.0",
"ruff>=0.12,<1.0",
"bandit>=1.8,<2.0",
"pyrefly>=1.3,<2.0",
"prek>=0.2,<1.0",
"pytest-asyncio>=0.21,<1.0",
"pytest-asyncio>=1.4,<2.0",
"bump-my-version>=1.0,<2.0",
"detect-secrets>=1.5,<2.0",
"types-pyserial>=3.5,<4.0",
Expand Down
2 changes: 1 addition & 1 deletion src/scpi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""SCPI module, the scpi class implements the base command set, devices may extend it.
transports are separate from devices (so you can use for example hp6632b with either serial port or GPIB)"""

__version__ = "2.6.0" # NOTE Use `uv run --locked bump-my-version bump patch` to bump versions correctly
__version__ = "2.6.1" # NOTE Use `uv run --locked bump-my-version bump patch` to bump versions correctly
from .errors import CommandError
from .scpi import SCPIDevice, SCPIProtocol

Expand Down
2 changes: 1 addition & 1 deletion tests/test_scpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

def test_version() -> None:
"""Make sure version matches expected"""
assert __version__ == "2.6.0"
assert __version__ == "2.6.1"
50 changes: 48 additions & 2 deletions tests/test_version_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import pytest
import yaml

WORKFLOW = Path(__file__).parents[1] / ".github/workflows/check.yml"
WORKFLOW = Path(__file__).parents[1] / ".github/workflows/master.yml"


def test_version_tag(tmp_path: Path) -> None:
"""Create a tag, preserve it on reruns/new commits, and fail on remote errors."""
workflow = yaml.safe_load(WORKFLOW.read_text())
script = workflow["jobs"]["tag"]["steps"][-1]["run"]
script = workflow["jobs"]["release"]["steps"][-2]["run"]
remote = tmp_path / "remote.git"
checkout = tmp_path / "checkout"
checkout.mkdir()
Expand Down Expand Up @@ -46,3 +46,49 @@ def command(*args: str) -> str:
command(git, "remote", "set-url", "origin", str(tmp_path / "missing.git"))
with pytest.raises(subprocess.CalledProcessError):
command(bash, "-c", script)


def test_version_release(tmp_path: Path) -> None:
"""Create a missing release, preserve it on reruns, and propagate CLI failures."""
workflow = yaml.safe_load(WORKFLOW.read_text())
script = workflow["jobs"]["release"]["steps"][-1]["run"]
bash = shutil.which("bash")
assert bash is not None
(tmp_path / "pyproject.toml").write_text('[project]\nversion = "2.6.0"\n')
gh = tmp_path / "gh"
gh.write_text(
"#!/bin/sh\n"
'if [ "$FAIL_GH" = "1" ]; then exit 2; fi\n'
'case "$1 $2" in\n'
' "release view") test -f release.txt ;;\n'
' "release create") printf "%s\\n" "$@" > release.txt ;;\n'
" *) exit 2 ;;\n"
"esac\n"
)
gh.chmod(0o755)
env = {
**os.environ,
"PATH": f"{tmp_path}{os.pathsep}{Path(sys.executable).parent}{os.pathsep}{os.environ['PATH']}",
"FAIL_GH": "0",
}

def run() -> str:
return subprocess.check_output([bash, "-c", script], cwd=tmp_path, env=env, text=True) # nosec B603

run()
release = tmp_path / "release.txt"
assert release.read_text().splitlines() == [
"release",
"create",
"2.6.0",
"--verify-tag",
"--generate-notes",
"--title",
"2.6.0",
]
release.write_text("existing release")
assert "already exists" in run()
assert release.read_text() == "existing release"
env["FAIL_GH"] = "1"
with pytest.raises(subprocess.CalledProcessError):
run()
Loading
Loading