.. _spec_templates_from_scratch_v3:

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
    git add simple-template.spec.rpkg


Those commands will create a local git repository called `test-project`
containing our first spec template called ``simple-template.spec.rpkg``.

The last ``git add`` command is important to remember. ``rpkg`` ignores
files which are not tracked by git.

We can now try to invoke ``rpkg`` to generate a real spec file from the template:

::

    $ rpkg spec
    ERROR: git_dir_name: Could not get remote URL.
    git_dir_name failed with value 1.

...well, not so fast :).

Remote URL for the current branch should be set first or at least
the "origin" remote should be defined. That's because the ``git_dir_name``
macro determines the value of package name from the branch remote URL (and
falls back to "origin" URL if the current branch doesn't have a directly
associated remote yet).

For testing purposes, let's create the origin remote:

::

    git remote add origin ssh://git@pagure.io/test-project.git

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 to be configured appropriately to our needs.

Let's try again:

::

    $ rpkg spec
    Wrote: /tmp/rpkg/simple-template-2-kz8cqans/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.dirty.02mnis
    Release:    1%{?dist}
    Summary:    This is a test package.

    License:    GPLv2+
    URL:        https://someurl.org
    VCS:        git+ssh://git@pagure.io/test-project.git#:

    Source: test-project-master-dirty.tar.gz

    %description
    This is a test package.

    %prep
    %setup -T -b 0 -q -n test-project

    %changelog


In the resulting spec file, you can also see that the ``Version:`` field value is ``0.0``
followed by a curious ``.dirty.02mnis`` suffix. The fact that the suffix is present
means that our working tree is dirty. The ``02mnis`` substring encodes the time of
the latest uncommitted file status change.

You can also notice the ``dirty`` suffix (this time without the encoded timestamp)
denoting that the source was generated from a dirty repository.

The ``.dirty.*`` 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 commit -m 'first commit'

Viewing the commit:

::

    $ git log --oneline
    d4e72ea (HEAD -> master) first commit

Regenerating the spec file (this time just to stdout with ``-p`` switch):

::

    $ rpkg spec -p
    Name:       test-project
    Version:    0.0.git.1.d4e72eab
    Release:    1%{?dist}
    Summary:    This is a test package.

    License:    GPLv2+
    URL:        https://someurl.org
    VCS:        git+ssh://git@pagure.io/test-project.git#d4e72eabf843e99f4cf30b30762582f59b02f6b0:

    Source: test-project-d4e72eab.tar.gz

    %description
    This is a test package.

    %prep
    %setup -T -b 0 -q -n test-project

    %changelog


You can see that the ``.dirty.*`` suffix was replaced by ``.git.1.d4e72eab``.
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 ``d4e72eab`` part is the first eight characters of the full commit hash and
you can use this short hash to checkout the commit:

::

    git checkout d4e72eab

This is not very 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
    Created tag: test-project-0.1-1

The tag is created now.

.. 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 spec file now:

::

    $ rpkg spec -p
    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#d4e72eabf843e99f4cf30b30762582f59b02f6b0:

    Source: test-project-d4e72eab.tar.gz

    %description
    This is a test package.

    %prep
    %setup -T -b 0 -q -n test-project

    %changelog
    * Thu Apr 29 2021 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 there is ``VCS`` tag containing full pseudo-URL to
the given commit from which the generated spec file comes. You can extract
the VCS tag also from generated srpms and rpms (if you generate some) and
it is the best way how to identify the source for them.

Finally, 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``:

.. code-block:: spec

    Name:       {{{ git_dir_name }}}
    Version:    {{{ git_dir_version lead=1 }}}
    Release:    1%{?dist}
    ...

For our project this will now generate something like:

.. code-block:: spec

    Name:       test-project
    Version:    1.0.git.1.d4e72eab.dirty.02nl5m
    Release:    1%{?dist}
    ...

The version suffix is telling us from what commit it originates and
also that it is git work tree dirty at the moment after adding ``lead=1``
and not commiting yet.

Read more about the macros and their parameters in :ref:`macro_reference_v3`.

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-9-yuq_curi/test-project-d4e72eab-dirty.tar.gz
    Wrote: /tmp/rpkg/simple-template-9-yuq_curi/simple-template.spec
    setting SOURCE_DATE_EPOCH=1619654400
    Wrote: /tmp/rpkg/simple-template-9-yuq_curi/test-project-1.0.git.1.d4e72eab.dirty.02nl5m-1.fc31.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: archiving /home/clime/test-project:
    commit d4e72eabf843e99f4cf30b30762582f59b02f6b0 (HEAD, tag: test-project-0.1-1, master)
    Author: clime <clime@fedoraproject.org>
    Date:   Thu Apr 29 22:21:17 2021 +0200

        first commit
    git_dir_pack: Wrote: /tmp/rpkg/simple-template-10-xrg677vv/test-project-d4e72eab.tar.gz
    Wrote: /tmp/rpkg/simple-template-10-xrg677vv/simple-template.spec
    setting SOURCE_DATE_EPOCH=1619654400
    Wrote: /tmp/rpkg/simple-template-10-xrg677vv/test-project-0.1-1.fc31.src.rpm


Three files were created (see all the "Wrote:" lines). The source tarball, the generated
spec file and the source rpm that bundles the generated spec file and the tarball and can
be used for building an rpm installation package.

The names of the tarball and of the source rpm each contain information that can be
used to track them back to the original commit. In case of srpm, it is a tag name
and in case of the tarball, there is a short commit hash.

You can generate just a spec and a tarball with ``rpkg spec --sources``
command:

::

    git_dir_pack: archiving /home/clime/test-project:
    commit d4e72eabf843e99f4cf30b30762582f59b02f6b0 (HEAD, tag: test-project-0.1-1, master)
    Author: clime <clime@fedoraproject.org>
    Date:   Thu Apr 29 22:21:17 2021 +0200

        first commit
    git_dir_pack: Wrote: /tmp/rpkg/simple-template-11-stdtnrh4/test-project-d4e72eab.tar.gz
    Wrote: /tmp/rpkg/simple-template-11-stdtnrh4/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 ``rpkg srpm`` is called)
or when explicitly asked to do it (``rpkg spec --sources``) but not when
``rpkg spec`` or ``rpkg spec -p`` commands are used. This is a useful optimization.

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