1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
# This Makefile is only used by developers.
############# Settings ############
# use Bash as shell, not sh
SHELL := bash
# execute makefile in a single bash process instead of one per target
.ONESHELL:
# set Bash flags
.SHELLFLAGS := -eu -o pipefail -c
# remove target files if a rule fails, forces reruns of aborted rules
.DELETE_ON_ERROR:
# warn for undefined variables
MAKEFLAGS += --warn-undefined-variables
# disable builtin default rules
MAKEFLAGS += --no-builtin-rules
############ Configuration ############
VERSION:=$(shell grep "Version:" patoolib/configuration.py | cut -d '"' -f2)
AUTHOR:=$(shell grep "MyName:" patoolib/configuration.py | cut -d '"' -f2)
APPNAME:=$(shell grep "AppName:" patoolib/configuration.py | cut -d '"' -f2)
ARCHIVE_SOURCE:=$(APPNAME)-$(VERSION).tar.gz
ARCHIVE_WHEEL:=$(APPNAME)-$(VERSION)-py2.py3-none-any.whl
GITRELEASETAG:=$(VERSION)
GITUSER:=wummel
GITREPO:=$(APPNAME)
HOMEPAGE:=$(HOME)/public_html/patool-webpage.git
WEBMETA:=doc/web/source/conf.py
CHANGELOG:=doc/changelog.txt
GIT_MAIN_BRANCH:=master
# Pytest options:
# -s: do not capture stdout/stderr (some tests fail otherwise)
# --full-trace: print full stacktrace on keyboard interrupts
# --log-file: write test output to file for easier inspection
# --numprocesses auto: run tests in parallel on available CPU cores (uses pytest-xdist)
PYTESTOPTS?=-s --full-trace --log-file=build/test.log --numprocesses auto
# which test modules to run
TESTS ?= tests/
# set test options
TESTOPTS=
# python files and directories
PY_FILES_DIRS:=setup.py patoolib tests doc/web/source
############ Default target ############
# `make help` displays all targets documented with `##`in the target line
.PHONY: help
help: ## display this help section
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf "\033[36m%-38s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.DEFAULT_GOAL := help
############ Installation and provisioning ############
.PHONY: init
init: ## install python virtual env and required development packages
uv sync
############ Build and release targets ############
.PHONY: clean
clean: ## remove generated python, web page files and all local patool installations
python setup.py clean --all
uv pip uninstall patool
$(MAKE) -C doc/web clean
.PHONY: distclean
distclean: clean ## run clean and additionally remove all build and dist files
rm -rf build dist
rm -f MANIFEST
# clean aborted dist builds and output files
rm -rf $(APPNAME)-$(VERSION) $(APPNAME).egg-info
rm -f *-stamp*
.PHONY: dist
dist: ## build source and wheel distribution file
env SOURCE_DATE_EPOCH=$(shell git log -1 --pretty=%ct) uv build
.PHONY: release-pypi
release-pypi: ## upload a new release to pypi
uv publish dist/$(ARCHIVE_SOURCE) dist/$(ARCHIVE_WHEEL)
# export GITHUB_TOKEN for the gh command
# Generate a fine grained access token with:
# - Restricted to this repository (patool)
# - Repository permission: Metadata -> Read (displayed as "Read access to metadata" in token view)
# - Repository permission: Contents -> Read and write (displayed as "Read and Write access to code" in token view)
# - Expiration in 90 days
# After that run "export GITHUB_TOKEN=<token-content>"
.PHONY: release-gh
release-gh: ## upload a new release to github
gh release create \
--title "Release $(GITRELEASETAG)" \
--notes "See doc/changelog.txt for release notes" \
--latest \
--draft=false \
--prerelease=false \
--verify-tag \
"$(GITRELEASETAG)" \
dist/$(ARCHIVE_SOURCE) \
dist/$(ARCHIVE_WHEEL)
# Make a new release by calling all the distinct steps in the correct order.
# Each step is a separate target so that it's easy to do this manually if
# anything screwed up.
.PHONY: release
release: distclean releasecheck ## release a new version of patool
$(MAKE) dist release-gh release-pypi release-homepage github-issues
.PHONY: releasecheck
releasecheck: update-webmeta checkgit checkchangelog lint test typecheck checkgitreleasetag ## check that repo is ready for release
.PHONY: checkgit
checkgit: ## check that git changes are all committed on the main branch
# check that branch is the main branch
@if [ "$(shell git rev-parse --abbrev-ref HEAD)" != "$(GIT_MAIN_BRANCH)" ]; then \
echo "ERROR: current git branch is not '$(GIT_MAIN_BRANCH)'"; \
git rev-parse --abbrev-ref HEAD; \
false; \
fi
# check for uncommitted versions
@if [ -n "$(shell git status --porcelain --untracked-files=all)" ]; then \
echo "ERROR: uncommitted git changes"; \
git status --porcelain --untracked-files=all; \
false; \
fi
.PHONY: checkgitreleasetag
checkgitreleasetag: ## check release tag for git exists
@if [ -z "$(shell git tag -l -- $(GITRELEASETAG))" ]; then \
echo "ERROR: git tag \"$(GITRELEASETAG)\" does not exist, execute 'git tag -a $(GITRELEASETAG) -m \"$(GITRELEASETAG)\"'"; \
false; \
fi
# check that release tags is pushed to remote
@if ! git ls-remote --exit-code --tags origin $(GITRELEASETAG); then \
echo "ERROR: git tag \"$(GITRELEASETAG)\" does not exist on remote repo, execute 'git push --tags'"; \
fi
.PHONY: github-issues
github-issues: ## close github issues mentioned in changelog
# github-changelog is a local tool which parses the changelog and automatically
# closes issues mentioned in the changelog entries.
cd .. && github-changelog $(GITUSER) $(GITREPO) patool.git/doc/changelog.txt
############ Versioning ############
bumpversion-%: ## shortcut target for bumpversion: bumpversion-{major,minor,patch}
bumpversion $*
$(MAKE) bumpchangelog
bumpchangelog: ## add leading changelog entry for a new version
sed -i '1i$(VERSION) (released xx.xx.xxxx)\n *\n' $(CHANGELOG)
checkchangelog: ## check changelog before release
@if egrep -i "xx\.|xxxx|\.xx" $(CHANGELOG) > /dev/null; then \
echo "Could not release: edit $(CHANGELOG) release date"; false; \
fi
@if ! grep "^$(VERSION)" $(CHANGELOG) > /dev/null; then \
echo "ERROR: Version $(VERSION) missing from $(CHANGELOG)"; \
false; \
fi
@if ! head -n1 $(CHANGELOG) | egrep "^$(VERSION)" >/dev/null; then \
echo "Could not release: different versions in $(CHANGELOG) and setup.py"; \
echo "Version in $(CHANGELOG):"; head -n1 $(CHANGELOG); \
echo "Version in setup.py: $(VERSION)"; false; \
fi
############ Linting and syntax checks ############
.PHONY: lint
lint: ## lint python code
ruff check $(PY_FILES_DIRS)
.PHONY: audit
audit: ## run audit checks
zizmor --config .zizmor.yml .github/workflows/python-package.yml
pip-audit
.PHONY: reformat
reformat: ## format the python code
ruff check --fix $(PY_FILES_DIRS)
ruff format $(PY_FILES_DIRS)
.PHONY: checkoutdated checkoutdated-py checkoutdated-gh
checkoutdated: checkoutdated-py checkoutdated-gh
checkoutdated-py: ## Check for outdated package requirements
# Assumes all packages in requirements have pinned versions with "==".
# Compare the output of "uv pip list" (the current versions) with the result of "uv pip compile" (available versions).
# Then filter only for upgrades of direct dependencies with grep.
# When grep does not find any match, all direct dependencies are uptodate.
# In this case, grep exits with exitcode 1. Test for this after running grep.
@set +e; \
echo "Check for outdated Python packages"; \
uv pip list --format=freeze |sed 's/==.*//' | uv pip compile - --color=never --quiet --no-deps --no-header --no-annotate |diff <(uv pip list --format=freeze) - --side-by-side --suppress-common-lines | \
grep -iE "($(shell grep == pyproject.toml | cut -f1 -d= | tr -d "\"\' "| sed -e 's/\[.*\]//' |sort | paste -sd '|'))"; \
test $$? = 1
checkoutdated-gh: ## check for outdated github projects
# github-check-outdated is a local tool which compares a given version with the latest available github release version
# see https://gist.github.com/wummel/ef14989766009effa4e262b01096fc8c for an example implementation
@echo "Check for outdated Github tools"
github-check-outdated astral-sh uv "$(shell uv --version | cut -f2 -d" ")"
github-check-outdated python cpython v$(shell python --version | cut -f2 -d" ") '^v3\.14\.[0-9]+$$'
.PHONY: upgradeoutdated
upgradeoutdated: upgradeoutdated-gh upgradeoutdated-py
.PHONY: upgradeoutdated-gh
upgradeoutdated-gh:
sed -i -e 's/uv_version_dev = ".*"/uv_version_dev = "$(shell github-check-outdated astral-sh uv 0 | cut -f4 -d" ")"/' pyproject.toml
sed -i -e 's/ version: ".*"/ version: "$(shell github-check-outdated astral-sh uv 0 | cut -f4 -d" ")"/' .github/workflows/python-package.yml
.PHONY: upgradeoutdated-py
upgradeoutdated-py: ## upgrade dependencies in uv.lock
uv lock --upgrade
############ Testing ############
.PHONY: test
test: ## run tests
uv run pytest $(PYTESTOPTS) $(TESTOPTS) $(TESTS)
# Needs https://nektosact.com/ and docker
# Uses https://github.com/catthehacker/docker_images
.PHONY: test-github
test-github: ## run github workflow actions
act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --matrix os:ubuntu-latest
.PHONY: typecheck
typecheck: ## run the ty type checker
ty check
############ Documentation ############
doc/$(APPNAME).txt: doc/$(APPNAME).1 ## make text file from man page for wheel builds
cols=`stty size | cut -d" " -f2`; stty cols 72; man -l $< | sed -e 's/.\cH//g' > $@; stty cols $$cols
.PHONY: count
count: ## print some code statistics
@sloccount patoolib
.PHONY: update-webmeta
update-webmeta: ## update package metadata for the homepage
sed -i -e 's/project =.*/project = "$(APPNAME)"/g' $(WEBMETA)
sed -i -e 's/version =.*/version = "$(VERSION)"/g' $(WEBMETA)
sed -i -e 's/author =.*/author = "$(AUTHOR)"/g' $(WEBMETA)
.PHONY: release-homepage
release-homepage: update-webmeta ## update the homepage after a release
$(MAKE) -C doc/web release
|