Skip to content

version-check: parse .git_archival.txt as fallback when .git is absent - #524

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:pr-version-check-archive-fallback
May 4, 2026
Merged

version-check: parse .git_archival.txt as fallback when .git is absent#524
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:pr-version-check-archive-fallback

Conversation

@got3nks

@got3nks got3nks commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

GitHub's "Download ZIP" button and git archive produce source archives without a .git directory. Without .git, the existing CMake auto-detection block in CMakeLists.txt is skipped, so VERSION / PACKAGE_VERSION / PACKAGE_STRING all stay at the dev placeholders (GIT / SVN / aMule SVN), SVNDATE never gets set, and the resulting binary's banner reads:

Initialising aMule GIT compiled with wxOSX Cocoa v3.3.2 and Boost 1.90

— with no (Snapshot: rev. ...) trailer and no commit/tag info recoverable.

This PR uses the standard git archive mechanism (export-subst in .gitattributes) to bake metadata into archives at archive-creation time, then teaches CMake to read it as a fallback when .git is absent. After this lands, a zip download identifies itself the same way a .git-present clone of the same commit would — for both off-tag dev snapshots and tagged releases.

How it works

Three pieces

  1. .gitattributes — binds .git_archival.txt to export-subst, telling git archive to substitute $Format:...$ placeholders in that file at archive time.

  2. .git_archival.txt — a small template with four placeholders:

    node: $Format:%H$
    node-date: $Format:%cI$
    describe-name: $Format:%(describe:tags=true)$
    ref-names: $Format:%D$
    

    In the working tree (git clone), these stay as the literal $Format:...$ strings. In an archive produced by git archive (or downloaded via GitHub's "Download ZIP"), they're substituted with the actual SHA / commit date / git describe --tags output / ref names.

  3. CMakeLists.txt — added an else branch to if (GIT_FOUND AND EXISTS .git). When .git is absent and .git_archival.txt is present in substituted form, parse describe-name: and feed it into the same VERSION / SVNDATE / AMULE_TAGGED_RELEASE machinery the .git path uses.

Detection logic

if .git exists:
    use git describe (existing behaviour, unchanged)
else if .git_archival.txt exists with substituted content:
    parse describe-name:
        if matches "<tag>-<N>-g<sha>" form (off-tag):
            SVNDATE = "rev. <describe-output>"   →  Snapshot trailer
        else (exact-tag form):
            VERSION = PACKAGE_VERSION = describe-output
            PACKAGE_STRING = "aMule <describe-output>"
            AMULE_TAGGED_RELEASE = 1            →  release-form banner
else:
    fall through to CMakeLists.txt defaults    →  generic dev banner

Net effect by source-of-build

How the source got onto the user's disk Banner the binary self-reports
git clone (any branch / commit, off-tag) aMule GIT ... (Snapshot: rev. <describe-output>) (existing)
git clone of tagged commit aMule <tag> ... (existing)
GitHub "Download ZIP" of master HEAD aMule GIT ... (Snapshot: rev. <describe-output>) (new)
GitHub "Download ZIP" of a tagged release aMule <tag> ... (new)
git archive --format=tar v3.0.0 by distro packager aMule 3.0.0 ... (new)
Manual tar czf src.tar.gz src/ (no git archive) aMule GIT ... (template still un-substituted, file ignored, defaults retained)
Tarball with packager passing -DPACKAGE_VERSION=… etc. on the cmake command line unchanged — explicit -D overrides take precedence (existing distro-packager contract)

How to use it at release time

Maintainer's workflow when tagging a release: no change.

  1. Tag a commit on master: git tag -a 3.0.0 -m "release 3.0.0"
  2. Push the tag: git push origin 3.0.0
  3. The release.yml workflow fires (from ci: add tag-triggered release.yml workflow #520) and produces draft GitHub Release with the platform artifacts.
  4. Maintainer publishes (un-drafts) the Release on the GitHub side.

That's it. The substitution mechanism kicks in automatically:

  • GitHub's "Download ZIP" button on the tag's release page (or on the commit) → GitHub runs git archive server-side → substituted .git_archival.txt baked into the zip → users who download and build get a clean aMule 3.0.0 banner.
  • GitHub's "Source code" tarball on the release page → same mechanism, GitHub uses git archive.
  • Distro packagers running git archive --format=tar.gz v3.0.0 -o amule-3.0.0.tar.gz get the same self-identifying tarball; downstream Debian / Fedora / openSUSE / etc. builds can drop their per-distro -DPACKAGE_VERSION=... cmake overrides if they want (the existing override path still works for backward compat).

Verifying the substitution worked (sanity check after pushing a tag):

# Pick the tag you want to verify, then:
mkdir /tmp/verify && \
  git archive --format=tar 3.0.0 .git_archival.txt | tar -x -C /tmp/verify
cat /tmp/verify/.git_archival.txt
# Expect lines like:
#   node: <full SHA>
#   describe-name: 3.0.0
#   ref-names: HEAD -> master, tag: 3.0.0

Or just: download the GitHub "Source code (zip)" attachment for the release, extract, look at .git_archival.txt — it should have real values, not $Format:...$ strings.

Test plan

Verified locally on macOS via four configure paths:

  • .git present, off-tag (master tip) → STATUS line git revision rev. 2.3.3-297-g25a194d41 found; config.h: SVNDATE set, AMULE_TAGGED_RELEASE undef. Existing path, unchanged.
  • No .git, archival file with off-tag describe (describe-name: 2.3.3-296-ge3f87f77f) → STATUS line git archival metadata: rev. 2.3.3-296-ge3f87f77f found; same config.h shape as the .git path → identical banner output.
  • No .git, archival file with exact-tag describe (describe-name: 3.0.0) → STATUS line git archival metadata: tagged release aMule 3.0.0; config.h: AMULE_TAGGED_RELEASE defined, VERSION="3.0.0", PACKAGE_STRING="aMule 3.0.0", SVNDATE undef → release-form banner.
  • No .git, archival file unsubstituted (template still has $Format:...$ markers) → file detected as un-substituted, ignored; falls to CMakeLists defaults.
  • End-to-end git archive run: git archive HEAD .gitattributes .git_archival.txt | tar -x produced a substituted file; cmake configured against that source tree (no .git); built amuled; ran amuled --version → banner aMuleD GIT compiled with wxBase(OSX Cocoa) v3.3.2 and Boost 1.90 (Snapshot: rev. 2.3.3-298-g666c8e2a2) (OS: macOS) — identical to a .git-present build of the same commit.

Sized

Three files added, one file edited (CMakeLists.txt +50 LOC for the fallback block + the CONFIGURE_DEPENDS registration). Single commit. No conflicts with anything in flight.

GitHub's "Download ZIP" button and `git archive` produce source
archives without a `.git` directory.  Without that directory the
existing CMakeLists.txt auto-detection block — gated on
`EXISTS "${CMAKE_SOURCE_DIR}/.git"` — is skipped, so VERSION,
PACKAGE_VERSION, PACKAGE_STRING all stay at the dev placeholders
("GIT" / "SVN" / "aMule SVN"), SVNDATE never gets set, and the
resulting binary's banner reads:

  Initialising aMule GIT compiled with ...

with no `(Snapshot: rev. ...)` trailer and no version info beyond
the integer triplet from ClientVersion.h.  That's been the behaviour
for as long as the cmake build has existed — works, but the binary
can't self-identify which commit it was built from.

`git archive` has a built-in mechanism for embedding metadata into
archives: files marked with `export-subst` in `.gitattributes` get
their `$Format:...$` placeholders substituted at archive-creation
time.  This is the standard idiom (used by setuptools-scm and a number
of CMake projects) for making zip / tarball downloads self-identifying.

This commit adds the three pieces:

 - `.gitattributes` binds `.git_archival.txt` to `export-subst`.
 - `.git_archival.txt` is a tiny template file with four
   `$Format:...$` placeholders (full SHA, commit date,
   `git describe --tags` output, ref names).  `git archive`
   substitutes these at archive time; a working-tree copy of the file
   keeps the literal `$Format:` markers as the signal that no
   archive substitution has happened.
 - CMakeLists.txt: added an `else` branch to the
   `if (GIT_FOUND AND EXISTS .git)` block.  When .git is absent and
   `.git_archival.txt` exists in substituted form, parse the
   `describe-name:` field and feed it into the same VERSION /
   SVNDATE / AMULE_TAGGED_RELEASE machinery the .git path uses.

Detection logic:

 - If `.git_archival.txt` still contains literal `$Format:`,
   it's an unsubstituted template (e.g. someone tar'd up `src/`
   without running `git archive`).  Fall through to defaults — the
   file is ignored.
 - Otherwise, parse `describe-name`.  An off-tag `git describe`
   output is the `<tag>-<N>-g<sha>` form (e.g.
   `2.3.3-296-ge3f87f77f`); detect by the trailing `-N-g<hex>`
   regex and treat as SVNDATE for the snapshot trailer.  An exact-tag
   describe output is just the tag itself; treat as
   AMULE_TAGGED_RELEASE and feed into VERSION / PACKAGE_VERSION /
   PACKAGE_STRING the same way the .git tag-detection block does.

Net effect: a zip download of a tagged release reads as
`aMule 3.0.0 compiled with ...` (no Snapshot trailer), and a zip
download of master tip reads as
`aMule GIT compiled with ... (Snapshot: rev. <describe-output>)` —
identical to what a .git-present clone of the same commit would
produce.

Verified locally on macOS via four configure-paths:

 - `.git` present, off-tag → SVNDATE='rev. 2.3.3-NNN-gXXX' (existing
   path, unchanged).
 - No `.git`, archival file with off-tag describe → SVNDATE matches
   the same shape as the .git path.
 - No `.git`, archival file with exact-tag describe ("3.0.0") →
   AMULE_TAGGED_RELEASE=1, VERSION=3.0.0, PACKAGE_STRING='aMule 3.0.0',
   SVNDATE undef → release-form banner.
 - No `.git`, archival file unsubstituted (template form) → file
   ignored, defaults retained.

Plus an end-to-end `git archive`-and-build run that produced a
working amuled binary self-identifying as
`aMuleD GIT compiled with ... (Snapshot: rev. 2.3.3-NNN-gXXX) (OS: macOS)`
from a .git-stripped source tree.
@mrjimenez
mrjimenez merged commit 4edfcbc into amule-project:master May 4, 2026
12 checks passed
@got3nks
got3nks deleted the pr-version-check-archive-fallback branch May 4, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants