# file      : tests/release.testscript
# license   : MIT; see accompanying LICENSE file

.include common.testscript project.testscript

# bdep-release requirements for the minimum supported git version are higher
# then the default 2.1.0 (see bdep/release.cxx for details).
#
+if! ($git_version_major >  2 || \
      $git_version_major == 2 && $git_version_minor >= 11)
  exit

g = [cmdline] git 2>! >&2

# Create the remote repository.
#
+mkdir --no-cleanup prj.git
+$g -C prj.git -c init.defaultBranch=master init --bare &prj.git/***

clone_rep      = [cmdline] cp --no-cleanup -pr ../prj.git ./ &prj.git/***
clone_root_rep = [cmdline] cp --no-cleanup -pr $~/prj.git ./ &prj.git/***

# Prepare the local repository.
#
# Set the origin repository location as a relative path, so we can copy the
# local/remote repository pair into the testscript scopes. Also disable hooks,
# which can potentially be broken, not to interfere with the git commands
# (commit, etc) which bdep-release calls internally.
#
# Note that it would be cleaner to add support for the --git-option option, so
# that we wouldn't need to override the hooks and, for example, just pass
# through the --no-verify option to git-commit. However, since we normally run
# multiple different git commands during bdep run, we would also need to
# support the qualified option values (see bpkg's --openssl-option for an
# example). Feels a bit hairy for now, so maybe later.
#
gp  = [cmdline] $g -C prj
log = [cmdline] $gp log '--pretty=format:"%d %s"'

+$gp config user.name 'Test Script'
+$gp config user.email 'testscript@example.com'
+$gp config core.hooksPath ''
+$gp remote add origin ../prj.git
+$gp add '*'
+$gp commit -m 'Create'
+$gp push --set-upstream origin master

clone_prj      = [cmdline] cp --no-cleanup -pr ../prj ./ &prj/***
clone_root_prj = [cmdline] cp --no-cleanup -pr $~/prj ./ &prj/***

clone_repos      = [cmdline] $clone_prj && $clone_rep
clone_root_repos = [cmdline] $clone_root_prj && $clone_root_rep

release += 2>!

# As a common approach we will be using a second local repository to validate
# the result of the release operation(s), that is normally pushed to the
# remote repository.
#
gp2    = [cmdline] $g -C prj2
clone2 = [cmdline] $g clone prj.git prj2 &prj2/***
pull2  = [cmdline] $gp2 pull --ff-only
fetch2 = [cmdline] $gp2 fetch
log2   = [cmdline] $gp2 log '--pretty=format:"%d %s"'

new += --vcs git,branch=master 2>-

: single-pkg
:
{{
  : release
  :
  {{
    test.arguments += --yes

    : version
    :
    {{
      test.arguments += --push

      : patch
      :
      {
        $clone_root_repos

        $* 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        cat prj2/manifest >>~%EOO%
          %.*
          name: prj
          version: 0.2.0-a.0.z
          language: c++
          %.*
          EOO
      }

      : alpha
      :
      {
        $clone_root_repos

        $* --alpha 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0-a.1
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-a.1.z%d
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO
      }

      : beta
      :
      {
        $clone_root_repos

        $* --beta 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0-b.1
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d
           (tag: v0.1.0-b.1) Release version 0.1.0-b.1
           Create
          EOO
      }

      : minor
      :
      {
        $clone_root_repos

        $* --minor 2>>~%EOE%
          %.+
          pushing branch master, tag v0.2.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.3.0-a.0.z%d
           (tag: v0.2.0) Release version 0.2.0
           Create
          EOO
      }

      : major
      :
      {
        $clone_root_repos

        $* --major 2>>~%EOE%
          %.+
          pushing branch master, tag v1.0.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 1.1.0-a.0.z%d
           (tag: v1.0.0) Release version 1.0.0
           Create
          EOO
      }
    }}

    : open
    :
    {{
      test.arguments += --push

      : beta
      :
      {
        $clone_root_repos

        $* --open-beta 2>'error: --open-beta specified for final current version 0.1.0' != 0

        $* --alpha --open-beta 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0-a.1
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.0.z%d
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO

        $* --beta --open-beta 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0-b.1
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d
           (tag: v0.1.0-b.1) Release version 0.1.0-b.1
           Change version to 0.1.0-b.0.z
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO
      }

      : patch
      :
      {
        $clone_root_repos

        $* --open-patch 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.1-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        # Note: --open-patch is implied for bugfix release series.
        #
        $* 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.1
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.2-a.0.z%d
           (tag: v0.1.1) Release version 0.1.1
          % Change version to 0.1.1-a.0.z%
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : minor
      :
      {
        $clone_root_repos

        $* --alpha --open-minor 2>'error: --open-minor specified for alpha current version 0.1.0-a.1' != 0
        $* --beta  --open-minor 2>'error: --open-minor specified for beta current version 0.1.0-b.1' != 0

        $* --open-patch 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $* 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.1
          EOE

        $* --open-minor 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.2
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.2) Release version 0.1.2
           Change version to 0.1.2-a.0.z
           (tag: v0.1.1) Release version 0.1.1
           Change version to 0.1.1-a.0.z
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : major
      :
      {
        $clone_root_repos

        $* --open-major 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 1.0.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : base
      :
      {
        $clone_root_repos

        $* --open-base 1.0 2>>EOE != 0
          error: invalid base version '1.0': '.' expected after minor version
          EOE

        $* --open-base 1.0.0-a.1 2>>EOE != 0
          error: invalid base version '1.0.0-a.1': pre-release
          EOE

        $* --open-base 1.2.3 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 1.2.3-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $* --open-base 1.2.2 2>>EOE != 0
          error: base version 1.2.2 is less than or equal to current version 1.2.3
          EOE

        $* --open-base 1.2.3 2>>EOE != 0
          error: base version 1.2.3 is less than or equal to current version 1.2.3
          EOE
      }
    }}

    : no
    :
    {{
      test.arguments += --push

      : open
      :
      {
        $clone_root_repos

        $* --no-open 2>>~%EOE%
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --open 2>>~%EOE%
          %.+
          pushing branch master
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : tag
      :
      {
        $clone_root_repos

        $* --no-tag 2>>~%EOE%
          %.+
          pushing branch master
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           Release version 0.1.0
           Create
          EOO

        $* --tag 2>>EOE != 0
          error: current version 0.2.0-a.0.z is a snapshot
            info: use --force=snapshot to tag anyway
          EOE

        # While at it, verify that the --current-tag is forbidden for a
        # non-revision release.
        #
        $* --tag --force=snapshot --current-tag=update 2>>EOE != 0
          error: --current-tag specified for non-revision current version 0.2.0-a.0.z
          EOE

        $* --tag --force=snapshot 2>>~%EOE%
          %pushing tag v0.2.0-a.0.\.+%d
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.2.0-a.0.\.*.\.*\) Change version to 0.2.0-a.0.z%d
           Release version 0.1.0
           Create
          EOO
      }

      : tag-open
      :
      {
        $clone_root_repos

        $* --no-tag --no-open 2>>~%EOE%
          %.+
          pushing branch master
          EOE

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --tag 2>>EOE
          pushing tag v0.1.0
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --open 2>>~%EOE%
          %.+
          pushing branch master
          EOE

        $pull2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : commit
      :
      {
        $clone_root_repos

        $* --no-commit 2>'error: both --push and --no-commit specified' != 0
      }
    }}

    : validate-manifest
    {{
      test.options += -q
      test.arguments += --push

      : file-value
      :
      {
        $clone_root_repos

        rm prj/README.md
        $gp commit -a -m 'Remove description'

        $* 2>>~%EOE% != 0
          %error: unable to read .+README\.md referenced by description-file manifest value in .+manifest: .+%
          EOE

        touch prj/README.md
        $gp add README.md
        $gp commit -m 'Add README.md file'

        $* 2>>~%EOE% != 0
          %error: description-file manifest value in .+manifest references empty file .+README.md%
          EOE

        echo '.' >= prj/README.md
        $gp commit -a -m 'Fill README.md file'

        $*
      }

      : unknown-value
      :
      {
        $clone_root_repos

        echo 'some-file: README' >+ prj/manifest
        $gp commit -a -m 'Add file'

        $* 2>>~%EOE% != 0
          %error: invalid package manifest: .+manifest:.+:1: unknown name 'some-file' in package manifest%
          EOE
      }
    }}

    : unstaged
    :
    {
      $clone_root_repos
      echo '' >+ prj/manifest

      $* 2>>EOE != 0
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --no-commit

      $gp commit -a -m 'Release version'

      $* --tag --push 2>>EOE
        pushing branch master, tag v0.1.0
        EOE

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0, \.*\) Release version%d
         Create
        EOO
    }
  }}

  : revision
  :
  {{
    test.options += -q

    +$clone_root_repos
    +$* --no-open --push --yes

    test.arguments += --revision

    : current-tag
    :
    {{
      +$clone_repos

      +echo '' >+ prj/manifest
      +$gp add manifest

      test.arguments += --push --no-edit

      : update
      :
      {
        $clone_repos
        sp=' '

        $* --current-tag=update <'y' 2>>~%EOE%
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0 (update)
            push:    origin/master
          %continue\? \[y/n\] Updated tag 'v0.1.0' \.+%d
          EOE

        test.arguments += --yes

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $* --current-tag=update --force=unchanged 2>>~%EOE%
          %Updated tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO

        # Updates the existing local tag.
        #
        # Note that starting git 2.20.0 --force is also required (see git
        # release notes for details).
        #
        $pull2 --tags --force
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO
      }

      : remove
      :
      {
        $clone_repos

        $* --current-tag=remove <'y' 2>>~%EOE%
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0+1 (remove v0.1.0)
            push:    origin/master
          %continue\? \[y/n\] Deleted tag 'v0.1.0' \.+%d
          EOE

        test.arguments += --yes

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $* --current-tag=remove --force=unchanged 2>>~%EOE%
          %Deleted tag 'v0.1.0\+1' \.+%d
          EOE

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO
      }

      : keep
      :
      {
        $clone_repos
        sp = ' '

        $* --current-tag=keep <'y' 2>>:"EOE"
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0+1
            push:    origin/master
          continue? [y/n]$sp
          EOE

        test.arguments += --yes

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $* --current-tag=keep --force=unchanged

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           (tag: v0.1.0+1) Release version 0.1.0+1
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           (tag: v0.1.0+1) Release version 0.1.0+1
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }}

    : tag
    :
    {{
      +$clone_repos

      test.arguments += --push --no-edit --force=unchanged --yes

      +$* --no-tag

      +$clone2
      +$log2 >>:~%EOO%
        % \(HEAD -> master, origin/master, \.*\) Release version 0.1.0\+1%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      tag = [cmdline] $release --tag --push -q --yes -d prj

      : update
      :
      {
        $clone_repos

        $tag --current-tag=update 2>>~%EOE%
          %Updated tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO
      }

      : remove
      :
      {
        $clone_repos

        $tag --current-tag=remove 2>>~%EOE%
          %Deleted tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO
      }

      : keep
      :
      {
        $clone_repos

        $tag --current-tag=keep

        $log >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }}

    : changes-staged
    :
    {{
      test.arguments += --push --no-edit --yes

      +$clone_repos

      +echo '' >+ prj/manifest
      +$gp add manifest

      : normal
      :
      {
        $clone_repos

        $*

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : stub
      :
      {
        $clone_root_repos

        sed -i -e 's/^(version:) .*$/\1 0/' prj/manifest
        $gp add manifest

        $* --revision

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0\+1, \.*\) Release version 0\+1%d
           Create
          EOO
      }

      : no-tag
      :
      {
        $clone_repos

        $* --no-tag

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $release --tag --push --yes -d prj

        $pull2 --tags --force # Updates the existing local tag (see above).
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }}

    : no-changes-staged
    :
    {
      test.arguments += --push --yes

      $clone_repos

      $* 2>>~%EOE% != 0
        error: project directory has no staged changes
          info: revision increment must be committed together with associated changes
          info: use --force=unchanged to release anyway
        EOE

      $* --force=unchanged

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : unstaged
    :
    {
      test.arguments += --yes

      $clone_repos

      echo '' >+ prj/manifest

      $* 2>>EOE != 0
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --no-commit

      $gp commit -a -m 'Release revision'
      $release --tag --push --yes -d prj

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release revision%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : amend
    :
    {{
      test.arguments += --push --amend --no-edit --yes

      +$clone_repos

      +echo '' >+ prj/repositories.manifest
      +$gp commit -a -m 'Fix repositories.manifest' --no-verify

      +echo '' >+ prj/buildfile
      +$gp commit -a -m 'Fix buildfile' -m "Add '\n' to the end of the file." \
           --no-verify

      +echo '' >+ prj/manifest
      +$gp add manifest

      : no-squash
      :
      {
        $clone_repos

        $*

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Fix repositories.manifest
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }

      : squash
      :
      {
        $clone_repos

        $* --squash 2

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }

      : no-changes-staged
      :
      {
        $clone_repos

        $gp commit -a -m 'Change manifest' --no-verify

        $*

        $clone2
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Fix buildfile
           Fix repositories.manifest
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO


        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Change manifest

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }
    }}
  }}

  : open
  :
  {{
    test.options   += -q
    test.arguments += --yes

    : unstaged
    :
    {
      $clone_root_repos

      $* --no-open

      echo '' >+ prj/manifest

      $* --open 2>>EOE != 0
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --open --no-commit

      $gp commit -a -m 'Open dev cycle'
      $gp push origin HEAD --tags

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Open dev cycle%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }
  }}

  : no-commit
  :
  {
    test.arguments += --yes

    $clone_root_repos

    $* --no-commit

    $clone2
    $log2 >>:~%EOO%
      % \(HEAD -> master, .*\) Create%
      EOO

    $gp commit -a -m 'Release version'

    $* --tag
    $* --open --no-commit

    $gp commit -a -m 'Change version'
    $gp push origin HEAD --tags

    $pull2
    $log2 >>:~%EOO%
      % \(HEAD -> master, .*\) Change version%
       (tag: v0.1.0) Release version
       Create
      EOO
  }

  : no-progress
  :
  {
    test.arguments += --yes

    $clone_root_repos

    $* --push --no-progress 2>>~%EOE%
      %\.+ Release version 0.1.0%d
       1 file changed, 1 insertion(+), 1 deletion(-)
      %\.+ Change version to 0.2.0-a.0.z%d
       1 file changed, 1 insertion(+), 1 deletion(-)
      EOE

    $clone2
    $log2 >>:~%EOO%
      % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
       (tag: v0.1.0) Release version 0.1.0
       Create
      EOO
  }
}}

: multi-pkg
:
{{
  # Create the remote repository.
  #
  +mkdir --no-cleanup prj.git
  +git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/***

  # Create the local repository.
  #
  +$new -t empty prj &prj/***
  +$new -t exe --package prj -d prj
  +$new -t lib --package libprj -d prj

  +$gp config user.name 'Test Script'
  +$gp config user.email 'testscript@example.com'
  +$gp config core.hooksPath ''
  +$gp remote add origin ../prj.git
  +$gp add '*'
  +$gp commit -m 'Create'
  +$gp push --set-upstream origin master

  test.options += -q

  : pkg-spec
  :
  {{
    +$clone_repos

    test.arguments += --no-open --push --yes

    : dir
    :
    {
      $clone_repos

      # Release the single libprj package rather than the whole prj project.
      #
      test.arguments = $regex.apply($test.arguments, '^(-d|prj)$', '')

      $* -d prj/libprj 2>>EOE
        warning: following project packages not being released: prj
        EOE

      $clone2

      cat prj2/libprj/manifest >>~%EOO%
        %.*
        name: libprj
        version: 0.1.0
        %.*
        EOO

      cat prj2/prj/manifest >>~%EOO%
        %.*
        name: prj
        version: 0.1.0-a.0.z
        %.*
        EOO
    }

    : name
    :
    {
      $clone_repos

      # Release the single libprj package rather than the whole prj project.
      #
      $* libprj 2>>EOE
        warning: following project packages not being released: prj
        EOE

      $clone2

      cat prj2/libprj/manifest >>~%EOO%
        %.*
        name: libprj
        version: 0.1.0
        %.*
        EOO

      cat prj2/prj/manifest >>~%EOO%
        %.*
        name: prj
        version: 0.1.0-a.0.z
        %.*
        EOO
    }

    : no-pkg-prj-spec
    :
    {
      $clone_repos

      # Release the whole project, but deduce the project directory from the
      # current working directory which is a package directory.
      #
      test.arguments = $regex.apply($test.arguments, '^(-d|prj)$', '')

      env -c prj/libprj -- $*

      $clone2

      cat prj2/libprj/manifest >>~%EOO%
        %.*
        name: libprj
        version: 0.1.0
        %.*
        EOO

      cat prj2/prj/manifest >>~%EOO%
        %.*
        name: prj
        version: 0.1.0
        %.*
        EOO
    }
  }}

  : patch
  :
  {
    $clone_repos

    $* --push --yes

    $clone2
    $log2 >>:~%EOO%
      % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
       (tag: v0.1.0) Release version 0.1.0
       Create
      EOO

    cat prj2/prj/manifest >>~%EOO%
      %.*
      name: prj
      version: 0.2.0-a.0.z
      language: c++
      %.*
      EOO

    cat prj2/libprj/manifest >>~%EOO%
      %.*
      name: libprj
      version: 0.2.0-a.0.z
      language: c++
      %.*
      EOO
  }

  : multiple-revisions
  :
  {
    test.arguments += --push --yes

    $clone_repos
    $* --no-open

    echo '' >+ prj/prj/manifest
    $gp add prj/manifest

    $release --revision --current-tag=update -q --yes --no-edit -d prj/prj 2>>~%EOE%
      %.*
      warning: following project packages not being released: libprj
      %.*
      EOE

    echo '' >+ prj/prj/manifest
    $gp add prj/manifest

    echo '' >+ prj/libprj/manifest
    $gp add libprj/manifest

    $* --revision --no-edit 2>>EOE != 0
      error: different current package versions
        info: package prj version 0.1.0+1
        info: package libprj version 0.1.0
      EOE

    $* --revision --current-tag=update --no-edit 2>>~%EOE%
      %Updated tag 'v0.1.0' \.+%d
      EOE

    # Note that the 0.1.0+1 release is not tagged as it wasn't pushed.
    #
    $clone2
    $log2 >>:~%EOO%
      % \(HEAD -> master, tag: v0.1.0, \.*\) Release versions prj/0.1.0\+2, libprj/0.1.0\+1%d
       Release version 0.1.0+1
       Release version 0.1.0
       Create
      EOO
  }

  : prompt
  :
  {{
    +$clone_repos
    sp = ' '

    : default
    :
    {
      $clone_repos

      $* <'y' 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    no
        continue? [y/n]$sp
        EOE

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Create%d
        EOO
    }

    : push
    :
    {
      $clone_repos

      $* --push  <'y' 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    origin/master
        continue? [y/n]$sp
        EOE

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : show-push
    :
    {
      test.arguments += --show-push

      $clone_repos

      $* <'y' 2>>:"EOE" >>~%EOO%
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    origin/master \(show\)
        continue? [y/n]$sp
        EOE
        %git -C "?\.+prj"? push origin master refs/tags/v0.1.0%d
        EOO

      $gp push origin master refs/tags/v0.1.0

      $clone2
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --no-tag --no-open --yes >>~%EOO%
        %git -C "?.+prj"? push origin master%
        EOO

      $gp push origin master

      $pull2 --tags --force # Updates the existing local tag (see above).
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Release version 0.2.0%d
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --tag --yes >>~%EOO%
        %git -C "?\.+prj"? push origin refs/tags/v0.2.0%d
        EOO

      $gp push origin refs/tags/v0.2.0

      $pull2 --tags --force # Updates the existing local tag (see above).
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.2.0, \.*\) Release version 0.2.0%d
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --revision --current-tag=remove --force=unchanged --yes >>~%EOO% 2>>~%EOE%
        %git -C "?\.+prj"? push origin master refs/tags/v0.2.0\+1 :refs/tags/v0.2.0%d
        EOO
        %Deleted tag 'v0.2.0' \.+%d
        EOE

      $gp push origin master refs/tags/v0.2.0+1 ':refs/tags/v0.2.0'

      $fetch2 --prune origin '+refs/tags/*:refs/tags/*'
      $pull2 --tags
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.2.0\+1, \.*\) Release version 0.2.0\+1%d
         Release version 0.2.0
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : no-tag
    :
    {
      $clone_repos

      $* <'y' --no-tag 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     no
          push:    no
        continue? [y/n]$sp
        EOE
    }

    : no-open
    :
    {
      $clone_repos

      $* <'y' --no-open 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0

          commit:  yes
          tag:     v0.1.0
          push:    no
        continue? [y/n]$sp
        EOE
    }
  }}
}}

: options-incompatibility
:
{
  $clone_prj

  $* --revision --open                      2>'error: both --revision and --open specified'       != 0
  $* --revision --alpha                     2>'error: both --revision and --alpha specified'      != 0
  $* --revision --no-open                   2>'error: both --revision and --no-open specified'    != 0
  $* --revision --open-beta                 2>'error: both --revision and --open-beta specified'  != 0
  $* --open     --no-tag                    2>'error: both --open and --no-tag specified'         != 0
  $* --tag      --no-commit                 2>'error: both --tag and --no-commit specified'       != 0
  $* --push     --no-commit                 2>'error: both --push and --no-commit specified'      != 0
  $* --push     --show-push                 2>'error: both --push and --show-push specified'      != 0
  $* --edit     --no-commit                 2>'error: both --no-commit and --edit specified'      != 0
  $* --current-tag=keep                     2>'error: --current-tag requires --revision or --tag' != 0
  $* --revision --current-tag keep --no-tag 2>'error: both --current-tag and --no-tag specified'  != 0
  $* --open-base 1.2.3 --open-beta          2>'error: both --open-beta and --open-base specified' != 0
  $* --amend                                2>'error: --amend requires --revision'                != 0
  $* --squash 1                             2>'error: --squash requires --amend'                  != 0
  $* --revision --amend --squash 0          2>'error: invalid --squash value: 0'                  != 0
  $* --revision --amend --no-commit         2>'error: both --amend and --no-commit specified'     != 0
}
