#!/usr/bin/env zsh

# Automatically make .deb package from a commit or a tag.
#
# Prerequisites:
#     zsh, build-essential, devscripts, debhelper (>= 9)
#
# Reference: https://wiki.debian.org/IntroDebianPackaging.

setopt errexit noshwordsplit nobashrematch
[[ -n $DEBUG ]] && setopt xtrace

################### SET UP ENVIRONMENT AND BASE DIRECTORIES ####################

[[ -z $DEBFULLNAME ]] && DEBFULLNAME='Arun Prakash Jana'
[[ -z $DEBEMAIL ]] && DEBEMAIL='engineerarun@gmail.com'
[[ -z $TZ ]] && TZ='Asia/Kolkata'
export DEBFULLNAME DEBEMAIL TZ

here=$0:A:h
repodir=$here/..
builddir=$here/../build
distdir=$here/../dist

export GIT_DIR=$repodir/.git

################################# SET UP TRAPS #################################

# Trap SIGUSR1: Abort program when functions called from within command
# substitutions in heredocs fail.
trap 'print_error "Encountered problem inside cmdsubst at line $LINENO."; exit 1' SIGUSR1
export NOTIFY_PID=$$

############################### HELPER FUNCTIONS ###############################

print_error () print -R $'\e[31m'"Error: $*"$'\e[0m' >&2

print_warning () print -R $'\e[33m'"Warning: $*"$'\e[0m' >&2

# Usage: apt_package_version <package_name>
apt_package_version () {
    local version="${$(apt-cache show $1 | grep '^Version')#Version: }" || {
        print_error "Version info not available for package ${(q-)1}."
        [[ -n $NOTIFY_PID ]] && kill -SIGUSR1 $NOTIFY_PID
        exit 1
    }
    printf %s $version
}

debian_policy_version () {
    local full_version="$(apt_package_version debian-policy)"
    local match
    [[ $full_version =~ ^(([0-9]+\.){2}[0-9]+) ]] || {
        print_error "Invalid debian-policy version ${(q-)full_version}."
        [[ -n $NOTIFY_PID ]] && kill -SIGUSR1 $NOTIFY_PID
        exit 1
    }
    printf %s $match[1]
}

# Git helpers

# Usage: git_normalize_commitish <commit-ish>
#
# Normalize a commit-ish to a tag name if the commit-ish refers to a tag or
# tagged commit; otherwise to a SHA (via git-rev-parse).
git_normalize_commitish () {
    local tagname commitsha
    if tagname=$(git describe --exact --tags $1 2>/dev/null); then
        printf %s $tagname
    else
        commitsha=$(git rev-parse --verify --quite $1) || {
            print_error "Unable to parse ${(q-)1} as a git commit."
            [[ -n $NOTIFY_PID ]] && kill -SIGUSR1 $NOTIFY_PID
            exit 1
        }
        printf %s $commitsha
    fi
}

# Usage: git_ref_is_tag <refname>
#
# Returns 0 (is tag) or 1 (not tag).
git_ref_is_tag () {
    git show-ref --quiet --verify refs/tags/$1
}

# Usage: git_commitish_timestamp <normalized_commitish>
#
# Expects a normalized commit-ish (see git_normalize_commitish) as input, and
# outputs a timestamp. For a tag, the timestamp is the tagger date. For a
# commit, the timestamp is the committer date.
git_commitish_timestamp () {
    if git_ref_is_tag $1; then
        local date="$(git for-each-ref --format='%(taggerdate)' refs/tags/$1)"

        # The date returned by git-for-each-ref looks like `Sat Apr 23 10:38:27
        # 2016 +0530', which isn't recognized by date(1) from coreutils. Need a
        # hack to turn `+0530' into `U+0530' (same for -).
        #
        # Note that date will be empty if the tag at question is a lightweight
        # tag (i.e., non-annotated) because they don't carry any tagger
        # information; in that case we simply fall back to the committer.
        if [[ -n $date ]]; then
            date -d ${${date// -/ U-}// +/ U+} +%s
            return
        fi
    fi
    git rev-list --format=format:%ct --max-count=1 $1 | tail -n1
}

######################### PARSE COMMAND LINE ARGUMENTS #########################

tag_only=0
while [[ $1 == -* ]]; do
    case $1 in
        -h|--help)
            cat >&2 <<EOF
Usage: $0:t [options] [<commit-ish>]

Make a deb package from a git commit-ish, which defaults to HEAD.

Options:
    -h, --help
        Print this help and exit.
    --tag-only
        Only make deb if HEAD (or <commit-ish>, if specified) is a tag or a
        tagged commit.
EOF
            exit 1
            ;;
        --tag-only)
            tag_only=1
            ;;
        --)
            shift
            break
            ;;
        *)
            print_error "Unknown option ${(q-)1}."
            exit 1
            ;;
    esac
    shift
done

[[ -n $1 ]] && { commitish=$1; shift } || commitish=HEAD
(( $# > 0 )) && {
    print_error 'Unrecognized arguments' ${(q-)@}
    exit 1
}

##################################### MAIN #####################################

mkdir -p $builddir $distdir
cd $builddir

normalized_commitish=$(git_normalize_commitish $commitish)
git_ref_is_tag $normalized_commitish && commitish_is_tag=1 || commitish_is_tag=0

(( tag_only && !commitish_is_tag )) && {
    print_warning "${(q-)commitish} is not a tag or tagged commit, skipping build."
    exit
}

pkgname=googler
version="${$(git describe --tags $commitish)#v}" # Quoting just to make sh-mode happy
[[ -n $version ]] || {
    print_error 'Failed to extract version information.'
    exit 1
}
debrevision=1
creation_timestamp=$(git_commitish_timestamp $normalized_commitish)

upstream_tarball=$builddir/${pkgname}_${version}.orig.tar.gz
buildsubdirname=${pkgname}-${version}
git -C $repodir archive --format=tar.gz --prefix=$buildsubdirname/ --output=$upstream_tarball $commitish .
rm -rf $buildsubdirname
tar xf $upstream_tarball

cd $buildsubdirname
mkdir debian

# Write debian/changelog

if (( commitish_is_tag )); then
    # Tag -- point to the release
    changelog_url=https://github.com/jarun/googler/releases/tag/$normalized_commitish
else
    # Just a commit -- point to the list of commits in the tree
    changelog_url=https://github.com/jarun/googler/commits/$normalized_commitish
fi
cat >debian/changelog <<EOF
$pkgname (${version}-${debrevision}) UNRELEASED; urgency=medium

  * See full changelog at
    $changelog_url

 -- $DEBFULLNAME <$DEBEMAIL>  $(date --rfc-2822 --date=@$creation_timestamp)
EOF

# Alternatively, use dch to create changelog interactively:
#     dch --create -v ${version}-${debrevision} --package $pkgname

# Write debian/compat
cat >debian/compat <<'EOF'
9
EOF

# Write debian/control
cat >debian/control <<EOF
Source: $pkgname
Maintainer: $DEBFULLNAME <$DEBEMAIL>
Section: misc
Priority: optional
Standards-Version: $(debian_policy_version)
Build-Depends: debhelper (>= 9)

Package: $pkgname
Architecture: all
Depends: \${shlibs:Depends}, \${misc:Depends}, python3 (>= 3.3)
Description: Google Search and News from the command-line
 See https://github.com/jarun/googler#readme.
EOF

# Write debian/copyright
copyright_file=$builddir/$buildsubdirname/debian/copyright
cat >debian/copyright <<EOF
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: $pkgname
Upstream-Contact: $DEBFULLNAME <$DEBEMAIL>
Source: https://github.com/jarun/googler

Files: *
Copyright: 2008 Henri Hakkinen
           2015, 2016 Arun Prakash Jana
License: GPL-3
 This program is free software: you can redistribute it and/or modify it under
 the terms of the GNU General Public License version 3 as published by the Free
 Software Foundation.
 .
 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.
 .
 You should have received a copy of the GNU General Public License along with
 this program.  If not, see <http://www.gnu.org/licenses/>.
 .
 On Debian systems, the full text of the GNU General Public License version 3
 can be found in the file '/usr/share/common-licenses/GPL-3'.
EOF

# Write debian/rules
cat >debian/rules <<EOF
#!/usr/bin/make -f
%:
	dh \$@

override_dh_auto_install:
	\$(MAKE) DESTDIR=\$\$(pwd)/debian/$pkgname PREFIX=/usr install
	cp -p ${(q-)copyright_file} \$\$(pwd)/debian/$pkgname/usr/share/doc/$pkgname/copyright
EOF
chmod +x debian/rules

# Write debian/source/format
mkdir -p debian/source
cat >debian/source/format <<'EOF'
3.0 (quilt)
EOF

# Build binary package
debuild -us -uc

# Copying deb to dist
binary_package=$builddir/${pkgname}_${version}-${debrevision}_all.deb
cp -p $binary_package $distdir
