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
20 changes: 13 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: CI

# CI gates pull requests only. The Codecov baseline for `main` is refreshed
# by coverage-baseline.yml on a schedule, not by a push trigger.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -32,21 +33,26 @@ jobs:
- name: Type-check
run: mypy src/shimkit
- name: Tests
# The coverage floor is [tool.coverage.report] fail_under in
# pyproject.toml, so local runs and CI enforce the same number.
run: |
pytest -q --cov=shimkit \
--cov-report=term \
--cov-report=xml:coverage.xml \
--cov-fail-under=80
--cov-report=xml:coverage.xml
- name: Upload coverage to Codecov
# Only upload from one matrix cell to avoid double-counting.
# Ubuntu 3.12 is the canonical row; macOS / other Pythons
# produce identical coverage modulo platform-gated tests.
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
uses: codecov/codecov-action@v7
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12'
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
files: ./coverage.xml
flags: unit
# Public repos use OIDC token-less upload; no secret required.
disable_search: true
# Uses CODECOV_TOKEN when one is configured; otherwise a tokenless
# upload. Codecov being down or refusing the upload never blocks a
# PR: the local fail_under floor is the gate.
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

security:
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/coverage-baseline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Coverage baseline

# Refreshes the Codecov baseline for `main` so PR comparisons have a recent
# base commit to diff against. Runs weekly (Monday 06:00 America/New_York,
# matching dependabot.yml; 11:00 UTC) and on demand. It replaces the old
# `push: branches: [main]` trigger on ci.yml, which re-ran the full matrix
# after every merge. Scheduled runs always use the default branch.

on:
schedule:
- cron: '0 11 * * 1'
workflow_dispatch:

permissions:
contents: read

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

jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.12'
cache: pip
- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Tests with coverage
run: |
pytest -q --cov=shimkit \
--cov-report=term \
--cov-report=xml:coverage.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
files: ./coverage.xml
flags: unit
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Changed

- CI runs on `pull_request` and `workflow_dispatch` only; the
`push: branches: [main]` trigger is gone. A new
`coverage-baseline.yml` refreshes the Codecov baseline for `main`
weekly (Monday 06:00 America/New_York) and on demand.
- Coverage floor raised from 80% to 84% (measured 84.81%, rounded
down) and moved from the CI command line to
`[tool.coverage.report] fail_under`, so local runs enforce it too.
- `codecov/codecov-action` pinned to the v7.1.1 commit SHA, and it
uses `CODECOV_TOKEN` when one is configured.
- `codecov.yml` added: carryforward flags, a blocking project status
with 1% slack, and an advisory patch status.

### Fixed

- The Codecov upload step never ran. Its condition read
`matrix.python-version`, but the matrix key is `python`.

## [0.19.0] — 2026-05-16

### Added
Expand Down
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ A tool joins shimkit only if it shares ≥2 of `Platform` / `Shell` /
`SHIMKIT_CONFIG=<path>`, then `reset_cache()`. The autouse fixture
resets between tests so leakage isn't a concern.

### Coverage

- The enforced floor is `[tool.coverage.report] fail_under` in
`pyproject.toml`; plain `pytest --cov=shimkit` fails below it, locally
and in CI. It is the measured total rounded down. Raise it when coverage
rises; never lower it to get a PR through.
- Every PR uploads coverage to Codecov from the Ubuntu / Python 3.12 CI
cell. The upload is advisory (`fail_ci_if_error: false`), so a Codecov
outage never blocks a merge.
- `main` has no push trigger. `coverage-baseline.yml` refreshes the
Codecov baseline every Monday (06:00 America/New_York) and on demand
(`gh workflow run coverage-baseline.yml`). `codecov.yml` turns on
carryforward flags so a stale baseline never reads as 0%. GitHub
pauses schedules after 60 days without repository activity; re-enable
it from the Actions tab if that happens.

## Releasing

See [`docs/release.md`](docs/release.md). Releases are
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# shimkit

[![codecov](https://codecov.io/gh/simtabi/shimkit/graph/badge.svg)](https://codecov.io/gh/simtabi/shimkit)

A toolkit of developer utilities. Python tools, shimmed by bash.

```
Expand Down
32 changes: 32 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Codecov configuration. Validate with:
# curl --data-binary @codecov.yml https://codecov.io/validate
codecov:
require_ci_to_pass: true

coverage:
precision: 2
round: down
status:
project:
default:
# Blocking, with 1% slack for platform-gated lines and noise.
target: auto
threshold: 1%
informational: false
patch:
default:
# Reported on every PR, but advisory: the enforced floor is
# fail_under in pyproject.toml.
target: auto
threshold: 5%
informational: true

flag_management:
default_rules:
# A flag missing from an upload inherits its last known value, so a
# stale or missing `main` baseline never reads as 0% coverage.
carryforward: true

comment:
layout: "diff, flags, files"
require_changes: true
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ filterwarnings = [
"ignore::PendingDeprecationWarning",
]

[tool.coverage.run]
source = ["shimkit"]

[tool.coverage.report]
# Local, enforced floor, independent of Codecov. Set to the measured total
# rounded down (CI 2026-10-02: 84.81% ubuntu, 84.87% macos) so it ratchets:
# raise it when coverage rises, never lower it to make a PR pass.
fail_under = 84

[tool.ruff]
line-length = 100
target-version = "py310"
Expand Down
Loading