aboutsummaryrefslogtreecommitdiffstats
path: root/git-archive-signer
blob: 6b3d840e24099d663cb44a125ce46f375923f8c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/bin/bash
#
# git-archive-signer
# ------------------
# Use this script to create a tarball signature for a tag and store
# it as part of the repo (using git notes, as supported by cgit).

# Don't change this if you want this to actually work
NOTEREF="refs/notes/signatures/tar"
NOTEREF_MINISIG="refs/notes/minisig/tar"

# Pass the tag as the only parameter, otherwise we grab the latest
# annotated tag we find. You may also pass "list" to list all tags that
# already carry corresponding signature notes.
if [[ $1 == "list" ]]; then
    git notes --ref ${NOTEREF} list | cut -d' ' -f2 | xargs git describe
    exit 0
fi

TAG=$1

# Set this to your gitolite.kernel.org remote
# We'll also use git config --get archive-signer.remote if we find it
REMOTE="$(git config --get archive-signer.remote)"
if [[ -z ${REMOTE} ]]; then
    REMOTE="origin"
fi

# Change this if your gpg2 is elsewhere
GPGBIN="/usr/bin/gpg2"
# Change this if your minisign is elsewhere
MINISIGNBIN="/usr/bin/minisign"

# If you want to use a specific key (or subkey) instead of the default,
# then edit and uncomment this line. If you have multiple valid signing
# subkeys, then add the exact subkey ID and add a "!" at the end.
# We'll also use git config archive-signer.usekey value if we find it
#USEKEY="Ox12345678DEADBEEF"
USEKEY="$(git config --get archive-signer.usekey)"

# We use TARNAME when making a git archive --prefix, e.g. $TARNAME-1.2.3/
# Set it here if guessing basename is wrong.
# We'll also use git config archive-signer.tarname value if we find it
TARNAME="$(git config --get archive-signer.tarname)"
if [[ -z ${TARNAME} ]]; then
    TARNAME="$(basename $(pwd))"
fi

# You shouldn't need to change anything below

if [[ -z ${TAG} ]]; then
    # Assume you want the latest tag
    TAG="$(git describe --abbrev=0)"
    if [[ -z ${TAG} ]]; then
        echo "Could not figure out which tag you want"
        exit 1
    fi
fi

# The archive prefix will be created as $TARNAME-$TAG/ (sans leading v)
# Change it here if it doesn't correspond to your needs
# We'll add the trailing / where it is needed, so don't add it here
PREFIX="${TARNAME}-${TAG#v}"

# Do we already have a signature note for this tag?
# Start by fetching the origin notes
echo "Updating notes from remote"
git fetch ${REMOTE} "refs/notes/*:refs/notes/*"
if git notes --ref=${NOTEREF} list ${TAG} >/dev/null 2>&1; then
    echo "Signature note for ${TAG} already exists!"
    echo "To make a new one, delete it first:"
    echo "    git notes --ref=${NOTEREF} remove ${TAG}"
    exit 1
fi

echo -n "Generate signature note for ${PREFIX}.tar? [Y/n] "
read YN

[[ -z ${YN} ]] && YN=y
[[ ${YN} != "y" ]] && exit 1

# We add the exact archive line to sig comments,
# so put it together here
GIT_ARCHIVE_CMD="git archive --format tar --prefix=${PREFIX}/ ${TAG}"
# Record the version of git that created this archive
GIT_VERSION=$(git --version)

if [[ ! -z ${USEKEY} ]]; then
    GPGBIN="${GPGBIN} -u ${USEKEY}"
fi

# We put the tarball into a temp file, in case we need to minisign it, too
TMP_ARCHIVE=$(mktemp)
echo -n "Running ${GIT_ARCHIVE_CMD}..."
${GIT_ARCHIVE_CMD} > ${TMP_ARCHIVE}
echo "done"
git notes --ref=${NOTEREF} add -C "$(
    cat ${TMP_ARCHIVE} | ${GPGBIN} -a -b -o - \
        --comment "This signature is for the .tar version of the archive" \
        --comment "${GIT_ARCHIVE_CMD}" \
        --comment "${GIT_VERSION}" |
        git hash-object -w --stdin)" "${TAG}"

if [[ $? != 0 ]]; then
    echo "git notes exited with error"
    rm -f ${TMP_ARCHIVE}
    exit 1
fi

echo
git --no-pager notes --ref=${NOTEREF} show ${TAG}
echo

USE_MINISIGN="$(git config --get archive-signer.use-minisign)"
if [[ ${USE_MINISIGN} == "yes" ]]; then
    if git notes --ref=${NOTEREF_MINISIG} list ${TAG} >/dev/null 2>&1; then
        echo "Minisign note for ${TAG} already exists!"
        echo "To make a new one, delete it first:"
        echo "    git notes --ref=${NOTEREF_MINISIG} remove ${TAG}"
        exit 1
    fi
    MINISIGN_CMD="${MINISIGNBIN}"
    MINISIGN_COMMENT="This minisign signature is for the .tar version of the archive"
    MINISIGN_TRUSTED="$(date -u), ${GIT_VERSION}, using: ${GIT_ARCHIVE_CMD}"
    # If minisign-keyfile is set, we'll use that key instead of the default
    MINISIGN_KEY="$(git config --get archive-signer.minisign-key)"
    if [[ ! -z ${MINISIGN_KEY} ]]; then
        MINISIGN_CMD="${MINISIGN_CMD} -s $(eval echo ${MINISIGN_KEY})"
    fi
    # If you don't want to type in the minisign passphrase, you can
    # store it gpg-encrypted and set archive.signer.minisign-gpg-passphrase to
    # point at the file containing the encrypted passphrase.
    # To generate, use:
    #     echo passphrase | gpg -r YOURKEYID -e > minisign-passphrase.gpg
    MINISIGN_PASSPHRASE="$(git config --get archive-signer.minisign-gpg-passphrase)"
    MINISIGN_OUT=$(mktemp)
    echo "Generating minisign signature"
    if [[ -z ${MINISIGN_PASSPHRASE} ]]; then
        ${MINISIGN_CMD} -S \
            -c "${MINISIGN_COMMENT}" -t "${MINISIGN_TRUSTED}" \
            -x ${MINISIGN_OUT} -m ${TMP_ARCHIVE}
    else
        echo "Using the gpg-encrypted passphrase from ${MINISIGN_PASSPHRASE}"
        ${GPGBIN} -q -d $(eval echo ${MINISIGN_PASSPHRASE}) \
            | ${MINISIGN_CMD} -S \
                -c "${MINISIGN_COMMENT}" -t "${MINISIGN_TRUSTED}" \
                -x ${MINISIGN_OUT} -m ${TMP_ARCHIVE}
    fi
    if [[ ! -s ${MINISIGN_OUT} ]]; then
        # Assume minisign process went wrong
        echo "Minisign signature is missing, aborting!"
        rm -f ${TMP_ARCHIVE}
        exit 1
    fi
    git notes --ref=${NOTEREF_MINISIG} add \
        -C "$(cat ${MINISIGN_OUT} | git hash-object -w --stdin)" "${TAG}"

    if [[ $? != 0 ]]; then
        echo "git notes exited with error"
        rm -f ${TMP_ARCHIVE} ${MINISIGN_OUT}
        exit 1
    fi

    echo "-----"
    git --no-pager notes --ref=${NOTEREF_MINISIG} show ${TAG}
    echo "-----"
    echo

    rm -f ${MINISIGN_OUT}
fi

rm -f ${TMP_ARCHIVE}

echo -n "Push to ${REMOTE}? [Y/n] "
read YN
echo

[[ -z ${YN} ]] && YN=y
if [[ ${YN} != "y" ]]; then
    echo "Remember to push it using:"
    echo "    git push ${REMOTE} refs/notes/*"
    exit 0
fi

echo "Pushing notes to ${REMOTE}..."
git push ${REMOTE} refs/notes/*