diff --git a/Makefile b/Makefile index 83bdc15e..931e4fdd 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,14 @@ -APP_NAME:= $(notdir $(CURDIR)) -RELEASE_DIR:=$(APP_NAME)-$(VERSION) -RELEASE_FILE:=$(RELEASE_DIR).tar.gz -SHA_ALGORITHMS:=256 512 -PWD_NAME:=$(shell pwd) -TMPDIR:=$(shell mktemp -d) -BUILDDIR:=$(TMPDIR)/$(RELEASE_DIR) +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)) -# 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 +clean: + git reset --hard + git clean --force -d vendors: composer install @@ -35,108 +28,34 @@ 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 -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 - git clone ./ $(BUILDDIR) - (cd $(BUILDDIR); \ - APP_ENV=prod composer install --no-dev --ignore-platform-reqs --no-scripts; \ - APP_ENV=prod composer dump-autoload; \ - $(YARN) --pure-lockfile; \ - $(YARN) encore production) +release: clean prepare + APP_ENV=prod composer install --no-dev --ignore-platform-reqs + APP_ENV=prod composer dump-autoload + yarn --pure-lockfile + yarn encore production # Create a release tarball - (cd $(TMPDIR); tar --exclude='$(RELEASE_DIR)/.env.*' \ - --exclude='$(RELEASE_DIR)/.git*' \ - --exclude='$(RELEASE_DIR)/.idea' \ - --exclude='$(RELEASE_DIR)/.php-cs-fixer.cache' \ - --exclude='$(RELEASE_DIR)/.phpunit*' \ - --exclude='$(RELEASE_DIR)/.vagrant' \ - --exclude='$(RELEASE_DIR)/.*.yml' \ - --exclude='$(RELEASE_DIR)/ansible' \ - --exclude='$(RELEASE_DIR)/behat.yml' \ - --exclude='$(RELEASE_DIR)/bin/behat*' \ - --exclude='$(RELEASE_DIR)/bin/crypt-gpg-pinentry' \ - --exclude='$(RELEASE_DIR)/bin/doctrine*' \ - --exclude='$(RELEASE_DIR)/bin/github-release.sh' \ - --exclude='$(RELEASE_DIR)/bin/local-php-security-checker' \ - --exclude='$(RELEASE_DIR)/bin/patch-type-declarations' \ - --exclude='$(RELEASE_DIR)/bin/php*' \ - --exclude='$(RELEASE_DIR)/bin/rector' \ - --exclude='$(RELEASE_DIR)/bin/simple-phpunit' \ - --exclude='$(RELEASE_DIR)/bin/sql-formatter' \ - --exclude='$(RELEASE_DIR)/bin/uaparser' \ - --exclude='$(RELEASE_DIR)/bin/var-dump-server' \ - --exclude='$(RELEASE_DIR)/bin/yaml-lint' \ - --exclude='$(RELEASE_DIR)/build' \ - --exclude='$(RELEASE_DIR)/composer.*' \ - --exclude='$(RELEASE_DIR)/features' \ - --exclude='$(RELEASE_DIR)/Makefile' \ - --exclude='$(RELEASE_DIR)/mkdocs.yml' \ - --exclude='$(RELEASE_DIR)/node_modules' \ - --exclude='$(RELEASE_DIR)/package.json' \ - --exclude='$(RELEASE_DIR)/phpunit.xml' \ - --exclude='$(RELEASE_DIR)/rector.php' \ - --exclude='$(RELEASE_DIR)/requirements.yml' \ - --exclude='$(RELEASE_DIR)/sonar-project.properties' \ - --exclude='$(RELEASE_DIR)/symfony.lock' \ - --exclude='$(RELEASE_DIR)/tests' \ - --exclude='$(RELEASE_DIR)/Vagrantfile' \ - --exclude='$(RELEASE_DIR)/var/cache/*' \ - --exclude='$(RELEASE_DIR)/var/db_test.sqlite' \ - --exclude='$(RELEASE_DIR)/var/log/*' \ - --exclude='$(RELEASE_DIR)/vendor/bin/.phpunit' \ - --exclude='$(RELEASE_DIR)/webpack.config.js' \ - --exclude='$(RELEASE_DIR)/yarn.lock' \ - -czf $(PWD)/build/$(RELEASE_FILE) $(RELEASE_DIR)) - rm -rf $(TMPDIR) + 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' \ + -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: build 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 b99ea2e3..d9e05e61 100755 --- a/bin/github-release.sh +++ b/bin/github-release.sh @@ -2,12 +2,20 @@ 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 "$VERSION" ]; then - printf "Error: environment variable \$VERSION needs to be set\n" >&2 +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 exit 1 fi @@ -16,36 +24,78 @@ 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 -gh_notes="$(awk "/^# $VERSION/{flag=1; next} /^# [0-9]+/{flag=0} flag" CHANGELOG.md | grep '[^[:blank:]]' | awk -vORS='\\n' 1)" +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" # 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_tag_response="$(curl --silent --header "$gh_auth" "$gh_tags")" +gh_response="$(curl --silent --header "$auth" "$gh_tags")" # get release id -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; } +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; } # 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=userli-${VERSION}.tar.gz${ext}" + gh_asset="https://uploads.github.com/repos/${gh_group}/${gh_project}/releases/${id}/assets?name=$(basename ${tarball}${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 @"build/userli-${VERSION}.tar.gz${ext}" + -H "$gh_auth" --data-binary @"${tarball}${ext}" done diff --git a/docs/development/release.md b/docs/development/release.md index 0f2cfb51..7e6ffa34 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 need a [Github API token](https://github.com/settings/tokens). -The token needs the following privileges: +First, you'll need a [Github API token](https://github.com/settings/tokens). +The token needs the following priviledges: public_repo, repo:status, repo_deployment -Now, run the Makefile target `release`. It will create a version tag, Github release and +Now, execute the following script. It will create a version tag, release and copy the info from `CHANGELOG.md` to the release info. - $ VERSION= GITHUB_API_TOKEN= GPG_SIGN_KEY="" make release + $ GITHUB_API_TOKEN= GPG_SIGN_KEY="" ./bin/github-release.sh