forked from ungleich-public/cdist
4f40c6ac65
Maintainers should use build-helper script. End users should use Makefile, which contains targets that can be run on pure source (without git repository).
618 lines
16 KiB
Bash
Executable file
618 lines
16 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
|
|
# 2016-2019 Darko Poljak (darko.poljak at gmail.com)
|
|
#
|
|
# This file is part of cdist.
|
|
#
|
|
# cdist is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# cdist is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
#
|
|
# This file contains the heavy lifting found usually in the Makefile.
|
|
#
|
|
|
|
usage() {
|
|
printf "usage: %s TARGET [RUN-AS]
|
|
Available targets:
|
|
print-runas
|
|
changelog-changes
|
|
changelog-version
|
|
check-date
|
|
check-unittest
|
|
ml-release
|
|
archlinux-release
|
|
pypi-release
|
|
release-git-tag
|
|
sign-git-release
|
|
release
|
|
test
|
|
test-remote
|
|
pycodestyle
|
|
pep8
|
|
check-pycodestyle
|
|
shellcheck-global-explorers
|
|
shellcheck-type-explorers
|
|
shellcheck-manifests
|
|
shellcheck-local-gencodes
|
|
shellcheck-remote-gencodes
|
|
shellcheck-scripts
|
|
shellcheck-gencodes
|
|
shellcheck-types
|
|
shellcheck
|
|
shellcheck-type-files
|
|
shellcheck-with-files
|
|
shellcheck-build-helper
|
|
check-shellcheck
|
|
version-branch
|
|
version
|
|
target-version
|
|
clean
|
|
distclean
|
|
Run as:
|
|
nico
|
|
darko - default, if not specified\n" "$1"
|
|
}
|
|
|
|
basename="${0##*/}"
|
|
|
|
if [ $# -lt 1 ]
|
|
then
|
|
usage "${basename}"
|
|
exit 1
|
|
fi
|
|
|
|
option=$1; shift
|
|
if [ $# -ge 1 ]
|
|
then
|
|
run_as="$1"
|
|
else
|
|
run_as="darko"
|
|
fi
|
|
|
|
case "$run_as" in
|
|
nico)
|
|
from_a=nico.schottelius
|
|
from_d=ungleich.ch
|
|
ml_name="Nico Schottelius"
|
|
ml_sig_name="Nico"
|
|
;;
|
|
darko|'')
|
|
from_a=darko.poljak
|
|
from_d=gmail.com
|
|
ml_name="Darko Poljak"
|
|
ml_sig_name="Darko"
|
|
;;
|
|
*)
|
|
printf "Unsupported RUN-AS value: '%s'.\n" "${run_as}" >&2
|
|
usage "${basename}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
SHELLCHECKCMD="shellcheck -s sh -f gcc -x"
|
|
# Skip SC2154 for variables starting with __ since such variables are cdist
|
|
# environment variables.
|
|
SHELLCHECK_SKIP=': __.*is referenced but not assigned.*\[SC2154\]'
|
|
|
|
to_a="cdist-configuration-management"
|
|
to_d="googlegroups.com"
|
|
|
|
# Change to checkout directory
|
|
basedir="${0%/*}/../"
|
|
cd "$basedir"
|
|
|
|
case "$option" in
|
|
print-runas)
|
|
printf "run_as: '%s'\n" "$run_as"
|
|
;;
|
|
|
|
changelog-changes)
|
|
if [ "$#" -eq 1 ]; then
|
|
start=$1
|
|
else
|
|
start="[[:digit:]]"
|
|
fi
|
|
|
|
end="[[:digit:]]"
|
|
|
|
awk -F: "BEGIN { start=0 }
|
|
{
|
|
if(start == 0) {
|
|
if (\$0 ~ /^$start/) {
|
|
start = 1
|
|
}
|
|
} else {
|
|
if (\$0 ~ /^$end/) {
|
|
exit
|
|
} else {
|
|
print \$0
|
|
}
|
|
}
|
|
}" "$basedir/docs/changelog"
|
|
;;
|
|
|
|
changelog-version)
|
|
# get version from changelog
|
|
grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//'
|
|
;;
|
|
|
|
check-date)
|
|
# verify date in changelog is today
|
|
date_today="$(date +%Y-%m-%d)"
|
|
date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //')
|
|
|
|
if [ "$date_today" != "$date_changelog" ]; then
|
|
printf "Date in changelog is not today\n"
|
|
printf "Changelog date: %s\n" "${date_changelog}"
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
check-unittest)
|
|
"$0" test
|
|
;;
|
|
|
|
ml-release)
|
|
if [ $# -ne 1 ]; then
|
|
printf "%s ml-release version\n" "$0" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Send mail only once - lock until new changelog things happened.
|
|
[ ! -f .lock-ml ] && touch .lock-ml
|
|
x=$(find 'docs' -name changelog -type f -newer .lock-ml)
|
|
[ -z "${x}" ] && exit 0
|
|
|
|
version=$1; shift
|
|
|
|
to=${to_a}@${to_d}
|
|
from=${from_a}@${from_d}
|
|
|
|
(
|
|
cat << eof
|
|
From: ${ml_name} <$from>
|
|
To: cdist mailing list <$to>
|
|
Subject: cdist $version has been released
|
|
|
|
Hello .*,
|
|
|
|
cdist $version has been released with the following changes:
|
|
|
|
eof
|
|
|
|
"$0" changelog-changes "$version"
|
|
cat << eof
|
|
|
|
Cheers,
|
|
${ml_sig_name}
|
|
|
|
--
|
|
Automatisation at its best level. With cdist.
|
|
eof
|
|
) > mailinglist.tmp
|
|
|
|
if [ "$run_as" = "nico" ]
|
|
then
|
|
/usr/sbin/sendmail -f "$from" "$to" < mailinglist.tmp && rm -f mailinglist.tmp
|
|
fi
|
|
|
|
touch .lock-ml
|
|
;;
|
|
|
|
archlinux-release)
|
|
if [ $# -ne 1 ]; then
|
|
printf "%s archlinux-release version\n" "$0" >&2
|
|
exit 1
|
|
fi
|
|
version=$1; shift
|
|
|
|
ARCHLINUXTAR="cdist-${version}-1.src.tar.gz"
|
|
./PKGBUILD.in "${version}"
|
|
umask 022
|
|
mkaurball
|
|
burp -c system "${ARCHLINUXTAR}"
|
|
;;
|
|
|
|
pypi-release)
|
|
# Ensure that pypi release has the right version
|
|
"$0" version
|
|
|
|
make docs-clean
|
|
make docs
|
|
python3 setup.py sdist upload
|
|
;;
|
|
|
|
release-git-tag)
|
|
target_version=$($0 changelog-version)
|
|
if git rev-parse --verify "refs/tags/${target_version}" 2>/dev/null; then
|
|
printf "Tag for %s exists, aborting\n" "${target_version}"
|
|
exit 1
|
|
fi
|
|
printf "Enter tag description for %s: " "${target_version}"
|
|
read -r tagmessage
|
|
|
|
# setup for signed tags:
|
|
# gpg --fulL-gen-key
|
|
# gpg --list-secret-keys --keyid-format LONG
|
|
# git config --local user.signingkey <id>
|
|
# for exporting pub key:
|
|
# gpg --armor --export <id> > pubkey.asc
|
|
# gpg --output pubkey.gpg --export <id>
|
|
# show tag with signature
|
|
# git show <tag>
|
|
# verify tag signature
|
|
# git tag -v <tag>
|
|
#
|
|
# gpg verify signature
|
|
# gpg --verify <asc-file> <file>
|
|
# gpg --no-default-keyring --keyring <pubkey.gpg> --verify <asc-file> <file>
|
|
# Ensure gpg-agent is running.
|
|
GPG_TTY=$(tty)
|
|
export GPG_TTY
|
|
gpg-agent
|
|
|
|
git tag -s "$target_version" -m "$tagmessage"
|
|
git push --tags
|
|
;;
|
|
|
|
sign-git-release)
|
|
if [ $# -lt 2 ]
|
|
then
|
|
printf "usage: %s sign-git-release TAG TOKEN [ARCHIVE]\n" "$0"
|
|
printf " if ARCHIVE is not specified then it is created\n"
|
|
exit 1
|
|
fi
|
|
tag="$1"
|
|
if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1
|
|
then
|
|
printf "Tag \"%s\" not found.\n" "${tag}"
|
|
exit 1
|
|
fi
|
|
token="$2"
|
|
if [ $# -gt 2 ]
|
|
then
|
|
archivename="$3"
|
|
else
|
|
archivename="cdist-${tag}.tar"
|
|
git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \
|
|
|| exit 1
|
|
# make sure target version is generated
|
|
"$0" target-version
|
|
tar -x -f "${archivename}" || exit 1
|
|
cp cdist/version.py "cdist-${tag}/cdist/version.py" || exit 1
|
|
tar -c -f "${archivename}" "cdist-${tag}/" || exit 1
|
|
rm -r -f "cdist-${tag}/"
|
|
gzip "${archivename}" || exit 1
|
|
archivename="${archivename}.gz"
|
|
fi
|
|
gpg --armor --detach-sign "${archivename}" || exit 1
|
|
|
|
project="ungleich-public%2Fcdist"
|
|
sed_cmd='s/^.*"markdown":"\([^"]*\)".*$/\1/'
|
|
|
|
# upload archive
|
|
response_archive=$(curl -f -X POST \
|
|
--http1.1 \
|
|
-H "PRIVATE-TOKEN: ${token}" \
|
|
-F "file=@${archivename}" \
|
|
"https://code.ungleich.ch/api/v4/projects/${project}/uploads" \
|
|
| sed "${sed_cmd}") || exit 1
|
|
|
|
# upload archive signature
|
|
response_archive_sig=$(curl -f -X POST \
|
|
--http1.1 \
|
|
-H "PRIVATE-TOKEN: ${token}" \
|
|
-F "file=@${archivename}.asc" \
|
|
"https://code.ungleich.ch/api/v4/projects/${project}/uploads" \
|
|
| sed "${sed_cmd}") || exit 1
|
|
|
|
# make release
|
|
changelog=$("$0" changelog-changes "$1" | sed 's/^[[:space:]]*//')
|
|
release_notes=$(
|
|
printf "%s\n\n%s\n\n**Changelog**\n\n%s\n" \
|
|
"${response_archive}" "${response_archive_sig}" "${changelog}"
|
|
)
|
|
curl -f -X POST \
|
|
-H "PRIVATE-TOKEN: ${token}" \
|
|
-F "description=${release_notes}" \
|
|
"https://code.ungleich.ch/api/v4/projects/${project}/repository/tags/${tag}/release" \
|
|
|| exit 1
|
|
|
|
# remove generated files (archive and asc)
|
|
if [ $# -eq 2 ]
|
|
then
|
|
rm -f "${archivename}"
|
|
fi
|
|
rm -f "${archivename}.asc"
|
|
;;
|
|
|
|
release)
|
|
set -e
|
|
target_version=$($0 changelog-version)
|
|
target_branch=$($0 version-branch)
|
|
|
|
printf "Beginning release process for %s\n" "${target_version}"
|
|
|
|
# First check everything is sane
|
|
"$0" check-date
|
|
"$0" check-unittest
|
|
"$0" check-pycodestyle
|
|
"$0" check-shellcheck
|
|
|
|
# Generate version file to be included in packaging
|
|
"$0" target-version
|
|
|
|
# Ensure the git status is clean, else abort
|
|
if ! git diff-index --name-only --exit-code HEAD ; then
|
|
printf "Unclean tree, see files above, aborting.\n"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure we are on the master branch
|
|
masterbranch=yes
|
|
if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
|
|
printf "Releases are happening from the master branch, aborting.\n"
|
|
|
|
printf "Enter the magic word to release anyway:"
|
|
read -r magicword
|
|
|
|
if [ "$magicword" = "iknowwhatido" ]; then
|
|
masterbranch=no
|
|
else
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ "$masterbranch" = yes ]; then
|
|
# Ensure version branch exists
|
|
if ! git rev-parse --verify "refs/heads/${target_branch}" 2>/dev/null; then
|
|
git branch "$target_branch"
|
|
fi
|
|
|
|
# Merge master branch into version branch
|
|
git checkout "$target_branch"
|
|
git merge master
|
|
fi
|
|
|
|
# Verify that after the merge everything works
|
|
"$0" check-date
|
|
"$0" check-unittest
|
|
|
|
# Generate documentation (man and html)
|
|
# First, clean old generated docs
|
|
make docs-clean
|
|
make docs
|
|
|
|
#############################################################
|
|
# Everything green, let's do the release
|
|
|
|
# Tag the current commit
|
|
"$0" release-git-tag
|
|
|
|
# Also merge back the version branch
|
|
if [ "$masterbranch" = yes ]; then
|
|
git checkout master
|
|
git merge "$target_branch"
|
|
fi
|
|
|
|
# Publish git changes
|
|
# if you want to have mirror locally then uncomment this support
|
|
# if [ "$run_as" = "nico" ]
|
|
# then
|
|
# git push --mirror
|
|
# else
|
|
# if we are not Nico :) then just push, no mirror
|
|
git push
|
|
# push also new branch and set up tracking
|
|
git push -u origin "${target_branch}"
|
|
# fi
|
|
|
|
# Create and publish package for pypi
|
|
"$0" pypi-release
|
|
|
|
if [ "$run_as" = "nico" ]
|
|
then
|
|
# Archlinux release is based on pypi
|
|
"$0" archlinux-release
|
|
fi
|
|
|
|
# sign git tag
|
|
printf "Enter upstream repository authentication token: "
|
|
read -r token
|
|
"$0" sign-git-release "${target_version}" "${token}"
|
|
|
|
# Announce change on ML
|
|
"$0" ml-release "${target_version}"
|
|
|
|
cat << eof
|
|
Manual steps post release:
|
|
- cdist-web
|
|
- twitter
|
|
eof
|
|
;;
|
|
|
|
test)
|
|
if [ ! -f "cdist/version.py" ]
|
|
then
|
|
printf "cdist/version.py is missing, generate it first.\n"
|
|
exit 1
|
|
fi
|
|
|
|
PYTHONPATH="$(pwd -P)"
|
|
export PYTHONPATH
|
|
|
|
if [ $# -lt 1 ]; then
|
|
python3 -m cdist.test
|
|
else
|
|
python3 -m unittest "$@"
|
|
fi
|
|
;;
|
|
|
|
test-remote)
|
|
if [ ! -f "cdist/version.py" ]
|
|
then
|
|
printf "cdist/version.py is missing, generate it first.\n"
|
|
exit 1
|
|
fi
|
|
|
|
PYTHONPATH="$(pwd -P)"
|
|
export PYTHONPATH
|
|
|
|
python3 -m cdist.test.exec.remote
|
|
;;
|
|
|
|
pycodestyle|pep8)
|
|
pycodestyle "${basedir}" "${basedir}/scripts/cdist" | less
|
|
;;
|
|
|
|
check-pycodestyle)
|
|
"$0" pycodestyle
|
|
printf "\\nPlease review pycodestyle report.\\n"
|
|
while true
|
|
do
|
|
printf "Continue (yes/no)?\n"
|
|
any=
|
|
read -r any
|
|
case "$any" in
|
|
yes)
|
|
break
|
|
;;
|
|
no)
|
|
exit 1
|
|
;;
|
|
*)
|
|
printf "Please answer with 'yes' or 'no' explicitly.\n"
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
|
|
shellcheck-global-explorers)
|
|
find cdist/conf/explorer -type f -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-type-explorers)
|
|
find cdist/conf/type -type f -path "*/explorer/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-manifests)
|
|
find cdist/conf/type -type f -name manifest -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-local-gencodes)
|
|
find cdist/conf/type -type f -name gencode-local -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-remote-gencodes)
|
|
find cdist/conf/type -type f -name gencode-remote -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-scripts)
|
|
${SHELLCHECKCMD} scripts/cdist-dump || exit 0
|
|
;;
|
|
|
|
shellcheck-gencodes)
|
|
"$0" shellcheck-local-gencodes
|
|
"$0" shellcheck-remote-gencodes
|
|
;;
|
|
|
|
shellcheck-types)
|
|
"$0" shellcheck-type-explorers
|
|
"$0" shellcheck-manifests
|
|
"$0" shellcheck-gencodes
|
|
;;
|
|
|
|
shellcheck)
|
|
"$0" shellcheck-global-explorers
|
|
"$0" shellcheck-types
|
|
"$0" shellcheck-scripts
|
|
;;
|
|
|
|
shellcheck-type-files)
|
|
find cdist/conf/type -type f -path "*/files/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0
|
|
;;
|
|
|
|
shellcheck-with-files)
|
|
"$0" shellcheck
|
|
"$0" shellcheck-type-files
|
|
;;
|
|
|
|
shellcheck-build-helper)
|
|
${SHELLCHECKCMD} ./bin/build-helper
|
|
;;
|
|
|
|
check-shellcheck)
|
|
"$0" shellcheck
|
|
printf "\\nPlease review shellcheck report.\\n"
|
|
while true
|
|
do
|
|
printf "Continue (yes/no)?\n"
|
|
any=
|
|
read -r any
|
|
case "$any" in
|
|
yes)
|
|
break
|
|
;;
|
|
no)
|
|
exit 1
|
|
;;
|
|
*)
|
|
printf "Please answer with 'yes' or 'no' explicitly.\n"
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
|
|
version-branch)
|
|
"$0" changelog-version | cut -d. -f '1,2'
|
|
;;
|
|
|
|
version)
|
|
printf "VERSION = \"%s\"\n" "$(git describe)" > cdist/version.py
|
|
;;
|
|
|
|
target-version)
|
|
target_version=$($0 changelog-version)
|
|
printf "VERSION = \"%s\"\n" "${target_version}" > cdist/version.py
|
|
;;
|
|
|
|
clean)
|
|
make clean
|
|
|
|
# Archlinux
|
|
rm -f cdist-*.pkg.tar.xz cdist-*.tar.gz
|
|
rm -rf pkg/ src/
|
|
|
|
rm -f MANIFEST PKGBUILD
|
|
rm -rf dist/
|
|
|
|
# Signed release
|
|
rm -f cdist-*.tar.gz
|
|
rm -f cdist-*.tar.gz.asc
|
|
|
|
# Temp files
|
|
rm -f ./*.tmp
|
|
;;
|
|
|
|
distclean)
|
|
"$0" clean
|
|
rm -f cdist/version.py
|
|
;;
|
|
*)
|
|
printf "Unknown target: '%s'.\n" "${option}" >&2
|
|
usage "${basename}"
|
|
exit 1
|
|
;;
|
|
|
|
esac
|