Spec templates from scratch¶
To setup the test environment, run the following commands:
mkdir test-project
cd test-project
cat > simple-template.spec.rpkg <<EOF
Name: {{{ git_dir_name }}}
Version: {{{ git_dir_version }}}
Release: 1%{?dist}
Summary: This is a test package.
License: GPLv2+
URL: https://someurl.org
VCS: {{{ git_dir_vcs }}}
Source: {{{ git_dir_pack }}}
%description
This is a test package.
%prep
{{{ git_dir_setup_macro }}}
%changelog
{{{ git_dir_changelog }}}
EOF
git init
Those commands will create a local git repository called test-project
containing our first spec template called simple-template.spec.rpkg
.
We can now try to invoke rpkg
to generate a real spec file from the template:
$ rpkg spec
git_dir_name: Could not get remote URL.
git_dir_name failed with value 2
…well, not so fast :).
Remote URL for the current branch should be set first and that’s
because the git_dir_name
macro determines the value of package name
from that URL.
For testing purposes, we can set it up by running:
git remote add origin ssh://git@pagure.io/test-project.git
git config --add branch.master.remote origin
Note that we don’t really need pagure.io/test-project.git project to exist publicly for the following parts to work. We only need the local Git repository variables to be set appropriately to our needs.
Let’s try again:
$ rpkg spec
Wrote: /tmp/rpkg/simple-template-2-ck20yI/simple-template.spec
Voilà! You can see that the real spec was generated into a directory under
/tmp/rpkg
, which is a default output path. Name of the directory is
derived from the name of the spec file and count (plus one) of already
existing directories under /tmp/rpkg
for that particular spec file name.
There is also a dynamic suffix appended to ensure directory name uniqueness.
A new directory is created for each new .spec generating operation.
$ cat /tmp/rpkg/simple-template-2-ck20yI/simple-template.spec
Name: test-project
Version: 0.0.wtree.zf4r62
Release: 1%{?dist}
Summary: This is a test package.
License: GPLv2+
URL: https://someurl.org
VCS: git+ssh://git@pagure.io/test-project.git#HEAD:
Source: test-project-0.0.wtree.zf4r62.tar.gz
%description
This is a test package.
%prep
%setup -q -n test-project-0.0.wtree.zf4r62
%changelog
In the resulting spec file, you can also see that the Version:
field value is 0.0
followed by a curious .wtree.zf4r62
suffix. The fact that the suffix is present
means that our working tree is dirty. The zf4r62
substring encodes the time of
the latest uncommitted file status change.
You can also notice that the same suffix is present in the Source
filename
as well as in the parameter given to %setup -n
macro. That’s because the output
of git_dir_version
is remembered and the subsequent rpkg macro invocations
may use it as part of their own output value.
The .wtree.*
suffix will disappear once we make our first commit (see below).
The version will stay at 0.0
, however, and it will be incremented later to
0.1
when we make our first tag.
Now let’s make the first commit in our test project and see what happens:
git add simple-template.spec.rpkg
git commit -m 'first commit'
Viewing the commit:
$ git log --oneline
dadef2a (HEAD -> master) first commit
Regenerating the spec file:
rpkg spec
Finally getting:
Name: test-project
Version: 0.0.git.1.dadef2a
Release: 1%{?dist}
Summary: This is a test package.
License: GPLv2+
URL: https://someurl.org
VCS: git+ssh://git@pagure.io/test-project.git#dadef2a8b9554e94797a7336261192e02d5d9351:
Source: test-project-0.0.git.1.dadef2a.tar.gz
%description
This is a test package.
%prep
%setup -q -n test-project-0.0.git.1.dadef2a
%changelog
You can see that the .wtree.*
suffix was replaced by .git.1.dadef2a
.
This suffix is based on the latest commit in the currently checked out branch.
The number 1
is given by number of commits from the latest git tag created
for our test-project
package or from the repository initialization if no
such tag exists yet (our case).
The dadef2a
part is the first seven characters of the full commit hash and you can
use this short hash to checkout the commit:
git checkout dadef2a
This is not useful at the moment because we have just made the commit and we have it checked out already in our repository but it becomes useful when you build a source rpm from the generated spec file.
That’s because the value in the spec file’s version field gets propagated into the built srpm name, which means that if you store that srpm somewhere and you will later come back to it, you will know from which commit it was built and what it contains consequently.
Now let’s examine how tagging works with rpkg
because tagging becomes actually very handy
when your application reaches a certain stable (feature-complete) state that you would like
to share with people. We will need to assume that we have already reached that state after
just the first commit in our test-project :) (which is very rare).
rpkg tag
First thing that happens is that an editor window pops up where you can edit a tag message:
- first commit
#
# Write a changelog for tag:
# test-project-0.1-1
# Lines starting with '#' will be ignored.
The intial content of the tag message gets populated from the first lines of all commit messages since the last tag. Because we don’t have any existing tag before this one, commit messages since the repository initialization are collected and used to populate the initial message content.
You can keep this pre-generated tag message as it is, edit it in any way, or just delete it and write your own stuff but note that this message constitutes the basic information about our application release (and what have changed since the previous realease), so it is quite important.
After you are finally satisfied with your message, you can save the content and close the editor. The new tag will be generated:
$ rpkg tag
Wrote: /tmp/rpkg/simple-template-4-7Rh6hh/simple-template.spec
Created tag: test-project-0.1-1
Wrote: /tmp/rpkg/simple-template-4-7Rh6hh/simple-template.spec
You might wonder about the same two lines Wrote: /tmp/rpkg/simple-template-4-7Rh6hh/simple-template.spec
.
The first line is there because the tag name is derived from the Name:, Version:,
and Release: field values in the generated spec file. The second line is there
because the spec needs to be regenerated after the tag creation so that it contains
the latest changelog record.
Note
EXPERT INFO: When tagging takes place, rpkg
sets a special variable VERSION_BUMP
into the spec preprocessing environment. git_dir_version
macro is aware of this variable
and if it is set, an incremented version value is generated. This incremented value
then gets carried over into the tag name together with the package name and release.
Let’s view the final spec file now:
$ cat /tmp/rpkg/simple-template-4-7Rh6hh/simple-template.spec
Name: test-project
Version: 0.1
Release: 1%{?dist}
Summary: This is a test package.
License: GPLv2+
URL: https://someurl.org
VCS: git+ssh://git@pagure.io/test-project.git#dadef2a8b9554e94797a7336261192e02d5d9351:
Source: test-project-0.1.tar.gz
%description
This is a test package.
%prep
%setup -q -n test-project-0.1
%changelog
* Wed Mar 07 2018 clime <clime@fedoraproject.org> 0.1-1
- first commit
You can see that no dynamic version suffix has been generated into the
Version:
field this time. This happens every time the currently
checked out commit is a tagged one.
The reason is that if you know a tag name, you can just
directly pass it to git checkout
:
$ git checkout test-project-0.1
and you will get back to the respective commit for which the tag was created.
You can also see that the changelog has been automatically generated for us based on the currently existing tags and their content.
If you don’t like this and you prefer editing your changelog messages
directly in the source spec file instead of having them generated from tags,
no problem. Just don’t use the git_dir_changelog
macro and instead do your
edits directly in the spec file template. Any normal text (not enclosed in
{{{
, }}}
brace triplets) will be copied verbatim into the resulting
spec file.
If you like the git_dir_changelog
macro but you don’t have every single
changelog message stored in a tag, you can configure it to generate
changelog entries only since a certain point in history by specifying its
since_tag
parameter (e.g. git_dir_changelog since_tag=test-project-0.1-1
).
There are plenty of parameters also for the other macros to give you that same freedom you experience when you are dealing with just plain spec files.
For example, you can increment the major version number (the first number in
the version string) by setting lead
parameter of git_dir_version
:
Name: {{{ git_dir_name }}}
Version: {{{ git_dir_version lead=1 }}}
Release: 1%{?dist}
...
For our project this will now generate something like:
Name: test-project
Version: 1.0.wtree.zf4657
Release: 1%{?dist}
...
The .wtree.*
suffix is telling us that our working tree
is dirty again after adding lead=1
and not commiting yet.
Read more about the macros and their parameters in Macro reference.
And of course, you can fallback to using the plain spec files (without any rpkg macros) at any time, although you will lose the advantage of the certain spec parts being generated automatically for you.
So far, we have only seen generating spec from a template but what if we want to generate a whole source package.
We can do that with rpkg srpm
:
$ rpkg srpm
git_dir_pack: packing path /home/clime/test-project
git_dir_pack: Wrote: /tmp/rpkg/simple-template-5-H5AuzX/test-project-1.0.wtree.zgwsnr.tar.gz
Wrote: /tmp/rpkg/simple-template-5-H5AuzX/simple-template.spec
Wrote: /tmp/rpkg/simple-template-5-H5AuzX/test-project-1.0.wtree.zgwsnr-1.fc27.src.rpm
Now, actually we don’t usually want to build an srpm from a dirty tree because srpm is something, which eventually gets built into a final installable rpm package.
It should be always possible to track that rpm package back to a particular commit in a git history, so that you can look at the exact sources it was built from. This becomes impossible if we build an srpm from a dirty tree. It might be still useful for local development, however.
So let’s reset to the latest commit (which is also a tagged one) and continue from there:
$ git reset --hard test-project-0.1-1
HEAD is now at dadef2a first commit
$ rpkg srpm
git_dir_pack: packing path /home/clime/test-project
git_dir_pack: Wrote: /tmp/rpkg/simple-template-6-gtzDQX/test-project-0.1.tar.gz
Wrote: /tmp/rpkg/simple-template-6-gtzDQX/simple-template.spec
Wrote: /tmp/rpkg/simple-template-6-gtzDQX/test-project-0.1-1.fc27.src.rpm
Three files were created. The spec (you can see the relevant log message on the second line). The tarball that packs all our application sources for the current commit (on the first line). And the source rpm that bundles the generated spec file and the tarball and can be used for building an rpm installation package (the third line).
Note that the names of the tarball and of the source srpm match the tag name of the currently checked out commit as they should.
You can generate just a spec and a tarball with rpkg spec --sources
command:
$ rpkg spec --sources
git_dir_pack: packing path /home/clime/test-project
git_dir_pack: Wrote: /tmp/rpkg/simple-template-7-b9rah4/test-project-0.1.tar.gz
Wrote: /tmp/rpkg/simple-template-7-b9rah4/simple-template.spec
In the previous examples, we could see that git_dir_pack
macro
generates the source tarball only when needed (e.g. when calling
rpkg srpm
) or when explicitly asked to do it (rpkg spec --sources
).
This is a useful optimization.
By the way, there is also git_dir_archive
rpkg macro (which uses
git archive
command instead of tar
to do the packing), which behaves
the same way in this regard. With this macro, however, all your changes
need to be committed before you can generate a tarball or a source rpm.
In this tutorial, we have learned:
how to create a simple spec file template
how to generate a real rpm spec from the template
how the spec is automatically following git history
how to tag a commit and create release notes
how to generate a source tarball and even source rpm