#!/bin/bash
#
# Copyright (C) 2021 Jesus E.
#
# This program 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.
#
# This program 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 this program. If not, see .
#
VERSION='1.0.1'
HELP_MESSAGE="\
usage: $(basename "$0") [--dry-run] [--force] [-s ] [-F ] [-p ] -t
Create a detached signature for an archive of and add it to the tag's
notes in the refs/notes/signatures/ namespace.
This is useful for cgit's snapshot support.
-d, --dry-run Don't actually add any signature refs
-f, --force Overwrite any existing signature
-F, --format Archive format to use; either \"tar.gz\", \"tar.lz\", \"tar.xz\",
\"tgz\", \"tar\" or \"zip\". For default \"tar.lz\".
-p, --prefix Snapsnot prefix to use instead of the repository basename
-P, --push Upload signature to remote git
-t, --tag Git tag name
-s, --signature Create the signature using instead of the default
-v, --version Show version of $(basename "$0")
-h, --help This message
Examples:
git-snapsign -t 1.0.0
git-snapsign --format tar.gz -t 1.0.0
"
#-------------
# variables
#-------------
# Require Git repository
eval "$(
echo "$OPTIONS_SPEC" \
| git rev-parse ||
echo exit $?
)"
gpg="$(git config gpg.program)"
toplevel="$(git rev-parse --show-toplevel)"
dryrun="n"
force="n"
format="tar.lz"
keyid="$(git config user.signingkey)"
prefix="$(basename "${toplevel%.git}")"
_cgit_snappfx="$(git config --local cgit.snapshot-prefix)"
_repo_snappfx="$(git config --local repo.snapshot-prefix)"
if [ -n "${_cgit_snappfx}" ]; then
prefix="${_cgit_snappfx})"
elif [ -n "${_repo_snappfx}" ]; then
prefix="${_repo_snappfx})"
fi
ref="$(git symbolic-ref HEAD 2> /dev/null | sed -e 's/refs\/heads\///')"
if [[ -n $ref ]]; then
gtbranch=$ref
else
gtbranch=$ref
fi
check_format() {
if [ "${format}" != "tar.gz" ] \
&& [ "${format}" != "tar.lz" ] \
&& [ "${format}" != "tar.xz" ] \
&& [ "${format}" != "tgz" ] \
&& [ "${format}" != "tar" ] \
&& [ "${format}" != "zip" ];
then
echo -e "fatal: format not supported by git: ${format}"
exit 1
fi
}
check_notes() {
if ! git show-ref --quiet --verify -- "refs/notes" >/dev/null 2>&1; then
git config --add remote.origin.fetch "+refs/notes/*:refs/notes/*" >/dev/null 2>&1
git fetch >/dev/null 2>&1
else
git fetch >/dev/null 2>&1
fi
}
#---------
# core
#---------
if [ -z "${1}" ]; then
echo "$HELP_MESSAGE"
fi
for param in "$@"; do
shift
case "$param" in
"--dry-run") set -- "$@" "-d" ;;
"--force") set -- "$@" "-f" ;;
"--format") set -- "$@" "-F" ;;
"--prefix") set -- "$@" "-p" ;;
"--push") set -- "$@" "-P" ;;
"--signature") set -- "$@" "-s" ;;
"--tag") set -- "$@" "-t" ;;
"--version") set -- "$@" "-v" ;;
"--help") set -- "$@" "-h" ;;
*) set -- "$@" "$param" ;;
esac
done
while getopts ":dfPhvF:s:p:t:" opt; do
case $opt in
d)
dryrun=y
;;
f)
force=y
;;
F)
format=$OPTARG
check_format "$@"
;;
p)
prefix="${OPTARG}"
;;
P)
git push origin "${gtbranch}" refs/notes/*
;;
s)
keyid="$OPTARG"
;;
t)
check_notes "$@"
if ! git rev-parse --verify "${OPTARG}" >/dev/null 2>&1; then
echo -e "fatal: failed to verify tag: ${OPTARG}"
exit 1
fi
tag="${OPTARG}"
;;
v)
echo -e "$VERSION"
exit 0
;;
h)
echo -e "$HELP_MESSAGE"
exit 0
;;
\?)
echo "Invalid option -$OPTARG" >&2
exit 1
;;
esac
done
tmpdir="$(mktemp -dp"${TMPDIR:-/tmp}")"
trap 'rm -rf "${tmpdir}"' EXIT HUP INT QUIT TERM
if [[ -n "${tag}" ]]; then
archive="${tmpdir}/${prefix}-${tag#v}.${format}"
case "${format}" in
tar.lz)
git archive --format=tar --prefix "${prefix}-${tag#v}/" \
"${tag}" | lzip -c > "${archive}"
;;
tar.xz)
git archive --format=tar --prefix "${prefix}-${tag#v}/" \
"${tag}" | xz -c > "${archive}"
;;
*)
git archive --format "${format}" --prefix "${prefix}-${tag#v}/" \
--output "${archive}" "${tag}"
;;
esac
if ! "${gpg:-gpg}" --output "${archive}.asc" --armor \
--sign-with "${keyid}" --detach-sign <"${archive}"
then
echo -e "fatal: failed to sign archive"
exit 1
fi
if [ "${dryrun}" == "y" ]; then
sig_hash="$(git hash-object --stdin <"${archive}.asc")"
echo -e "dry-run: add signature (${sig_hash}) note for tag ${tag}"
else
sig_hash="$(git hash-object -w --stdin <"${archive}.asc")"
fi
if [ "${force}" == "y" ]; then
git notes --ref="refs/notes/signatures/${format}" \
add --force -C "${sig_hash}" "${tag}"
_interger=$?
if [[ "$_interger" -eq 0 ]]; then
echo -e "Forcing add signature (${sig_hash}) note for tag ${tag}"
else
exit 1
fi
else
git notes --ref="refs/notes/signatures/${format}" \
add -C "${sig_hash}" "${tag}"
_interger=$?
if [[ "$_interger" -eq 0 ]]; then
echo -e "Add signature (${sig_hash}) note for tag ${tag}"
else
echo -e "Signature already exists or invalid tag"
exit 1
fi
fi
fi