File: Makefile

package info (click to toggle)
patool 4.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,400 kB
  • sloc: python: 5,517; makefile: 177; sh: 122; vhdl: 1
file content (258 lines) | stat: -rw-r--r-- 9,732 bytes parent folder | download
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