ZANARDI Bruno

Software Developper

About
Resume

Git Blog

How To: Releasing

previous home

related commands:
archive describe tag

Your project is in a stable state and you want to create a release? Let’s do it!

First step: create a signature

You need to have a signature to nicely create a release and signatures are stored by gpg. To know the currently stored signatures, run

$ gpg --list-keys

If nothing appears or if you can’t find your email in the output, you must create your signature with:

$ gpg --gen-key
Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection?

Chose 1 to create an asymetric signature

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)

Default is fine, hit <Return>

Please specify how long the key should be valid.
0 = key does not expire
d = key expires in n days
w = key expires in n weeks
m = key expires in n months
y = key expires in n years
key is valid for? (0) 

It is a nice idea to set a expiration date. And there is a way to change it, so say 1y

Key expires at <date>
Is this correct? (y/N)

Should be y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: <your name>
Email address: <your email>
Comment: <message>
You selected this USER-ID:
    "<your name> (<message>) <<your email>>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

Check entered datas, then O.

You need a Passphrase to protect your secret key.

Enter passphrase: <passphrase>
Repeat passphrase: <passphrase>

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
....+++++
+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
..............+++++
.....+++++
gpg: key <pub_hash> marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2018-02-28
pub   2048R/<pub_hash> 2017-02-28 [expires: 2018-02-28]
      Key fingerprint = <fingerprint>
uid                  <your name> (<message>) <<your email>>
sub   2048R/<sub_hash> 2017-02-28 [expires: 2018-02-28]

$

Now check gpg:

$ gpg --list-keys
<gpg file> ------------------------------------ pub 2048R/<pub_hash> 2017-02-28 [expires: 2018-02-28] uid <your name> (<message>) <<your email>> sub 2048R/<sub_hash> 2017-02-28 [expires: 2018-02-28]

Allright, gpg is configured.

Share public signature with git

Signing release tags allows end users / contributors to verify release tags, but they need your public key. Here is a way to publish it.
First, add your public key to a git blob object:

$ gpg -a --export <pub_hash> | git hash-object -w --stdin
<git_key_hash>

Then, tag this object:

$ git tag -a maintainer-gpg-pub <git_key_hash> -m "Public gpg maintainer key"
$ git push --tags

Now your public key is shared to anyone who wants to verify a tag.

Create a signed tag

Now you can create a signed tag. But create just one tag by release, no need to create one for each release build. I’ll explain why later in this post.

$ git tag -s <tagname> -m "<message>"
You need a passphrase to unlock the secret key for user: "<your name> (<message>) <<your email>>" 2048-bit RSA key, ID <pub_hash>, created 2017-02-28 Enter passphrase:

It is possible you have an error here:

$ git tag -s <tagname> -m "<message>"
gpg: skipped "<wrong name> <<wrong email>>": secret key not available gpg: signing failed: secret key not available error: gpg failed to sign the data error: unable to sign the tag

In this case, you must use:

$ git tag -u <sub_hash> <tagname> -m "<message>"
You need a passphrase to unlock the secret key for user: "<your name> (<message>) <<your email>>" 2048-bit RSA key, ID <pub_hash>, created 2017-02-28 Enter passphrase:

Verify a tag

Generate a build number

This command will generate for you a build number, not a release number. The release number is in fact the tag name!
Here is the format of the generated build number: <tagname>-<nb>-<hash> where <tagname> is the last annotated tag name (and signed tags are annotated), <nb> the number of commits since the tag and <hash> the abbreviate used commit hash. If the last signed tag is at HEAD, <nb> and <hash> are ommitted.
How to generate this number?

$ git describe <object>
<tagname>-<nb>-<hash>

<object> can be master, <branch-name>, <tag-name>, <commit-hash>, HEAD, HEAD^, … or nothing (which means HEAD).

You also can specific the size of abbreviate hash using the --abbrev=<n> option, where <n> is the size of the abbreviate hash. For example, the Linux kernel project is currently using --abbrev=10.

The output is not usable only for creating a release. You can use it as <object> for commands like checkout or show.

Build a release

Now it’s time to build your release. You can create a tarball and/or a zip archive of a specific snapshot:

$ git archive <object> | gzip > `git describe <object>`.tar.gz
$ git archive <object> --format=zip > `git describe <object>`.zip

where <object> can took the same values than for describe except that you must specify HEAD instead of nothing.

If you look into your project directory, you should show a ‘tar.gz’ and a ‘zip’ archives with the same name. There are the archives you just created!

You maybe want to only put the files from a specific subfolder in archives. It is possible with the --prefix option:

$ git archive <object> --prefix='<path>/' | gzip > `git describe <object>`.tar.gz
$ git archive <object> --prefix='<path>/' --format=zip > `git describe <object>`.zip

or put the archives in a specific folder:

$ git archive <object> | gzip > path/to/`git describe <object>`.tar.gz
$ git archive <object> --format=zip > path/to/`git describe <object>`.zip

I suggest you to create two aliases for archive, commands are long. Or create a script like this one (named git-arc.sh) in your working directory and run it:

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
#!/bin/sh

function usage
{
    echo "NAME"
    echo "git-arc: optionnaly tag, create tarball and zip release archives"
    echo ""
    echo "SYNOPSIS"
    echo "git-arc [-t tagname [-m message]] [what]"
    echo ""
    echo "OPTIONS"
    echo "  -t <tagname>"
    echo "    The tag name (optionnal). Tag will be create at 'what'"
    echo ""
    echo "  -m <message>"
    echo "    The message associate to the tag (optionnal). Default is \"release tag\""
    echo ""
    echo "  what"
    echo "    Position to create the tag and make archives. Can be 'master', '<commit_hash>', 'HEAD', 'HEAD^', ... Default is 'HEAD'"
}

tagname="no"
message="release tag"
what="HEAD"

# getting parameters
while [[ $1 ]]; do
    case $1 in 
        -t)                 shift
                            tagname=$1
                            ;;
        -m)                 shift
                            message=$1
                            ;;
        -h | --help)        usage
                            exit 0
                            ;;
        * )                 what=$1
                            ;;
    esac
    shift
done

# check if "releases" directory exists
if [ -d "releases/" ]; then
    mkdir releases
fi

# must create tag?
if [ "$tagname" != "no" ]; then
    echo "Create '"$tagname"' tag..."
    git tag -s $tagname $what -m "${message}"
    if [ $? != 0 ]; then
        echo "Error while creating tag"
        exit 1
    fi
fi

build=$(git describe $what)

echo "Building archives for "$build"..."
# tarball
git archive ${what} | gzip > releases/${build}.tar.gz
if [ $? != 0 ]; then
    echo "Error while creating tar.gz archive"
    exit 1
fi

# zip
git archive ${what} --format=zip > releases/${build}.zip
if [ $? != 0 ]; then
    echo "Error while creating zip archive"
    exit 1
fi

echo "Done"
exit 0
previous top home

Reminder

archive

Create a tarball archive from <object> (branch, tag, commit, HEAD, …)

$ git archive <object> | gzip > <archive>.tar.gz

Create a zip archive from <object> (branch, tag, commit, HEAD, …)

$ git archive <object> --format=zip > <archive>.zip

Create an archive from <object>’s subdir

$ git archive <object> --prefix='<path>/' | gzip > <archive>.tar.gz
$ git archive <object> --prefix='<path>/' --format=zip > <archive>.zip

describe

Describe an object (branch, tag, commit, …)

$ git describe <object>

Describe an object with the specified abbreviate hash size

$ git describe --abbrev=<n> <object>

tag

Create a signed tag

$ git tag -s <tagname> -m <message>

Create a signed tag by specifying user

$ git tag -u <sub_key_hash> <tagname> -m <message>
previous top home