#!/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.2' 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() { mapfile -d '\0' signatures < <(find .git/refs/notes -type f 2>/dev/null) if [[ $(echo ${#signatures[@]}) != 0 ]]; then git fetch >/dev/null 2>&1 else git config --add remote.origin.fetch "+refs/notes/*:refs/notes/*" >/dev/null 2>&1 git fetch origin refs/notes/*:refs/notes/* >/dev/null 2>&1 fi unset signatures } #--------- # 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