diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b351985..3d682c8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 3.10.1 (2024.11.20) + +* Migrate large parts of release process to Makefile + # 3.10.0 (2024.11.10) * Upgrade to Symfony 6.4.14 (#659) diff --git a/Makefile b/Makefile index 931e4fdd..d645ac38 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,18 @@ -GIT_VERSION := $(shell git --no-pager describe --tags --always) -GIT_COMMIT := $(shell git rev-parse --verify HEAD) -GIT_DATE := $(firstword $(shell git --no-pager show --date=iso-strict --format="%ad" --name-only)) -BUILD_DATE := $(shell date) -RELEASE_FILE := userli-${GIT_VERSION}.tar.gz -SHA_ALGORITHMS := 256 512 -PWD_NAME := $(shell basename $(shell pwd)) +APP_NAME:= $(notdir $(CURDIR)) +RELEASE_FILE:=$(APP_NAME)-$(VERSION).tar.gz +SHA_ALGORITHMS:=256 512 +PWD_NAME:=$(shell basename $(shell pwd)) -clean: - git reset --hard - git clean --force -d +# Release variables +VERSION_CHANGELOG:=$(shell sed -ne 's/^\#\s\([0-9\.]\+\)\s.*$$/\1/p' CHANGELOG.md | head -n1) +DATE_CHANGELOG:=$(shell sed -n "s/# $(VERSION) (\(.*\))/\1/p" CHANGELOG.md) +TODAY:=$(shell date +%Y.%m.%d) + +# Yarn tool +YARN:=$(shell command -v yarn) +ifndef YARN + YARN:=$(shell command -v yarnpkg) +endif vendors: composer install @@ -28,34 +32,105 @@ security-check: vendors bin/local-php-security-checker integration: vendors lint - yarn - yarn encore dev + $(YARN) + $(YARN) encore dev bin/behat -f progress prepare: mkdir -p build -release: clean prepare - APP_ENV=prod composer install --no-dev --ignore-platform-reqs +build-checks: + @if [ -n "$$(git status --porcelain)" ]; then \ + echo "Git repo not clean!"; \ + exit 1; \ + fi +ifndef VERSION + $(error Missing $$VERSION) +endif +ifneq ($(VERSION),$(VERSION_CHANGELOG)) + $(error Version missmatch between $$VERSION $(VERSION) version in CHANGELOG.md $(VERSION_CHANGELOG)) +endif +ifneq ($(DATE_CHANGELOG),$(TODAY)) + $(error Release date ($(DATE_CHANGELOG)) is not today ($(TODAY))) +endif + +build: build-checks prepare + APP_ENV=prod composer install --no-dev --ignore-platform-reqs --no-scripts APP_ENV=prod composer dump-autoload - yarn --pure-lockfile - yarn encore production + $(YARN) --pure-lockfile + $(YARN) encore production # Create a release tarball - tar --exclude='${PWD_NAME}/.env.*' --exclude='${PWD_NAME}/.git*' \ - --exclude='${PWD_NAME}/.*.yml' --exclude='${PWD_NAME}/behat.yml' \ - --exclude='${PWD_NAME}/bin/github-release.sh' \ - --exclude='${PWD_NAME}/build' --exclude='${PWD_NAME}/features' \ - --exclude='${PWD_NAME}/Makefile' --exclude='${PWD_NAME}/node_modules' \ - --exclude='${PWD_NAME}/phpunit.xml' --exclude='${PWD_NAME}/tests' \ - --exclude='${PWD_NAME}/ansible' --exclude='${PWD_NAME}/var/cache/*' \ - --exclude='${PWD_NAME}/var/log/*' --exclude='${PWD_NAME}/webpack.config.js' \ - --exclude='${PWD_NAME}/yarn.lock' --exclude='${PWD_NAME}/Vagrantfile' \ + tar --exclude='${PWD_NAME}/.env.*' \ + --exclude='${PWD_NAME}/.git*' \ + --exclude='${PWD_NAME}/.idea' \ + --exclude='${PWD_NAME}/.php-cs-fixer.cache' \ + --exclude='${PWD_NAME}/.phpunit*' \ + --exclude='${PWD_NAME}/.vagrant' \ + --exclude='${PWD_NAME}/.*.yml' \ + --exclude='${PWD_NAME}/ansible' \ + --exclude='${PWD_NAME}/behat.yml' \ + --exclude='${PWD_NAME}/bin/behat*' \ + --exclude='${PWD_NAME}/bin/crypt-gpg-pinentry' \ + --exclude='${PWD_NAME}/bin/doctrine*' \ + --exclude='${PWD_NAME}/bin/github-release.sh' \ + --exclude='${PWD_NAME}/bin/local-php-security-checker' \ + --exclude='${PWD_NAME}/bin/patch-type-declarations' \ + --exclude='${PWD_NAME}/bin/php*' \ + --exclude='${PWD_NAME}/bin/rector' \ + --exclude='${PWD_NAME}/bin/simple-phpunit' \ + --exclude='${PWD_NAME}/bin/sql-formatter' \ + --exclude='${PWD_NAME}/bin/uaparser' \ + --exclude='${PWD_NAME}/bin/var-dump-server' \ + --exclude='${PWD_NAME}/bin/yaml-lint' \ + --exclude='${PWD_NAME}/build' \ + --exclude='${PWD_NAME}/composer.*' \ + --exclude='${PWD_NAME}/features' \ + --exclude='${PWD_NAME}/Makefile' \ + --exclude='${PWD_NAME}/mkdocs.yml' \ + --exclude='${PWD_NAME}/node_modules' \ + --exclude='${PWD_NAME}/package.json' \ + --exclude='${PWD_NAME}/phpunit.xml' \ + --exclude='${PWD_NAME}/rector.php' \ + --exclude='${PWD_NAME}/requirements.yml' \ + --exclude='${PWD_NAME}/sonar-project.properties' \ + --exclude='${PWD_NAME}/symfony.lock' \ + --exclude='${PWD_NAME}/tests' \ + --exclude='${PWD_NAME}/Vagrantfile' \ + --exclude='${PWD_NAME}/var/cache/*' \ + --exclude='${PWD_NAME}/var/db_test.sqlite' \ + --exclude='${PWD_NAME}/var/log/*' \ + --exclude='${PWD_NAME}/vendor/bin/.phpunit' \ + --exclude='${PWD_NAME}/webpack.config.js' \ + --exclude='${PWD_NAME}/yarn.lock' \ -czf build/${RELEASE_FILE} ../${PWD_NAME} # Generate SHA hash sum files for sha in ${SHA_ALGORITHMS}; do \ shasum -a "$${sha}" "build/${RELEASE_FILE}" >"build/${RELEASE_FILE}.sha$${sha}"; \ done +release-checks: +ifndef GITHUB_API_TOKEN + $(error Missing $$GITHUB_API_TOKEN) +endif +ifndef GPG_SIGN_KEY + $(error Missing $$GPG_SIGN_KEY) +endif + @if git tag | grep -qFx $(VERSION); then \ + echo "Git tag already exists!"; \ + echo "Delete it with 'git tag -d $(VERSION)'"; \ + exit 1; \ + fi + +# Publish to Github +release: release-checks + # sign release tarball + gpg -u $(GPG_SIGN_KEY) --output "build/$(RELEASE_FILE).asc" --armor --detach-sign --batch --yes build/$(RELEASE_FILE) + # git tag and push + git tag $(VERSION) -m "Release $(VERSION)" + git push origin "refs/tags/$(VERSION)" + # create Github release via shell script (`gh` doesn't have multi-account support) + VERSION=$(VERSION) GITHUB_API_TOKEN=$(GITHUB_API_TOKEN) ./bin/github-release.sh + reset: clean rm -f php_cs.cache rm -rf node-modules diff --git a/bin/github-release.sh b/bin/github-release.sh index d9e05e61..b99ea2e3 100755 --- a/bin/github-release.sh +++ b/bin/github-release.sh @@ -2,20 +2,12 @@ set -e -# configuration -vagrant="yes" # run build process in vagrant - # idea taken from the following projects: # * https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 # * https://github.com/NicoHood/gpgit -if [ -z "$1" ]; then - printf "Error: release version required as first argument\n" >&2 - exit 1 -fi - -if [ -z "$GPG_SIGN_KEY" ]; then - printf "Error: environment variable \$GPG_SIGN_KEY needs to be set\n" >&2 +if [ -z "$VERSION" ]; then + printf "Error: environment variable \$VERSION needs to be set\n" >&2 exit 1 fi @@ -24,78 +16,36 @@ if [ -z "$GITHUB_API_TOKEN" ]; then exit 1 fi -# set release variables -version="$1" -today="$(date +%Y.%m.%d)" - # set Github variables gh_group="systemli" gh_project="userli" gh_api="https://api.github.com" gh_repo="$gh_api/repos/${gh_group}/${gh_project}" -gh_tags="$gh_repo/releases/tags/$version" +gh_tags="$gh_repo/releases/tags/$VERSION" gh_auth="Authorization: token $GITHUB_API_TOKEN" -curl_args="--location --remote-header-name --remote-name #" # parse CHANGELOG.md -if ! grep -qx "# $version (.*)" CHANGELOG.md; then - printf "Error: Couldn't find section for version %s in CHANGELOG.md\n" "$version" >&2 - exit 1 -elif ! grep -qx "# $version ($today)" CHANGELOG.md; then - date="$(sed -n "s/# $version (\(.*\))/\1/p" CHANGELOG.md)" - printf "Error: Release date \"%s\" != \"%s\" (today) for version %s in CHANGELOG.md\n" "$date" "$today" "$version" >&2 - exit 1 -fi - -gh_notes="$(awk "/^# $version/{flag=1; next} /^# [0-9]+/{flag=0} flag" CHANGELOG.md | grep '[^[:blank:]]' | awk -vORS='\\n' 1)" - -# make a gpg-signed tag for the release -git tag --sign --message "Release $version" "$version" - -# create release tarball -tarball="build/userli-$(git --no-pager describe --tags --always).tar.gz" -if [ "$vagrant" = "yes" ]; then - (vagrant up; - vagrant ssh -c "tempdir=\"\$(mktemp -d)\"; - git clone /vagrant \"\$tempdir/userli-$version\"; - make prepare; - (cd \"\$tempdir/userli-$version\"; - make release; - cp -a build/userli* /vagrant/build/); - rm -r \"\$tempdir\"") -else - make release -fi -if [ ! -f "$tarball" ]; then - printf "Error: release tarball %s not created\n" "$tarball" >&2 - exit 1 -fi - -# gpg-sign release tarball -gpg -u ${GPG_SIGN_KEY} --output "${tarball}.asc" --armor --detach-sign --batch --yes "$tarball" +gh_notes="$(awk "/^# $VERSION/{flag=1; next} /^# [0-9]+/{flag=0} flag" CHANGELOG.md | grep '[^[:blank:]]' | awk -vORS='\\n' 1)" # validate token curl --output /dev/null --silent --header "$auth" $gh_repo || { printf "Error: Invalid repo, token or network issue\n" >&2; exit 1; } -# push git tag -git push origin "refs/tags/${version}" >/dev/null - # create release on Github -api_json=$(printf '{"tag_name": "%s","target_commitish": "%s","name": "%s","body": "%s","draft": false,"prerelease": %s}' "$version" "main" "$version" "$gh_notes" "false") +api_json=$(printf '{"tag_name": "%s","target_commitish": "%s","name": "%s","body": "%s","draft": false,"prerelease": %s}' "$VERSION" "main" "$VERSION" "$gh_notes" "false") gh_release="$(curl --silent --proto-redir https --data "$api_json" "$gh_repo/releases" -H "Accept: application/vnd.github.v3+json" -H "$gh_auth")" # read asset tags -gh_response="$(curl --silent --header "$auth" "$gh_tags")" +gh_tag_response="$(curl --silent --header "$gh_auth" "$gh_tags")" # get release id -eval $(printf "$gh_response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=') -[ "$id" ] || { printf "Error: Failed to get release id for tag: %s\n" "$version"; printf "%s\n" "$gh_response" | awk 'length($0)<100' >&2; exit 1; } +eval $(printf "$gh_tag_response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=') +[ "$id" ] || { printf "Error: Failed to get release id for tag: %s\n" "$VERSION"; printf "%s\n" "$gh_tag_response" | awk 'length($0)<100' >&2; exit 1; } # upload to Github for ext in "" ".asc" ".sha256" ".sha512"; do - gh_asset="https://uploads.github.com/repos/${gh_group}/${gh_project}/releases/${id}/assets?name=$(basename ${tarball}${ext})" + gh_asset="https://uploads.github.com/repos/${gh_group}/${gh_project}/releases/${id}/assets?name=userli-${VERSION}.tar.gz${ext}" curl --silent --proto-redir https "$gh_asset" \ -H "Content-Type: application/octet-stream" \ -H "Accept: application/vnd.github.v3+json" \ - -H "$gh_auth" --data-binary @"${tarball}${ext}" + -H "$gh_auth" --data-binary @"build/userli-${VERSION}.tar.gz${ext}" done diff --git a/docs/development/release.md b/docs/development/release.md index 7e6ffa34..0f2cfb51 100644 --- a/docs/development/release.md +++ b/docs/development/release.md @@ -3,13 +3,13 @@ Release tarballs are the preferred way to install Userli. This page explains how to create them. -First, you'll need a [Github API token](https://github.com/settings/tokens). -The token needs the following priviledges: +First, you need a [Github API token](https://github.com/settings/tokens). +The token needs the following privileges: public_repo, repo:status, repo_deployment -Now, execute the following script. It will create a version tag, release and +Now, run the Makefile target `release`. It will create a version tag, Github release and copy the info from `CHANGELOG.md` to the release info. - $ GITHUB_API_TOKEN= GPG_SIGN_KEY="" ./bin/github-release.sh + $ VERSION= GITHUB_API_TOKEN= GPG_SIGN_KEY="" make release