From 829845c0262fa3485184e1b0dfbdff71be05fa8c Mon Sep 17 00:00:00 2001 From: mhucka Date: Thu, 24 Mar 2022 13:28:50 -0700 Subject: [PATCH] Revamp Makefile based on what I'm using elsewhere --- Makefile | 225 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 161 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 5548e1c..d0d072a 100644 --- a/Makefile +++ b/Makefile @@ -1,54 +1,117 @@ # ============================================================================= # @file Makefile # @brief Makefile for some steps in creating new releases on GitHub -# @author Michael Hucka -# @date 2020-08-11 +# @date 2021-10-16 # @license Please see the file named LICENSE in the project directory -# @website https://github.com/caltechlibrary/eprints2bags +# @website https://github.com/caltechlibrary/commonpy # ============================================================================= +.ONESHELL: # Run all commands in the same shell. +.SHELLFLAGS += -e # Exit at the first error. + # Before we go any further, test if certain programs are available. # The following is based on the approach posted by Jonathan Ben-Avraham to # Stack Overflow in 2014 at https://stackoverflow.com/a/25668869 -PROGRAMS_NEEDED = curl gh git jq sed +PROGRAMS_NEEDED = curl gh git jq sed pyinstaller TEST := $(foreach p,$(PROGRAMS_NEEDED),\ $(if $(shell which $(p)),_,$(error Cannot find program "$(p)"))) -# Gather values that we need further below. - -$(info Gathering data -- this takes a few moments ...) +# Set some basic variables. These are quick to set; we set additional +# variables using "set-vars" but only when the others are needed. -name := $(strip $(shell grep -m 1 'name\s*=' setup.cfg | cut -f2 -d'=')) -version := $(strip $(shell grep -m 1 'version\s*=' setup.cfg | cut -f2 -d'=')) -url := $(strip $(shell grep -m 1 'url\s*=' setup.cfg | cut -f2 -d'=')) -desc := $(strip $(shell grep -m 1 'description\s*=' setup.cfg | cut -f2 -d'=')) -author := $(strip $(shell grep -m 1 'author\s*=' setup.cfg | cut -f2 -d'=')) -email := $(strip $(shell grep -m 1 'author_email\s*=' setup.cfg | cut -f2 -d'=')) -license := $(strip $(shell grep -m 1 'license\s*=' setup.cfg | cut -f2 -d'=')) - -branch := $(shell git rev-parse --abbrev-ref HEAD) -repo := $(strip $(shell gh repo view | head -1 | cut -f2 -d':')) -id := $(shell curl -s https://api.github.com/repos/$(repo) | jq '.id') -id_url := https://data.caltech.edu/badge/latestdoi/$(id) -doi_url := $(shell curl -sILk $(id_url) | grep Locat | cut -f2 -d' ') -doi := $(subst https://doi.org/,,$(doi_url)) -doi_tail := $(lastword $(subst ., ,$(doi))) +name := $(strip $(shell awk -F "=" '/^name/ {print $$2}' setup.cfg)) +version := $(strip $(shell awk -F "=" '/^version/ {print $$2}' setup.cfg)) +url := $(strip $(shell awk -F "=" '/^url/ {print $$2}' setup.cfg)) +desc := $(strip $(shell awk -F "=" '/^description / {print $$2}' setup.cfg)) +author := $(strip $(shell awk -F "=" '/^author / {print $$2}' setup.cfg)) +email := $(strip $(shell awk -F "=" '/^author_email/ {print $$2}' setup.cfg)) +license := $(strip $(shell awk -F "=" '/^license / {print $$2}' setup.cfg)) init_file := $(name)/__init__.py -tmp_file := $(shell mktemp /tmp/release-notes-$(name).XXXXXX) +branch := $(shell git rev-parse --abbrev-ref HEAD) -$(info Gathering data ... Done.) -# The main action is "make release". +# Print help if no command is given ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +help: + @echo 'Available commands:' + @echo '' + @echo 'make' + @echo 'make help' + @echo ' Print this summary of available commands.' + @echo '' + @echo 'make report' + @echo ' Print variables set in this Makefile from various sources.' + @echo ' This is useful to verify the values that have been parsed.' + @echo '' + @echo 'make release' + @echo ' Do a release on GitHub. This will push changes to GitHub,' + @echo ' open an editor to let you edit release notes, and run' + @echo ' "gh release create" followed by "gh release upload".' + @echo ' Note: this will NOT upload to PyPI, nor create binaries.' + @echo '' + @echo 'make update-doi' + @echo ' Update the DOI inside the README.md file.' + @echo ' This is only to be done after doing a "make release".' + @echo '' + @echo 'make packages' + @echo ' Create the distribution files for PyPI.' + @echo ' Do this manually to check that everything looks okay before.' + @echo ' After doing this, do a "make test-pypi".' + @echo '' + @echo 'make test-pypi' + @echo ' Upload distribution to test.pypi.org.' + @echo ' Do this before doing "make pypi" for real.' + @echo '' + @echo 'make pypi' + @echo ' Upload distribution to pypi.org.' + @echo '' + @echo 'make clean' + @echo ' Clean up various files generated by this Makefile.' + + +# Gather values that we need ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.SILENT: vars +vars: + $(info Gathering data -- this takes a few moments ...) + $(eval repo := $(strip $(shell gh repo view | head -1 | cut -f2 -d':'))) + $(eval api_url := https://api.github.com) + $(eval id := $(shell curl -s $(api_url)/repos/$(repo) | jq '.id')) + $(eval id_url := https://data.caltech.edu/badge/latestdoi/$(id)) + $(eval doi_url := $(shell curl -sILk $(id_url) | grep Locat | cut -f2 -d' ')) + $(eval doi := $(subst https://doi.org/,,$(doi_url))) + $(eval doi_tail := $(lastword $(subst ., ,$(doi)))) + $(info Gathering data -- this takes a few moments ... Done.) + +report: vars + @echo name = $(name) + @echo version = $(version) + @echo url = $(url) + @echo desc = $(desc) + @echo author = $(author) + @echo email = $(email) + @echo license = $(license) + @echo branch = $(branch) + @echo repo = $(repo) + @echo id = $(id) + @echo id_url = $(id_url) + @echo doi_url = $(doi_url) + @echo doi = $(doi) + @echo doi_tail = $(doi_tail) + @echo init_file = $(init_file) + + +# make release ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ release: | test-branch release-on-github print-instructions -test-branch: +test-branch: vars ifneq ($(branch),main) - $(error Current git branch != main. Merge changes into main first) + $(error Current git branch != main. Merge changes into main first!) endif -update-init-file:; +update-init: vars @sed -i .bak -e "s|^\(__version__ *=\).*|\1 '$(version)'|" $(init_file) @sed -i .bak -e "s|^\(__description__ *=\).*|\1 '$(desc)'|" $(init_file) @sed -i .bak -e "s|^\(__url__ *=\).*|\1 '$(url)'|" $(init_file) @@ -56,58 +119,92 @@ update-init-file:; @sed -i .bak -e "s|^\(__email__ *=\).*|\1 '$(email)'|" $(init_file) @sed -i .bak -e "s|^\(__license__ *=\).*|\1 '$(license)'|" $(init_file) -update-codemeta-file:; +update-meta: vars @sed -i .bak -e "/version/ s/[0-9].[0-9][0-9]*.[0-9][0-9]*/$(version)/" codemeta.json -edited := codemeta.json $(init_file) +update-citation: vars + $(eval date := $(shell date "+%F")) + @sed -i .bak -e "/^date-released/ s/[0-9][0-9-]*/$(date)/" CITATION.cff + @sed -i .bak -e "/^version/ s/[0-9].[0-9][0-9]*.[0-9][0-9]*/$(version)/" CITATION.cff + +edited := codemeta.json $(init_file) CITATION.cff -check-in-updated-files:; +commit-updates: vars git add $(edited) - git diff-index --quiet HEAD $(edited) || git commit -m"Update version" $(edited) + git diff-index --quiet HEAD $(edited) || \ + git commit -m"Update stored version number" $(edited) -release-on-github: | update-init-file update-codemeta-file check-in-updated-files +release-on-github: | vars update-init update-meta update-citation commit-updates + $(eval tmp_file := $(shell mktemp /tmp/release-notes-$(name).XXXX)) git push -v --all git push -v --tags - $(info ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓) - $(info ┃ Write release notes in the file that will be opened in your editor ┃) - $(info ┃ then save and close the file to complete this release process. ┃) - $(info ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛) + $(info ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓) + $(info ┃ Write release notes in the file that gets opened in your ┃) + $(info ┃ editor. Close the editor to complete the release process. ┃) + $(info ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛) sleep 2 $(EDITOR) $(tmp_file) gh release create v$(version) -t "Release $(version)" -F $(tmp_file) -print-instructions:; - $(info ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓) - $(info ┃ Next steps: ┃) +print-instructions: vars + $(info ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓) + $(info ┃ Next steps: ┃) $(info ┃ 1. Visit https://github.com/$(repo)/releases ) - $(info ┃ 2. Double-check the draft release, and click "Publish" if ready ┃) - $(info ┃ 3. Wait a few seconds to let web services do their work ┃) - $(info ┃ 4. Run "make update-doi" to update the DOI in README.md ┃) - $(info ┃ 5. Run "make create-dist" and check the distribution for problems ┃) - $(info ┃ 6. Run "make test-pypi" to push to test.pypi.org ┃) - $(info ┃ 7. Double-check https://test.pypi.org/$(repo) ) - $(info ┃ 8. Run "make pypi" to push to pypi for real ┃) - $(info ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛) + $(info ┃ 2. Check the release ┃) + $(info ┃ 3. Wait a few seconds to let web services do their work ┃) + $(info ┃ 4. Run "make update-doi" to update the DOI in README.md ┃) + $(info ┃ 5. Run "make packages" and check the results ┃) + $(info ┃ 6. Run "make test-pypi" to push to test.pypi.org ┃) + $(info ┃ 7. Check https://test.pypi.org/$(repo) ) + $(info ┃ 8. Run "make pypi" to push to pypi for real ┃) + $(info ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛) @echo "" -update-doi: - sed -i .bak -e 's|DOI-.*-blue|DOI-$(doi)-blue|' README.md - sed -i .bak -e 's|caltech.edu/records/[0-9]\{1,\}|caltech.edu/records/$(doi_tail)|' README.md - git add README.md - git diff-index --quiet HEAD README.md || git commit -m"Update DOI" README.md && git push -v --all - -create-dist: clean +update-doi: vars + sed -i .bak -e 's|/api/record/[0-9]\{1,\}|/api/record/$(doi_tail)|' README.md + sed -i .bak -e 's|edu/records/[0-9]\{1,\}|edu/records/$(doi_tail)|' README.md + sed -i .bak -e '/doi:/ s|10.22002/[0-9]\{1,\}|10.22002/$(doi_tail)|' CITATION.cff + git add README.md CITATION.cff + git diff-index --quiet HEAD README.md || \ + (git commit -m"Update DOI" README.md && git push -v --all) + git diff-index --quiet HEAD CITATION.cff || \ + (git commit -m"Update DOI" CITATION.cff && git push -v --all) + +packages: | vars clean python3 setup.py sdist bdist_wheel - python3 -m twine check dist/* + python3 -m twine check dist/$(name)-$(version).tar.gz + python3 -m twine check dist/$(name)-$(version)-py3-none-any.whl + +test-pypi: packages + python3 -m twine upload --repository testpypi dist/$(name)-$(version)*.{whl,gz} + +pypi: packages + python3 -m twine upload dist/$(name)-$(version)*.{gz,whl} + + +# Cleanup and miscellaneous directives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +clean: clean-dist clean-build clean-release clean-other + +really-clean: clean really-clean-dist + +clean-dist: vars + -rm -fr dist/$(name) dist/$(name).zip dist/*.tar.gz dist/*.whl \ + dist/$(name)-${version}-macos-python3.*{,.zip} + +really-clean-dist:; + -rm -fr dist -test-pypi: create-dist - python3 -m twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/* +clean-build:; + -rm -rf build -pypi: create-dist - python3 -m twine upload --verbose dist/* +clean-release: vars + -rm -rf $(name).egg-info codemeta.json.bak $(init_file).bak README.md.bak -clean:; - -rm -rf dist build $(name).egg-info +clean-other: vars + -rm -fr __pycache__ $(name)/__pycache__ .eggs + -rm -rf .cache -.PHONY: release release-on-github update-init-file update-codemeta-file \ - print-instructions create-dist clean test-pypi pypi +.PHONY: release release-on-github update-init update-meta \ + vars print-instructions update-doi packages test-pypi pypi clean \ + clean-dist really-clean-dist clean-build clean-release clean-other