# Test that the version of a binary is stamped using git tag information.
# See https://go.dev/issue/50603

[short] skip 'constructs a local git repo'
[!git] skip

# Redirect git to a test-specific .gitconfig.
# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer.
# For older git versions we also set $HOME.
env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig
env HOME=$WORK${/}home${/}gopher
exec git config --global --show-origin user.name
stdout 'Go Gopher'

cd $WORK/repo
# Use devel when git information is missing.
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+\(devel\)'
rm example$GOEXE

env GIT_AUTHOR_NAME='Go Gopher'
env GIT_AUTHOR_EMAIL='gopher@golang.org'
env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL

exec git init
env GIT_COMMITTER_DATE=2022-07-19T11:07:00-04:00
env GIT_AUTHOR_DATE=2022-07-19T11:07:00-04:00
exec git add .
exec git commit -m 'initial commit'
exec git branch -m main

# Use a 0.0.0 pseudo-version when no tags are present.
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v0.0.0-20220719150700-e7537ba8fd6d\s+'
rm example$GOEXE

# Use a 0.0.0 pseudo-version if the current tag is not a valid semantic version.
exec git tag 1.0.1
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v0.0.0-20220719150700-e7537ba8fd6d\s+'
rm example$GOEXE

# Use the current tag which has a valid semantic version to stamp the version.
exec git tag v1.0.1
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.1\s+'
rm example$GOEXE

# Use tag+dirty when there are uncommitted changes present.
cp $WORK/copy/README $WORK/repo/README
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.1\+dirty\s+'
rm example$GOEXE

env GIT_COMMITTER_DATE=2022-07-19T11:07:01-04:00
env GIT_AUTHOR_DATE=2022-07-19T11:07:01-04:00
exec git add .
exec git commit -m 'commit 2'

# Use the updated tag to stamp the version.
exec git tag v1.0.2
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.2\s+'
rm example$GOEXE

env GIT_COMMITTER_DATE=2022-07-19T11:07:02-04:00
env GIT_AUTHOR_DATE=2022-07-19T11:07:02-04:00
mv README README2
exec git add .
exec git commit -m 'commit 3'

# Use a pseudo-version when current commit doesn't match a tagged version.
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.3-0.20220719150702-b0226f18a7ae\s+'
rm example$GOEXE

# Use pseudo+dirty when uncommitted changes are present.
mv README2 README3
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.3-0.20220719150702-b0226f18a7ae\+dirty\s+'
rm example$GOEXE

# Make sure we always use the previously tagged version to generate the pseudo-version at a untagged revision.
env GIT_COMMITTER_DATE=2022-07-19T11:07:03-04:00
env GIT_AUTHOR_DATE=2022-07-19T11:07:03-04:00
exec git add .
exec git commit -m 'commit 4'

mv README3 README4
env GIT_COMMITTER_DATE=2022-07-19T11:07:04-04:00
env GIT_AUTHOR_DATE=2022-07-19T11:07:04-04:00
exec git add .
exec git commit -m 'commit 5'
exec git tag v1.0.4
# Jump back to commit 4 which is untagged.
exec git checkout ':/commit 4'
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v1.0.3-0.20220719150703-2ebc76937b49\s+'
rm example$GOEXE

# Create +incompatible module
exec git checkout v1.0.4
exec git rm go.mod
exec git commit -m 'commit 6'
exec git tag v2.0.0
exec git checkout HEAD^ go.mod
# And make the tree +dirty
mv README4 README5
go build
go version -m example$GOEXE
stdout '\s+mod\s+example\s+v2.0.0\+incompatible.dirty\s+'
rm example$GOEXE

# Make sure v2 works as expected.
exec git checkout v1.0.4
go mod edit -module example/v2
exec git add .
exec git commit -m 'commit 7'
exec git tag v2.1.1
go build
go version -m example$GOEXE
stdout '\s+mod\s+example/v2\s+v2.1.1\s+'
rm example$GOEXE

# v2+dirty
mv README5 README6
go build
go version -m example$GOEXE
stdout '\s+mod\s+example/v2\s+v2.1.1\+dirty\s+'
rm example$GOEXE

# v2+pseudo
exec git add .
exec git commit -m 'commit 8'
go build
go version -m example$GOEXE
stdout '\s+mod\s+example/v2\s+v2.1.2-0.20220719150704-0ebeb94ecde2\s+'
rm example$GOEXE

# v2+pseudo+dirty
mv README6 README7
go build
go version -m example$GOEXE
stdout '\s+mod\s+example/v2\s+v2.1.2-0.20220719150704-0ebeb94ecde2\+dirty\s+'
rm example$GOEXE

# modules in subdirectories should be stamped with the correct tag
exec git add .
cd subdir
exec git commit -m 'commit 9'
go build
go version -m subdir$GOEXE
# missing tag creates a pseudo version with v2.0.0
stdout '\s+mod\s+example/subdir/v2\s+v2.0.0-20220719150704-fbef6799938f\s+'
rm subdir$GOEXE
# tag with subdir
exec git tag subdir/v2.1.0
go build
go version -m subdir$GOEXE
stdout '\s+mod\s+example/subdir/v2\s+v2.1.0\s+'
# v2+dirty
mv ../README7 README8
go build
go version -m subdir$GOEXE
stdout '\s+mod\s+example/subdir/v2\s+v2.1.0\+dirty\s+'
rm subdir$GOEXE

# modules in a subdirectory without a go.mod in the root should result in (devel)
rm ../go.mod
go build
go version -m subdir$GOEXE
stdout '\s+mod\s+example/subdir/v2\s+\(devel\)\s+'
rm subdir$GOEXE

-- $WORK/repo/go.mod --
module example

go 1.18
-- $WORK/repo/main.go --
package main

func main() {
}
-- $WORK/copy/README --
hello

-- $WORK/repo/subdir/go.mod --
module example/subdir/v2

go 1.18

-- $WORK/repo/subdir/main.go --
package main

func main() {
}

-- $WORK/home/gopher/.gitconfig --
[user]
    name = Go Gopher
    email = gopher@golang.org
