[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non:\n  pull_request:\n  push:\n  schedule:\n  - cron: '0 8 * * 0'\njobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 40\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - java: 8\n            sbt_version: \"1.4.9\"\n          - java: 17\n            sbt_version: \"2\"\n          - java: 8\n          - java: 25\n    steps:\n    - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 0\n    - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n      with:\n        java-version: ${{matrix.java}}\n        distribution: zulu\n    - uses: sbt/setup-sbt@508b753e53cb6095967669e0911487d2b9bc9f41 # v1.1.22\n    - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0\n    - run: |\n        git config --global user.email \"example@example.com\"\n        git config --global user.name \"example\"\n        echo '[ui]' > \"$HOME/.hgrc\"\n        echo 'username = example <example@example.com>' >> \"$HOME/.hgrc\"\n    - run: sbt \"+ scalafmtCheckAll\" scalafmtSbtCheck\n    - run: sbt -v \"set pluginCrossBuild / sbtVersion := \\\"${{matrix.sbt_version}}\\\"\" test scripted\n      if: ${{ matrix.sbt_version != '' && matrix.sbt_version != '2' }}\n    - run: sbt -v test scripted\n      if: ${{ matrix.sbt_version == '' }}\n    - run: sbt -v \"++ 3.x\" Test/compile test scripted\n      if: ${{ matrix.sbt_version == '2' }}\n    - run: rm -rf \"$HOME/.ivy2/local\" || true\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    tags: [\"*\"]\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          java-version: 17\n          distribution: temurin\n      - uses: sbt/setup-sbt@508b753e53cb6095967669e0911487d2b9bc9f41 # v1.1.22\n      - run: sbt ci-release\n        if: ${{ github.repository_owner == 'sbt' }}\n        env:\n          PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}\n          PGP_SECRET: ${{ secrets.PGP_SECRET }}\n          SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n          SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n"
  },
  {
    "path": ".gitignore",
    "content": "target\nproject/target\n.idea\n.idea_modules\n.bsp"
  },
  {
    "path": ".scalafmt.conf",
    "content": "version = \"3.10.7\"\nrunner.dialect = Scala212Source3\nmaxColumn = 120\nalign.preset = none\nalign.tokens = []\nrewrite.rules = [ExpandImportSelectors, PreferCurlyFors]\nrewrite.imports.contiguousGroups = \"no\"\nrewrite.imports.groups = [[\".*\"]]\nrewrite.imports.sort = ascii\ncontinuationIndent.callSite = 2\ncontinuationIndent.defnSite = 2\ndocstrings.style = keep\nincludeCurlyBraceInSelectChains = false\noptIn.breakChainOnFirstMethodDot = false\ntrailingCommas = preserve\nnewlines.topLevelStatementBlankLines = [\n  {\n    blanks { after = 1 }\n    maxNest = 0\n    regex = \"Import|Class|Trait|Object\"\n  }\n]\nproject.layout = StandardConvention\nrewrite.scala3.convertToNewSyntax = true\nrewrite.scala3.newSyntax.control = false\n"
  },
  {
    "path": "LICENSE",
    "content": "   Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n \nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n \n1. Definitions.\n \n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n \n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n \n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n \n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n \n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n \n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n \n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n \n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n \n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n \n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n \n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n \n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n \n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n \n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n \n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n \n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n \n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n \n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n \n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n \n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n \n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n \n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n \n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n \nEND OF TERMS AND CONDITIONS\n \nAPPENDIX: How to apply the Apache License to your work.\n \n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n \nCopyright [yyyy] [name of copyright owner]\n \nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n \n    http://www.apache.org/licenses/LICENSE-2.0\n \nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "README.md",
    "content": "# sbt-release\nThis sbt plugin provides a customizable release process that you can add to your project.\n\n[![sbt-release sbt 1](https://index.scala-lang.org/sbt/sbt-release/sbt-release/latest-by-scala-version.svg?platform=sbt1)](https://index.scala-lang.org/sbt/sbt-release/sbt-release)\n[![sbt-release sbt 2](https://index.scala-lang.org/sbt/sbt-release/sbt-release/latest-by-scala-version.svg?platform=sbt2)](https://index.scala-lang.org/sbt/sbt-release/sbt-release)\n\n**Notice:** This README contains information for the latest release. Please refer to the documents for a specific version by looking up the respective [tag](https://github.com/sbt/sbt-release/tags).\n\n## Requirements\n * sbt 1.x\n * The version of the project should follow the semantic versioning scheme on [semver.org](https://www.semver.org) with the following additions:\n   * The minor and bugfix (and beyond) part of the version are optional.\n   * There is no limit to the number of subversions you may have.\n   * The appendix after the bugfix part must be alphanumeric (`[0-9a-zA-Z]`) but may also contain dash characters `-`.\n   * These are all valid version numbers:\n     * 1.2.3\n     * 1.2.3-SNAPSHOT\n     * 1.2beta1\n     * 1.2-beta.1\n     * 1.2\n     * 1\n     * 1-BETA17\n     * 1.2.3.4.5\n     * 1.2.3.4.5-SNAPSHOT\n * A [publish repository](https://www.scala-sbt.org/1.x/docs/Publishing.html) configured. (Required only for the default release process. See further below for release process customizations.)\n * git [optional]\n\n## Usage\n\nAdd the following lines to `./project/plugins.sbt`. See the section [Using Plugins](https://www.scala-sbt.org/1.x/docs/Using-Plugins.html) in the sbt website for more information.\n\n```scala\naddSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % \"1.4.0\")\n```\n\n## version.sbt\n\nSince the build definition is actual Scala code, it's not as straight forward to change something in the middle of it as it is with an XML definition.\n\nFor this reason, *sbt-release* won't ever touch your build definition files, but instead writes the new release or development version to a file defined by the setting `releaseVersionFile`, which is set to **`file(\"version.sbt\")`** by default and points to `$PROJECT_ROOT/version.sbt`.\n\nBy default the version is set on the build level (using `ThisBuild / version`). This behavior can be controlled by setting `releaseUseGlobalVersion` to `false`, after which a version like `version := \"1.2.3\"` will be written to `version.sbt`.\n\n\n## Release Process\n\nThe default release process consists of the following tasks:\n\n 1. Check that the working directory is a git repository and the repository has no outstanding changes. Also prints the hash of the last commit to the console.\n 1. If there are any snapshot dependencies, ask the user whether to continue or not (default: no).\n 1. Ask the user for the `release version` and the `next development version`. Sensible defaults are provided.\n 1. Run `clean`.\n 1. Run `test`, if any test fails, the release process is aborted.\n 1. Write `ThisBuild / version := \"$releaseVersion\"` to the file `version.sbt` and also apply this setting to the current [build state](https://www.scala-sbt.org/1.x/docs/Core-Principles.html#Introduction+to+build+state).\n 1. Commit the changes in `version.sbt`.\n 1. Tag the previous commit with `v$version` (eg. `v1.2`, `v1.2.3`).\n 1. Run `publish`.\n 1. Write `ThisBuild / version := \"nextVersion\"` to the file `version.sbt` and also apply this setting to the current build state.\n 1. Commit the changes in `version.sbt`.\n\nIn case of a failure of a task, the release process is aborted.\n\n### Non-interactive release\n\nYou can run a non-interactive release by providing the argument `with-defaults` (tab completion works) to the `release` command.\n\nFor all interactions, the following default value will be chosen:\n\n * Continue with snapshots dependencies: no\n * Release Version: current version without the qualifier (eg. `1.2-SNAPSHOT` -> `1.2`)\n * Next Version: increase the minor version segment of the current version and set the qualifier to '-SNAPSHOT' (eg. `1.2.1-SNAPSHOT` -> `1.3.0-SNAPSHOT`)\n * VCS tag: default is abort if the tag already exists. It is possible to override the answer to VCS by ```default-tag-exists-answer``` with one of:\n    * ```o``` override\n    * ```k``` do not overwrite\n    * ```a``` abort (default)\n    * ```<tag-name>``` an explicit custom tag name (e.g. ```1.2-M3```)\n * VCS push:\n    * Abort if no remote tracking branch is set up.\n    * Abort if remote tracking branch cannot be checked (eg. via `git fetch`).\n    * Abort if the remote tracking branch has unmerged commits.\n\n### Set release version and next version as command arguments\n\nYou can set the release version using the argument `release-version` and next version with `next-version`.\n\nExample:\n\n    release release-version 1.0.99 next-version 1.2.0-SNAPSHOT\n\n### Skipping tests\n\nFor that emergency release at 2am on a Sunday, you can optionally avoid running any tests by providing the `skip-tests` argument to the `release` command.\n\n### Cross building during a release\n\nSince version 0.7, *sbt-release* comes with built-in support for [cross building](https://www.scala-sbt.org/1.x/docs/Cross-Build.html) and cross publishing. A cross release can be triggered in two ways:\n\n 1. via the setting `releaseCrossBuild` (by default set to `false`)\n 1. by using the option `cross` for the `release` command\n\n    `> release cross with-defaults`\n\nCombining both ways of steering a cross release, it is possible to generally disable automatic detection of cross release by using `releaseCrossBuild := false` and running `release cross`.\n\nOf the predefined release steps, the `clean`, `test`, and `publish` release steps are set up for cross building.\n\nA cross release behaves analogous to using the `+` command:\n 1. If no `crossScalaVersions` are set, then running `release` or `release cross` will not trigger a cross release (i.e. run the release with the scala version specified in the setting `scalaVersion`).\n 1. If the `crossScalaVersions` setting is set, then only these scala versions will be used. Make sure to include the regular/default `scalaVersion` in the `crossScalaVersions` setting as well. Note that setting running `release cross` on a root project with `crossScalaVersions` set to `Nil` will not release anything. \n\nIn the section *Customizing the release process* we take a look at how to define a `ReleaseStep` to participate in a cross build.\n\n### Versioning Strategies\n\nAs of version 0.8, *sbt-release* comes with several strategies for computing the next snapshot version via the `releaseVersionBump` setting. These strategies are defined in `sbtrelease.Version.Bump`. By default, the `Next` strategy is used:\n\n * `Major`: always bumps the *major* part of the version\n * `Minor`: always bumps the *minor* part of the version\n * `Bugfix`: always bumps the *bugfix* part of the version\n * `Nano`: always bumps the *nano* part of the version\n * `Next` (**default**): bumps the last version part, including the qualifier (e.g. `0.17` -> `0.18`, `0.11.7` -> `0.11.8`, `3.22.3.4.91` -> `3.22.3.4.92`, `1.0.0-RC1` -> `1.0.0-RC2`)\n * `NextStable`: bumps exactly like `Next` except that any prerelease qualifier is excluded (e.g. `1.0.0-RC1` -> `1.0.0`)\n\nUsers can set their preferred versioning strategy in `build.sbt` as follows:\n```sbt\nreleaseVersionBump := sbtrelease.Version.Bump.Major\n```\n\n### Default Versioning\n\nThe default settings make use of the helper class [`Version`](https://github.com/sbt/sbt-release/blob/master/src/main/scala/Version.scala) that ships with *sbt-release*.\n\n`releaseVersion`: The current version in version.sbt, without the \"-SNAPSHOT\" ending. So, if `version.sbt` contains `1.0.0-SNAPSHOT`, the release version will be set to `1.0.0`.\n\n`releaseNextVersion`: The \"bumped\" version according to the versioning strategy (explained above), including the `-SNAPSHOT` ending. So, if `releaseVersion` is `1.0.0`, `releaseNextVersion` will be `1.0.1-SNAPSHOT`.\n\n### Custom Versioning\n\n*sbt-release* comes with two settings for deriving the release version and the next development version from a given version.\n\nThese derived versions are used for the suggestions/defaults in the prompt and for non-interactive releases.\n\nLet's take a look at the types:\n\n```scala\nval releaseVersion     : TaskKey[String => String]\nval releaseNextVersion : TaskKey[String => String]\n```\n\nIf you want to customize the versioning, keep the following in mind:\n\n * `releaseVersion`\n   * input: the current development version\n   * output: the release version\n\n * `releaseNextVersion`\n   * input: the release version (either automatically 'chosen' in a non-interactive build or from user input)\n   * output: the next development version\n\n### Custom VCS messages\n*sbt-release* has built in support to commit/push to Git, Mercurial and Subversion repositories. The messages for the tag and the commits can be customized to your needs with these settings:\n\n```scala\nval releaseTagComment        : TaskKey[String]\nval releaseCommitMessage     : TaskKey[String]\nval releaseNextCommitMessage : TaskKey[String]\n\n// defaults\nreleaseTagComment        := s\"Releasing ${(ThisBuild / version).value}\",\nreleaseCommitMessage     := s\"Setting version to ${(ThisBuild / version).value}\",\nreleaseNextCommitMessage := s\"Setting version to ${(ThisBuild / version).value}\",\n```\n\n### Publishing signed releases\n\nSBT is able to publish signed releases using the [sbt-pgp plugin](https://github.com/sbt/sbt-pgp).\n\nAfter setting that up for your project, you can then tell *sbt-release* to use it by setting the `releasePublishArtifactsAction` key:\n\n```scala\nreleasePublishArtifactsAction := PgpKeys.publishSigned.value\n````\n\n## Customizing the release process\n\n### Not all releases are created equal\n\nThe release process can be customized to the project's needs.\n\n  * Not using Git? Then rip it out.\n  * Want to check for the existence of release notes at the start of the release and then publish it with [posterous-sbt](https://github.com/n8han/posterous-sbt) at the end? Just add the release step.\n\n\nThe release process is defined by [State](https://www.scala-sbt.org/1.x/docs/Build-State.html) transformation functions (`State => State`), for which *sbt-release* defines this case class:\n\n```scala\ncase class ReleaseStep (\n  action: State => State,\n  check: State => State = identity,\n  enableCrossBuild: Boolean = false\n)\n```\n\nThe function `action` is used to perform the actual release step. Additionally, each release step can provide a `check` function that is run at the beginning of the release and can be used to prevent the release from running because of an unsatisfied invariant (i.e. the release step for publishing artifacts checks that publishTo is properly set up).  The property `enableCrossBuild` tells *sbt-release* whether or not a particular `ReleaseStep` needs to be executed for the specified `crossScalaVersions`.\n\nThe sequence of `ReleaseStep`s that make up the release process is stored in the setting `releaseProcess: SettingKey[Seq[ReleaseStep]]`.\n\nThe state transformations functions used in *sbt-release* are the same as the action/body part of a no-argument command.  You can read more about [building commands](https://www.scala-sbt.org/1.x/docs/Commands.html) in the sbt website.\n\n### Release Steps\n\nThere are basically 2 ways to creating a new `ReleaseStep`:\n\n#### Defining your own release steps\n\nYou can define your own state tansformation functions, just like *sbt-release* does, for example:\n\n```scala\nval checkOrganization = ReleaseStep(action = st => {\n  // extract the build state\n  val extracted = Project.extract(st)\n  // retrieve the value of the organization SettingKey\n  val org = extracted.get(Keys.organization)\n\n  if (org.startsWith(\"com.acme\"))\n    sys.error(\"Hey, no need to release a toy project!\")\n\n  st\n})\n```\n\nWe will later see how to let this release step participate in the release process.\n\n#### Reusing already defined tasks\n\nSometimes you just want to run an existing task or command. This is especially useful if the task raises an error in case something went wrong and therefore interrupts the release process.\n\n*sbt-release* comes with a few convenience functions for converting tasks and commands to release steps:\n\n* `releaseStepTask` - Run an individual task. Does not aggregate builds.\n* `releaseStepTaskAggregated` - Run an aggregated task.\n* `releaseStepInputTask` - Run an input task, optionally taking the input to pass to it.\n* `releaseStepCommand` - Run a command.\n\nFor example:\n\n```scala\nreleaseProcess := Seq[ReleaseStep](\n  releaseStepInputTask(testOnly, \" com.example.MyTest\"),\n  releaseStepInputTask(scripted),\n  releaseStepTask(subproject / publishSigned),\n  releaseStepCommand(\"sonaRelease\")\n)\n```\n\nI highly recommend to make yourself familiar with the [State API](https://www.scala-sbt.org/1.x/docs/Build-State.html) before you continue your journey to a fully customized release process.\n\n### Can we finally customize that release process, please?\n\nYes, and as a start, let's take a look at the [default definition](https://github.com/sbt/sbt-release/blob/v1.4.0/src/main/scala/ReleasePlugin.scala#L262-L274) of `releaseProcess`:\n\n#### The default release process\n\n```scala\nimport ReleaseTransformations._\n\n// ...\n\nreleaseProcess := Seq[ReleaseStep](\n  checkSnapshotDependencies,              // : ReleaseStep\n  inquireVersions,                        // : ReleaseStep\n  runClean,                               // : ReleaseStep\n  runTest,                                // : ReleaseStep\n  setReleaseVersion,                      // : ReleaseStep\n  commitReleaseVersion,                   // : ReleaseStep, performs the initial git checks\n  tagRelease,                             // : ReleaseStep\n  publishArtifacts,                       // : ReleaseStep, checks whether `publishTo` is properly set up\n  setNextVersion,                         // : ReleaseStep\n  commitNextVersion,                      // : ReleaseStep\n  pushChanges                             // : ReleaseStep, also checks that an upstream branch is properly configured\n)\n```\n\nThe names of the individual steps of the release process are pretty much self-describing.\nNotice how we can just reuse the `publish` task by utilizing the `releaseTask` helper function,\nbut keep in mind that it needs to be properly scoped (more info on [Scopes](https://www.scala-sbt.org/1.x/docs/Scopes.html)).\n\nNote, the `commitReleaseVersion` step requires that the working directory has no untracked files by default. It will abort the release in this case. You may disable this check\nby setting the `releaseIgnoreUntrackedFiles` key to `true`.\n\n#### No Git, and no toy projects!\n\nLet's modify the previous release process and remove the Git related steps, who uses that anyway.\n\n```scala\nimport ReleaseTransformations._\n\n// ...\n\nReleaseKeys.releaseProcess := Seq[ReleaseStep](\n  checkOrganization,                // Look Ma', my own release step!\n  checkSnapshotDependencies,\n  inquireVersions,\n  runTest,\n  setReleaseVersion,\n  publishArtifacts,\n  setNextVersion\n)\n```\n\nOverall, the process stayed pretty much the same:\n\n  * The Git related steps were left out.\n  * Our `checkOrganization` task was added in the beginning, just to be sure this is a serious project.\n\n#### Release notes anyone?\nNow let's also add steps for [posterous-sbt](https://github.com/n8han/posterous-sbt):\n\n```scala\nimport posterous.Publish._\nimport ReleaseTransformations._\n\n// ...\n\nval publishReleaseNotes = (ref: ProjectRef) => ReleaseStep(\n  check  = releaseStepTaskAggregated(check in Posterous in ref),   // upfront check\n  action = releaseStepTaskAggregated(publish in Posterous in ref) // publish release notes\n)\n\n// ...\n\nReleaseKeys.releaseProcess <<= thisProjectRef apply { ref =>\n  import ReleaseStateTransformations._\n  Seq[ReleaseStep](\n    checkOrganization,\n    checkSnapshotDependencies,\n    inquireVersions,\n    runTest,\n    setReleaseVersion,\n    publishArtifacts,\n    publishReleaseNotes(ref) // we need to forward `thisProjectRef` for proper scoping of the underlying tasks\n    setNextVersion\n  )\n}\n```\n\nThe `check` part of the release step is run at the start, to make sure we have everything set up to post the release notes later on.\nAfter publishing the actual build artifacts, we also publish the release notes.\n\n## Credits\nThank you, [Jason](https://github.com/retronym) and [Mark](https://github.com/harrah), for your feedback and ideas.\n\n## Contributors\n[Johannes Rudolph](https://github.com/jrudolph), [Espen Wiborg](https://github.com/espenhw), [Eric Bowman](https://github.com/ebowman), [Petteri Valkonen](https://github.com/pvalkone),\n[Gary Coady](https://github.com/garycoady), [Alexey Alekhin](https://github.com/laughedelic), [Andrew Gustafson](https://github.com/agustafson), [Paul Davies](https://github.com/paulmdavies),\n[Stanislav Savulchik](https://github.com/savulchik), [Tim Van Laer](https://github.com/timvlaer), [Lars Hupel](https://github.com/larsrh)\n\n## License\nCopyright (c) 2011-2014 Gerolf Seitz\n\nPublished under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt)\n"
  },
  {
    "path": "build.sbt",
    "content": "lazy val `sbt-release` = project in file(\".\")\n\norganization := \"com.github.sbt\"\nname := \"sbt-release\"\n\ncrossScalaVersions += \"3.8.2\"\n\npluginCrossBuild / sbtVersion := {\n  scalaBinaryVersion.value match {\n    case \"2.12\" =>\n      (pluginCrossBuild / sbtVersion).value\n    case _ =>\n      \"2.0.0-RC11\"\n  }\n}\n\nhomepage := Some(url(\"https://github.com/sbt/sbt-release\"))\nlicenses := Seq(\"Apache-2.0\" -> url(\"http://www.apache.org/licenses/LICENSE-2.0\"))\n\npublishMavenStyle := true\nscalacOptions ++= Seq(\"-deprecation\", \"-feature\", \"-language:implicitConversions\")\n\nscalacOptions ++= {\n  scalaBinaryVersion.value match {\n    case \"3\" =>\n      Nil\n    case _ =>\n      Seq(\"-release:8\")\n  }\n}\n\nval unusedWarnings = Def.setting(\n  scalaBinaryVersion.value match {\n    case \"2.12\" =>\n      Seq(\"-Ywarn-unused:imports\")\n    case _ =>\n      Seq(\n        \"-Wunused:imports\",\n        \"-Wconf:msg=is no longer supported for vararg splices:error\",\n      )\n  }\n)\n\nscalacOptions ++= unusedWarnings.value\n\nSeq(Compile, Test).flatMap(c => c / console / scalacOptions --= unusedWarnings.value)\n\ndef hash(): String = sys.process.Process(\"git rev-parse HEAD\").lineStream_!.head\n\nCompile / doc / scalacOptions ++= {\n  Seq(\n    \"-sourcepath\",\n    (LocalRootProject / baseDirectory).value.getAbsolutePath,\n    \"-doc-source-url\",\n    s\"https://github.com/sbt/sbt-release/tree/${hash()}€{FILE_PATH}.scala\"\n  )\n}\n\nlibraryDependencies ++= Seq(\"org.specs2\" %% \"specs2-core\" % \"4.23.0\" % \"test\")\n\n// Scripted\nenablePlugins(SbtPlugin)\nscriptedLaunchOpts := {\n  scriptedLaunchOpts.value ++ Seq(\n    \"-Xmx1024M\",\n    \"-Dsbt.build.onchange=warn\",\n    \"-Dplugin.version=\" + version.value\n  )\n}\nscriptedBufferLog := false\n\npomExtra := (\n  <developers>{\n    Seq(\n      (\"xuwei-k\", \"Kenji Yoshida\"),\n    ).map { case (id, name) =>\n      <developer>\n        <id>{id}</id>\n        <name>{name}</name>\n        <url>https://github.com/{id}</url>\n      </developer>\n    }\n  }</developers>\n)\n"
  },
  {
    "path": "notes/0.1.markdown",
    "content": "* Initial release, see the [README](https://github.com/gseitz/sbt-release/blob/master/README.md) for a comprehensive introduction.\n"
  },
  {
    "path": "notes/0.2.markdown",
    "content": " * \\[Improvement\\] [#1](https://github.com/gseitz/sbt-release/issues/1): Include the Git tag and the has of the release commit in the JAR manifest.\n * \\[Improvement\\] [#3](https://github.com/gseitz/sbt-release/issues/3): Default version policy should increase the minor number, not the micro/bugfix\n * \\[Improvement\\] [#4](https://github.com/gseitz/sbt-release/issues/4): The suggested next development version should be based on the entered release version.\n * \\[Improvement\\] Allow easier customization of the release/next version. More info in the section *Custom versioning* in the [README](https://github.com/gseitz/sbt-release/blob/master/README.md).\n"
  },
  {
    "path": "notes/0.3.markdown",
    "content": " * \\[New\\] [#5](https://github.com/gseitz/sbt-release/issues/5): Added the key `tagName: SettingKey[String](\"release-tag-name\")` for easier customization of the used git tag name. Default setting: `tagName <<= (version in ThisBuild)(v => \"v\"+v)`\n * \\[Fix\\] [#6](https://github.com/gseitz/sbt-release/issues/6): The git command on Windows should be `git.exe`. Preliminary fix until the [sbt-git-plugin](http://github.com/jsuereth/sbt-git-plugin) is ready.\n * \\[Improvement] [#7](https://github.com/gseitz/sbt-release/issues/7): Tests are not executed after setting the release version anymore (only before the switch).\n"
  },
  {
    "path": "notes/0.4.markdown",
    "content": " * \\[Fix\\] `test` and `publish` are now properly propagated from the root project to aggregated projects.\n * In a multi-project build, `Release.releaseSettings` should be mixed into every sub-project.\n"
  },
  {
    "path": "notes/0.5.markdown",
    "content": "* `sbt-release` now adopts the sbt plugin best practices.\n* Add release step to run `git push && git push --tags` at the end of the release process.\n* Release steps can now contribute up-front sanity checks (eg. the git-push step checks if a remote repository is configured).\n* Added Mercurial support. Thanks [@espenhw](https://github.com/espenhw) for the contribution.\n* The appropriate VCS (Git or Mercurial, in that order) is automatically detected.\n* `sbt-release` is now published to the [scala-sbt community repository](http://scalasbt.artifactoryonline.com)"
  },
  {
    "path": "notes/0.6.markdown",
    "content": "* [#17](https://github.com/sbt/sbt-release/issues/17) Added setting for customizing the tag comment. Thanks [Eric Bowman](https://github.com/ebowman).\n* [#18](https://github.com/sbt/sbt-release/issues/18) Some release steps didn't properly use the default choice for non-interactive builds (`with-defaults`).\n* Added setting for customizing the commit message (defaults to `Setting version to x.y.z`).\n* [#19](https://github.com/sbt/sbt-release/issues/19) Make `tagName`, `tagComment` and `commitMessage` a `TaskKey[String]`, so they are only evaluated when they're needed (i.e. during a release instead of everytime sbt is started). Thanks again, [Eric Bowman](https://github.com/ebowman).\n\n"
  },
  {
    "path": "notes/0.7.1.markdown",
    "content": "* Publish for sbt 0.13.\n* Run `clean` before running `test` to avoid stale artifacts (e.g. in case a source file has been deleted, but the class file is still in `target`)\n"
  },
  {
    "path": "notes/0.7.markdown",
    "content": "* Recursively search parent directories for VCS marker directory. [#25](https://github.com/sbt/sbt-release/pull/25) (Thanks [pvalkone](https://github.com/pvalkone))\n* Add support for cross building/publishing. [#11](https://github.com/sbt/sbt-release/issues/11)\n* Print an informational message that `git push` writes to standard error. [#20](https://github.com/sbt/sbt-release/issues/20)\n"
  },
  {
    "path": "notes/0.8.1.markdown",
    "content": "* [#53](https://github.com/sbt/sbt-release/pull/53) Fix for git complaining about version.sbt being out of repository\n"
  },
  {
    "path": "notes/0.8.2.markdown",
    "content": "* [#61](https://github.com/sbt/sbt-release/issues/61) Clearer error message when `release with-defaults` fails due to an already existing tag.\n"
  },
  {
    "path": "notes/0.8.3.markdown",
    "content": "* [#55](https://github.com/sbt/sbt-release/issues/55) sbt `0.13.x` doesn't throw an exception after failed tests, thus the release would still go ahead. Thanks [@garycoady](https://github.com/garycoady)!\n* [#62](https://github.com/sbt/sbt-release/issues/62) Fix current working directory for VCS commands. Thanks [@laughedelic](https://github.com/laughedelic)!\n* [#64](https://github.com/sbt/sbt-release/issues/64) Allow writing the version string without `in ThisBuild`. This can be controlled via the setting `release-use-global-version`.\n"
  },
  {
    "path": "notes/0.8.4.markdown",
    "content": "* [#69](https://github.com/sbt/sbt-release/pull/69) Add Subversion support. Thanks [@timvlaer](https://github.com/timvlaer)!\n* [#70](https://github.com/sbt/sbt-release/issues/70) Fixed failed attempt at detecting the need for cross building. Cross building needs to enabled by either setting `ReleaseKeys.crossBuild := true` or launch the release with `release cross`.\n* [#78](https://github.com/sbt/sbt-release/pull/78) Use HEAD to determine current hash. Thanks [@larsrh](https://github.com/larsrh)!\n"
  },
  {
    "path": "notes/0.8.5.markdown",
    "content": "* [#82](https://github.com/sbt/sbt-release/pull/82) Ensure that task failure is propagated. Thanks [@jcrobak](https://github.com/jcrobak)!\n* [#49](https://github.com/sbt/sbt-release/pull/49) Make the used publish action configurable (for easier integration with sbt-pgp). Thanks [@jroper](https://github.com/jroper)\n"
  },
  {
    "path": "notes/0.8.markdown",
    "content": "* [#29](https://github.com/sbt/sbt-release/issues/29) The `release` task automatically runs a cross release build depending on whether `crossScalaVersions` contains a scala version other than the one defined by the `scalaVersion` setting key.\n* [#46](https://github.com/sbt/sbt-release/pull/46) Added a setting to more conveniently control the next version. (See [Convenient versioning](https://github.com/sbt/sbt-release#convenient-versioning))\n* [#48](https://github.com/sbt/sbt-release/issues/48) Show the appropriate version pattern in the version format error message."
  },
  {
    "path": "notes/1.0.5.markdown",
    "content": "* [#193][] The `checkSnapshotDependencies` release step now has `enabledCrossBuild` enabled by default.\n* [#185][] Adds a `default-tag-exists-answer` option to the `release` command to customise the default response to a tag already existing in the `tagRelease` release step.\n* [#194][] Makes `releaseVersion`, `releaseNextVersion`, and `releaseVersionBump` task keys instead of setting keys.\n\n[v1.0.4...v1.0.5](https://github.com/sbt/sbt-release/compare/v1.0.4%E2%80%A6v1.0.5)\n\n[#193]: https://github.com/sbt/sbt-release/pull/193\n[#185]: https://github.com/sbt/sbt-release/pull/185\n[#194]: https://github.com/sbt/sbt-release/pull/194\n"
  },
  {
    "path": "notes/about.markdown",
    "content": "[sbt-release](https://github.com/sbt/sbt-release) is a plugin for [sbt](https://github.com/sbt/sbt)\nand brings a customizable release process to your projects - think maven-release-plugin, but without the scary parts.\n"
  },
  {
    "path": "project/build.properties",
    "content": "sbt.version=1.12.9\n"
  },
  {
    "path": "project/plugins.sbt",
    "content": "libraryDependencies += \"org.scala-sbt\" %% \"scripted-plugin\" % sbtVersion.value\n\naddSbtPlugin(\"com.github.sbt\" % \"sbt-ci-release\" % \"1.11.2\")\n\naddSbtPlugin(\"org.scalameta\" % \"sbt-scalafmt\" % \"2.5.6\")\n"
  },
  {
    "path": "src/main/scala/Compat.scala",
    "content": "package sbtrelease\n\nimport sbt.*\nimport sbt.Def.ScopedKey\nimport sbt.EvaluateTask.extractedTaskConfig\nimport sbt.EvaluateTask.nodeView\nimport sbt.EvaluateTask.runTask\nimport sbt.EvaluateTask.withStreams\nimport sbt.Keys.*\nimport sbt.internal.Act\nimport sbt.internal.Aggregation\nimport sbt.internal.Aggregation.KeyValue\nimport sbt.internal.ExtendableKeyIndex\nimport sbt.std.Transform.DummyTaskMap\nimport scala.language.reflectiveCalls\n\nobject Compat {\n\n  import Utilities.*\n\n  def runTaskAggregated[T](taskKey: TaskKey[T], state: State): (State, Result[Seq[KeyValue[T]]]) = {\n    import EvaluateTask.*\n\n    val extra = DummyTaskMap(Nil)\n    val extracted = state.extract\n    val config = extractedTaskConfig(extracted, extracted.structure, state)\n\n    val rkey = Utilities.resolve(taskKey.scopedKey, extracted)\n    val keys = Aggregation.aggregate(rkey, ScopeMask(), extracted.structure.extra)\n    val tasks = Act.keyValues(extracted.structure)(keys)\n    val toRun = tasks.map { case KeyValue(k, t) => t.map(v => KeyValue(k, v)) }.join\n    val roots = tasks.map { case KeyValue(k, _) => k }\n\n    val (newS, result) = withStreams(extracted.structure, state) { str =>\n      val transform = nodeView(state, str, roots, extra)\n      runTask(toRun, state, str, extracted.structure.index.triggers, config)(using transform)\n    }\n    (newS, result)\n  }\n\n  def projectScope(project: Reference): Scope = Scope(Select(project), Zero, Zero, Zero)\n\n  // checking if publishTo is configured\n  def checkPublishTo(st: State): State = {\n    // getPublishTo fails if no publish repository is set up for projects with `skip in publish := false`.\n    val ex = st.extract\n    val ref = ex.get(thisProjectRef)\n    val (_, skipPublish) = ex.runTask(ref / publish / skip, st)\n    if (!skipPublish) {\n      Classpaths.getPublishTo(ex.runTask(ref / (Global / publishTo), st)._2)\n    }\n    st\n  }\n\n  val FailureCommand = sbt.Exec(\"--failure--\", None, None)\n\n  def excludeKeys(keys: Set[AttributeKey[?]]): Setting[?] => Boolean =\n    _.key match {\n      case ScopedKey(Scope(_, Zero, Zero, _), key) if keys.contains(key) => true\n      case _ => false\n    }\n\n  def crossVersions(st: State): Seq[String] = {\n    // copied from https://github.com/sbt/sbt/blob/2d7ec47b13e02526174f897cca0aef585bd7b128/main/src/main/scala/sbt/Cross.scala#L40\n    val proj = Project.extract(st)\n    import proj.*\n    crossVersions(proj, currentRef)\n  }\n\n  private def crossVersions(extracted: Extracted, proj: ProjectRef): Seq[String] = {\n    import extracted.*\n    ((proj / crossScalaVersions) get structure.data) getOrElse {\n      // reading scalaVersion is a one-time deal\n      ((proj / scalaVersion) get structure.data).toSeq\n    }\n  }\n\n  type Command = sbt.Exec\n  @deprecated(\"will be removed\")\n  private[sbtrelease] def command2String(command: Command): String = command.commandLine\n  @deprecated(\"will be removed\")\n  private[sbtrelease] def string2Exex(s: String): Command = sbt.Exec(s, None, None)\n\n  // type aliases\n  type StructureIndex = sbt.internal.StructureIndex\n  type BuildStructure = sbt.internal.BuildStructure\n  val BuildStreams = sbt.internal.BuildStreams\n  type BuildUtil[Proj] = sbt.internal.BuildUtil[Proj]\n  val BuildUtil = sbt.internal.BuildUtil\n  val Index = sbt.internal.Index\n  type KeyIndex = sbt.internal.KeyIndex\n  val KeyIndex = sbt.internal.KeyIndex\n  type LoadedBuildUnit = sbt.internal.LoadedBuildUnit\n\n  // https://github.com/sbt/sbt/issues/3792\n  private[sbtrelease] def keyIndexApply(\n    known: Iterable[ScopedKey[?]],\n    projects: Map[URI, Set[String]],\n    configurations: Map[String, Seq[Configuration]]\n  ): ExtendableKeyIndex = try {\n    // for sbt 1.1\n    KeyIndex\n      .asInstanceOf[{\n        def apply(\n          known: Iterable[ScopedKey[?]],\n          projects: Map[URI, Set[String]],\n          configurations: Map[String, Seq[Configuration]]\n        ): ExtendableKeyIndex\n      }]\n      .apply(known = known, projects = projects, configurations = configurations)\n  } catch {\n    case _: NoSuchMethodException =>\n      // for sbt 1.0.x\n      KeyIndex\n        .asInstanceOf[{\n          def apply(\n            known: Iterable[ScopedKey[?]],\n            projects: Map[URI, Set[String]],\n          ): ExtendableKeyIndex\n        }]\n        .apply(known = known, projects = projects)\n  }\n\n  // https://github.com/sbt/sbt/issues/3792\n  private[sbtrelease] def keyIndexAggregate(\n    known: Iterable[ScopedKey[?]],\n    extra: BuildUtil[?],\n    projects: Map[URI, Set[String]],\n    configurations: Map[String, Seq[Configuration]]\n  ) = try {\n    // for sbt 1.1\n    KeyIndex\n      .asInstanceOf[{\n        def aggregate(\n          known: Iterable[ScopedKey[?]],\n          extra: BuildUtil[?],\n          projects: Map[URI, Set[String]],\n          configurations: Map[String, Seq[Configuration]]\n        ): ExtendableKeyIndex\n      }]\n      .aggregate(known = known, extra = extra, projects = projects, configurations = configurations)\n  } catch {\n    case _: NoSuchMethodException =>\n      // for sbt 1.0.x\n      KeyIndex\n        .asInstanceOf[{\n          def aggregate(\n            known: Iterable[ScopedKey[?]],\n            extra: BuildUtil[?],\n            projects: Map[URI, Set[String]]\n          ): ExtendableKeyIndex\n        }]\n        .aggregate(known = known, extra = extra, projects = projects)\n  }\n\n}\n"
  },
  {
    "path": "src/main/scala/ReleaseExtra.scala",
    "content": "package sbtrelease\n\nimport sbt.*\nimport sbt.Keys.*\nimport sbt.Package.ManifestAttributes\nimport sbtrelease.ReleasePlugin.autoImport.*\nimport sbtrelease.ReleasePlugin.autoImport.ReleaseKeys.*\nimport scala.annotation.tailrec\nimport scala.sys.process.ProcessLogger\n\nobject ReleaseStateTransformations {\n  import Utilities.*\n\n  lazy val checkSnapshotDependencies: ReleaseStep = ReleaseStep(\n    { (st: State) =>\n      val thisRef = st.extract.get(thisProjectRef)\n      val (newSt, result) = Compat.runTaskAggregated(thisRef / releaseSnapshotDependencies, st)\n      val snapshotDeps = result.toEither match {\n        case Right(value) => value.flatMap(_.value)\n        case Left(cause) => sys.error(\"Error checking for snapshot dependencies: \" + cause)\n      }\n      if (snapshotDeps.nonEmpty) {\n        val useDefaults = extractDefault(newSt, \"n\")\n        st.log.warn(\"Snapshot dependencies detected:\\n\" + snapshotDeps.mkString(\"\\n\"))\n        useDefaults orElse SimpleReader.readLine(\"Do you want to continue (y/n)? [n] \") match {\n          case Yes() =>\n          case _ => sys.error(\"Aborting release due to snapshot dependencies.\")\n        }\n      }\n      newSt\n    },\n    enableCrossBuild = true\n  )\n\n  lazy val inquireVersions: ReleaseStep = { (st: State) =>\n    val extracted = Project.extract(st)\n\n    val useDefs = st.get(useDefaults).getOrElse(false)\n    val currentV = extracted.get(version)\n\n    val releaseFunc = extracted.runTask(releaseVersion, st)._2\n    val suggestedReleaseV = releaseFunc(currentV)\n\n    st.log.info(\"Press enter to use the default value\")\n\n    // flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option\n    val releaseV =\n      readVersion(suggestedReleaseV, \"Release version [%s] : \", useDefs, st.get(commandLineReleaseVersion).flatten)\n\n    val nextFunc = extracted.runTask(releaseNextVersion, st)._2\n    val suggestedNextV = nextFunc(releaseV)\n    // flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option\n    val nextV = readVersion(suggestedNextV, \"Next version [%s] : \", useDefs, st.get(commandLineNextVersion).flatten)\n    st.put(versions, (releaseV, nextV))\n\n  }\n\n  lazy val runClean: ReleaseStep = ReleasePluginCompat.runClean\n\n  lazy val runTest: ReleaseStep = ReleaseStep(\n    action = { (st: State) =>\n      if (!st.get(skipTests).getOrElse(false)) {\n        val extracted = Project.extract(st)\n        val ref = extracted.get(thisProjectRef)\n        extracted.runAggregated(ref / Test / ReleasePluginCompat.testTask, st)\n      } else st\n    },\n    enableCrossBuild = true\n  )\n\n  lazy val setReleaseVersion: ReleaseStep = setVersion(_._1)\n  lazy val setNextVersion: ReleaseStep = setVersion(_._2)\n  val globalVersionString = \"ThisBuild / version := \\\"%s\\\"\"\n  private[this] val globalVersionStringOldSyntax = \"version in ThisBuild := \\\"%s\\\"\"\n  val versionString = \"version := \\\"%s\\\"\"\n  private[sbtrelease] def setVersion(selectVersion: Versions => String): ReleaseStep = { (st: State) =>\n    val vs = st\n      .get(versions)\n      .getOrElse(sys.error(\"No versions are set! Was this release part executed before inquireVersions?\"))\n    val selected = selectVersion(vs)\n\n    st.log.info(s\"Setting version to '${selected}'.\")\n    val useGlobal = st.extract.get(releaseUseGlobalVersion)\n    val versionStr = (\n      if (useGlobal) {\n        val v = Project.extract(st).get(sbtVersion)\n        // use new slash syntax if sbt 1.1 or later\n        // https://github.com/sbt/sbt/commit/21bd7c3a91a3407826\n        if (v.startsWith(\"0\") || v.startsWith(\"1.0\")) {\n          globalVersionStringOldSyntax\n        } else {\n          globalVersionString\n        }\n      } else {\n        versionString\n      }\n    ) format selected\n    writeVersion(st, versionStr)\n\n    reapply(\n      Seq(\n        if (useGlobal) ThisBuild / version := selected\n        else version := selected\n      ),\n      st\n    )\n  }\n\n  private def vcs(st: State): Vcs = {\n    st.extract\n      .get(releaseVcs)\n      .getOrElse(sys.error(\"Aborting release. Working directory is not a repository of a recognized VCS.\"))\n  }\n\n  private def writeVersion(st: State, versionString: String): Unit = {\n    val file = st.extract.get(releaseVersionFile)\n    IO.writeLines(file, Seq(versionString))\n  }\n\n  private[sbtrelease] lazy val initialVcsChecks = { (st: State) =>\n    val extracted = Project.extract(st)\n\n    val hasUntrackedFiles = vcs(st).hasUntrackedFiles\n    val hasModifiedFiles = vcs(st).hasModifiedFiles\n    if (hasModifiedFiles) {\n      sys.error(s\"\"\"Aborting release: unstaged modified files\n          |\n          |Modified files:\n          |\n          |${vcs(st).modifiedFiles.mkString(\" - \", \"\\n\", \"\")}\n        \"\"\".stripMargin)\n    }\n    if (hasUntrackedFiles && !extracted.get(releaseIgnoreUntrackedFiles)) {\n      sys.error(\n        s\"\"\"Aborting release: untracked files. Remove them or specify 'releaseIgnoreUntrackedFiles := true' in settings\n            |\n            |Untracked files:\n            |\n            |${vcs(st).untrackedFiles.mkString(\" - \", \"\\n\", \"\")}\n          \"\"\".stripMargin\n      )\n    }\n\n    st.log.info(\"Starting release process off commit: \" + vcs(st).currentHash)\n    st\n  }\n\n  lazy val commitReleaseVersion = ReleaseStep(commitReleaseVersionAction, initialVcsChecks)\n  private[sbtrelease] lazy val commitReleaseVersionAction = { (st: State) =>\n    val newState = commitVersion(st, releaseCommitMessage)\n    reapply(\n      Seq[Setting[?]](\n        packageOptions += ManifestAttributes(\n          \"Vcs-Release-Hash\" -> vcs(st).currentHash\n        )\n      ),\n      newState\n    )\n  }\n\n  lazy val commitNextVersion = { (st: State) => commitVersion(st, releaseNextCommitMessage) }\n\n  private[sbtrelease] def commitVersion = { (st: State, commitMessage: TaskKey[String]) =>\n    val log = toProcessLogger(st)\n    val file = st.extract.get(releaseVersionFile).getCanonicalFile\n    val base = vcs(st).baseDir.getCanonicalFile\n    val sign = st.extract.get(releaseVcsSign)\n    val signOff = st.extract.get(releaseVcsSignOff)\n    val relativePath = IO\n      .relativize(base, file)\n      .getOrElse(s\"Version file [${file}] is outside of this VCS repository with base directory [${base}]!\")\n\n    vcs(st).add(relativePath) !! log\n    val status = vcs(st).status.!!.trim\n\n    val newState = if (status.nonEmpty) {\n      val (state, msg) = st.extract.runTask(commitMessage, st)\n      vcs(state).commit(msg, sign, signOff) ! log\n      state\n    } else {\n      // nothing to commit. this happens if the version.sbt file hasn't changed.\n      st\n    }\n    newState\n  }\n\n  lazy val tagRelease: ReleaseStep = { (st: State) =>\n    val defaultChoice =\n      st.get(tagDefault) match {\n        case Some(Some(td)) => Some(td)\n        case _ => extractDefault(st, \"a\")\n      }\n\n    @tailrec\n    def findTag(tag: String): Option[String] = {\n      if (vcs(st).existsTag(tag)) {\n        defaultChoice orElse SimpleReader.readLine(\n          s\"Tag [${tag}] exists! Overwrite, keep or abort or enter a new tag (o/k/a)? [a] \"\n        ) match {\n          case Some(\"\" | \"a\" | \"A\") =>\n            sys.error(s\"Tag [${tag}] already exists. Aborting release!\")\n\n          case Some(\"k\" | \"K\") =>\n            st.log.warn(s\"The current tag [${tag}] does not point to the commit for this release!\")\n            None\n\n          case Some(\"o\" | \"O\") =>\n            st.log.warn(\n              s\"Overwriting a tag can cause problems if others have already seen the tag (see `${vcs(st).commandName} help tag`)!\"\n            )\n            Some(tag)\n\n          case Some(newTag) =>\n            findTag(newTag)\n\n          case None =>\n            sys.error(\"No tag entered. Aborting release!\")\n        }\n      } else {\n        Some(tag)\n      }\n    }\n\n    val (tagState, tag) = st.extract.runTask(releaseTagName, st)\n    val (commentState, comment) = st.extract.runTask(releaseTagComment, tagState)\n    val tagToUse = findTag(tag)\n    val sign = st.extract.get(releaseVcsSign)\n    val log = toProcessLogger(commentState)\n    tagToUse.foreach(vcs(commentState).tag(_, comment, sign) !! log)\n\n    tagToUse map (t =>\n      reapply(\n        Seq[Setting[?]](\n          packageOptions += ManifestAttributes(\"Vcs-Release-Tag\" -> t)\n        ),\n        commentState\n      )\n    ) getOrElse commentState\n  }\n\n  lazy val pushChanges: ReleaseStep = ReleaseStep(pushChangesAction, checkUpstream)\n  private[sbtrelease] lazy val checkUpstream = { (st: State) =>\n    if (!vcs(st).hasUpstream) {\n      sys.error(\n        \"No tracking branch is set up. Either configure a remote tracking branch, or remove the pushChanges release part.\"\n      )\n    }\n    val defaultChoice = extractDefault(st, \"n\")\n\n    val log = toProcessLogger(st)\n\n    st.log.info(s\"Checking remote [${vcs(st).trackingRemote}] ...\")\n    if (vcs(st).checkRemote(vcs(st).trackingRemote) ! log != 0) {\n      defaultChoice orElse SimpleReader.readLine(\"Error while checking remote. Still continue (y/n)? [n] \") match {\n        case Yes() => // do nothing\n        case _ => sys.error(\"Aborting the release!\")\n      }\n    }\n\n    if (vcs(st).isBehindRemote) {\n      defaultChoice orElse SimpleReader.readLine(\n        \"The upstream branch has unmerged commits. A subsequent push will fail! Continue (y/n)? [n] \"\n      ) match {\n        case Yes() => // do nothing\n        case _ => sys.error(\"Merge the upstream commits and run `release` again.\")\n      }\n    }\n    st\n  }\n\n  private def toProcessLogger(st: State): ProcessLogger = new ProcessLogger {\n    override def err(s: => String): Unit = st.log.info(s)\n    override def out(s: => String): Unit = st.log.info(s)\n    override def buffer[T](f: => T): T = st.log.buffer(f)\n  }\n\n  private[sbtrelease] lazy val pushChangesAction = { (st: State) =>\n    val defaultChoice = extractDefault(st, \"y\")\n\n    val log = toProcessLogger(st)\n\n    val vc = vcs(st)\n    if (vc.hasUpstream) {\n      defaultChoice orElse SimpleReader.readLine(\"Push changes to the remote repository (y/n)? [y] \") match {\n        case Yes() | Some(\"\") =>\n          val processLogger: ProcessLogger = if (vc.isInstanceOf[Git]) {\n            // Git outputs to standard error, so use a logger that redirects stderr to info\n            vc.stdErrorToStdOut(log)\n          } else log\n          vc.pushChanges !! processLogger\n        case _ => st.log.warn(\"Remember to push the changes yourself!\")\n      }\n    } else {\n      st.log.info(\n        s\"Changes were NOT pushed, because no upstream branch is configured for the local branch [${vcs(st).currentBranch}]\"\n      )\n    }\n    st\n  }\n\n  lazy val publishArtifacts = ReleaseStep(\n    action = runPublishArtifactsAction,\n    check = Compat.checkPublishTo,\n    enableCrossBuild = true\n  )\n  private[sbtrelease] lazy val runPublishArtifactsAction = { (st: State) =>\n    val extracted = st.extract\n    val ref = extracted.get(thisProjectRef)\n    extracted.runAggregated(ref / (Global / releasePublishArtifactsAction), st)\n  }\n\n  def readVersion(ver: String, prompt: String, useDef: Boolean, commandLineVersion: Option[String]): String = {\n    commandLineVersion.getOrElse(\n      if (useDef) ver\n      else\n        SimpleReader.readLine(prompt format ver) match {\n          case Some(\"\") => ver\n          case Some(input) => Version(input).map(_.string).getOrElse(versionFormatError(input))\n          case None => sys.error(\"No version provided!\")\n        }\n    )\n  }\n\n  def reapply(settings: Seq[Setting[?]], state: State): State = {\n    val extracted = state.extract\n    import extracted.*\n\n    val append = LoadCompat.transformSettings(Compat.projectScope(currentRef), currentRef.build, rootProject, settings)\n\n    // We don't want even want to be able to save the settings that are applied to the session during the release cycle.\n    // Just using an empty string works fine and in case the user calls `session save`, empty lines will be generated.\n    val newSession = session.appendSettings(append map (a => (a, List.empty[String])))\n    BuiltinCommands.reapply(newSession, structure, state)\n  }\n\n  def crossExclude(s: Setting[?]): Boolean = Compat.excludeKeys(Set(scalaVersion.key, scalaHome.key))(s)\n\n  // This is a copy of the state function for the command Cross.switchVersion\n  private[sbtrelease] def switchScalaVersion(state: State, version: String): State = {\n    val x = Project.extract(state)\n    import x.{*, given}\n    state.log.info(\"Setting scala version to \" + version)\n    val add = (GlobalScope / scalaVersion := version) :: (GlobalScope / scalaHome := None) :: Nil\n    val cleared = session.mergeSettings.filterNot(crossExclude)\n    val newStructure = LoadCompat.reapply(add ++ cleared, structure)\n    Project.setProject(session, newStructure, state)\n  }\n\n  private[sbtrelease] def runCrossBuild(func: State => State): State => State = { state =>\n    val x = Project.extract(state)\n    import x.*\n    val versions = Compat.crossVersions(state)\n    val current = (currentRef / scalaVersion) get structure.data\n    val finalS = versions.foldLeft(state) { case (s, v) =>\n      func(switchScalaVersion(s, v))\n    }\n    current.map(switchScalaVersion(finalS, _)).getOrElse(finalS)\n  }\n}\n\nobject ExtraReleaseCommands {\n  import ReleaseStateTransformations.*\n\n  private lazy val initialVcsChecksCommandKey = \"release-vcs-checks\"\n  lazy val initialVcsChecksCommand = Command.command(initialVcsChecksCommandKey)(initialVcsChecks)\n\n  private lazy val checkSnapshotDependenciesCommandKey = \"release-check-snapshot-dependencies\"\n  lazy val checkSnapshotDependenciesCommand =\n    Command.command(checkSnapshotDependenciesCommandKey)(checkSnapshotDependencies)\n\n  private lazy val inquireVersionsCommandKey = \"release-inquire-versions\"\n  lazy val inquireVersionsCommand = Command.command(inquireVersionsCommandKey)(inquireVersions)\n\n  private lazy val setReleaseVersionCommandKey = \"release-set-release-version\"\n  lazy val setReleaseVersionCommand = Command.command(setReleaseVersionCommandKey)(setReleaseVersion)\n\n  private lazy val setNextVersionCommandKey = \"release-set-next-version\"\n  lazy val setNextVersionCommand = Command.command(setNextVersionCommandKey)(setNextVersion)\n\n  private lazy val commitReleaseVersionCommandKey = \"release-commit-release-version\"\n  lazy val commitReleaseVersionCommand = Command.command(commitReleaseVersionCommandKey)(commitReleaseVersion)\n\n  private lazy val commitNextVersionCommandKey = \"release-commit-next-version\"\n  lazy val commitNextVersionCommand = Command.command(commitNextVersionCommandKey)(commitNextVersion)\n\n  private lazy val tagReleaseCommandKey = \"release-tag-release\"\n  lazy val tagReleaseCommand = Command.command(tagReleaseCommandKey)(tagRelease)\n\n  private lazy val pushChangesCommandKey = \"release-push-changes\"\n  lazy val pushChangesCommand = Command.command(pushChangesCommandKey)(pushChanges)\n}\n\nobject Utilities {\n\n  implicit class StateW(st: State) {\n    def extract = Project.extract(st)\n  }\n  @deprecated(\"will be removed\")\n  private[sbtrelease] def stateW(st: State): StateW = new StateW(st)\n\n  private[sbtrelease] def resolve[T](key: ScopedKey[T], extracted: Extracted): ScopedKey[T] =\n    Project.mapScope(Scope.resolveScope(GlobalScope, extracted.currentRef.build, extracted.rootProject))(key.scopedKey)\n\n  object Yes {\n    def unapply(s: Option[String]) = s.exists(_.toLowerCase == \"y\")\n  }\n\n  def extractDefault(st: State, default: String): Option[String] = {\n    val useDefs = st.get(useDefaults).getOrElse(false)\n    if (useDefs) Some(default)\n    else None\n  }\n\n}\n"
  },
  {
    "path": "src/main/scala/ReleasePlugin.scala",
    "content": "package sbtrelease\n\nimport java.io.Serializable\nimport sbt.*\nimport sbt.Keys.*\nimport sbt.complete.DefaultParsers.*\nimport sbt.complete.Parser\nimport sbtrelease.Version.Bump\n\nobject ReleasePlugin extends AutoPlugin {\n\n  object autoImport {\n    @transient\n    val releaseSnapshotDependencies = taskKey[Seq[ModuleID]](\"Calculate the snapshot dependencies for a build\")\n    val releaseProcess = settingKey[Seq[ReleaseStep]](\"The release process\")\n    @transient\n    val releaseVersion = taskKey[String => String](\"The release version\")\n    @transient\n    val releaseNextVersion = taskKey[String => String](\"The next release version\")\n    @transient\n    val releaseVersionBump = taskKey[Version.Bump](\"How the version should be incremented\")\n    @transient\n    val releaseTagName = taskKey[String](\"The name of the tag\")\n    @transient\n    val releaseTagComment = taskKey[String](\"The comment to use when tagging\")\n    @transient\n    val releaseCommitMessage = taskKey[String](\"The commit message to use when tagging\")\n    @transient\n    val releaseNextCommitMessage = taskKey[String](\"The commit message to use for next iteration\")\n    val releaseCrossBuild = settingKey[Boolean](\"Whether the release should be cross built\")\n    val releaseVersionFile = settingKey[File](\"The file to write the version to\")\n    val releaseUseGlobalVersion = settingKey[Boolean](\"Whether to use a global version\")\n    val releaseIgnoreUntrackedFiles = settingKey[Boolean](\"Whether to ignore untracked files\")\n    val releaseVcsSign = settingKey[Boolean](\"Whether to sign VCS commits and tags\")\n    val releaseVcsSignOff = settingKey[Boolean](\"Whether to signoff VCS commits\")\n\n    val releaseVcs = settingKey[Option[Vcs]](\"The VCS to use\")\n    @transient\n    val releasePublishArtifactsAction = taskKey[Unit](\"The action that should be performed to publish artifacts\")\n\n    lazy val ReleaseTransformations = sbtrelease.ReleaseStateTransformations\n\n    case class ReleaseStep(action: State => State, check: State => State = identity, enableCrossBuild: Boolean = false)\n\n    object ReleaseStep {\n      implicit def func2ReleasePart(f: State => State): ReleaseStep = ReleaseStep(f)\n\n      implicit def releasePart2Func(rp: ReleaseStep): State => State = rp.action\n    }\n\n    @deprecated(\"Use releaseStepTaskAggregated\", \"1.0.0\")\n    def releaseTask[T](key: TaskKey[T]) = { (st: State) =>\n      Project.extract(st).runAggregated(key, st)\n    }\n\n    /**\n     * Convert the given task key to a release step action.\n     */\n    def releaseStepTask[T](key: TaskKey[T]) = { (st: State) =>\n      Project.extract(st).runTask(key, st)._1\n    }\n\n    /**\n     * Convert the given task key to a release step action that gets run aggregated.\n     */\n    def releaseStepTaskAggregated[T](key: TaskKey[T]): State => State = { (st: State) =>\n      Project.extract(st).runAggregated(key, st)\n    }\n\n    /**\n     * Convert the given input task key and input to a release step action.\n     */\n    def releaseStepInputTask[T](key: InputKey[T], input: String = \"\"): State => State = { (st: State) =>\n      import EvaluateTask.*\n      val extracted = Project.extract(st)\n      val inputTask = extracted.get(Scoped.scopedSetting(key.scope, key.key))\n      val task = Parser.parse(input, inputTask.parser(st)) match {\n        case Right(t) => t\n        case Left(msg) => sys.error(s\"Invalid programmatic input:\\n$msg\")\n      }\n      val config = extractedTaskConfig(extracted, extracted.structure, st)\n      withStreams(extracted.structure, st) { str =>\n        val nv = nodeView(st, str, key :: Nil)\n        val (newS, result) = runTask(task, st, str, extracted.structure.index.triggers, config)(using nv)\n        (newS, processResult2(result))\n      }._1\n    }\n\n    /**\n     * Convert the given command and input to a release step action\n     */\n    def releaseStepCommand(command: Command, input: String = \"\"): State => State = { (st: State) =>\n      Parser.parse(input, command.parser(st)) match {\n        case Right(cmd) => cmd()\n        case Left(msg) => sys.error(s\"Invalid programmatic input:\\n$msg\")\n      }\n    }\n\n    /**\n     * Convert the given command string to a release step action\n     */\n    def releaseStepCommand(command: String): State => State = { (st: State) =>\n      Parser.parse(command, st.combinedParser) match {\n        case Right(cmd) => cmd()\n        case Left(msg) => sys.error(s\"Invalid programmatic input:\\n$msg\")\n      }\n    }\n\n    /**\n     * Convert the given command string to a release step action, preserving and invoking remaining commands\n     */\n    def releaseStepCommandAndRemaining(command: String): State => State = { (initState: State) =>\n      import Compat.*\n      @annotation.tailrec\n      def runCommand(command: Compat.Command, state: State): State = {\n        val nextState = Parser.parse(command.commandLine, state.combinedParser) match {\n          case Right(cmd) => cmd()\n          case Left(msg) => sys.error(s\"Invalid programmatic input:\\n$msg\")\n        }\n        nextState.remainingCommands.toList match {\n          case Nil => nextState.copy(remainingCommands = initState.remainingCommands)\n          case Compat.FailureCommand :: tail =>\n            nextState.copy(remainingCommands = FailureCommand +: initState.remainingCommands)\n          case head :: tail => runCommand(head, nextState.copy(remainingCommands = tail))\n        }\n      }\n      runCommand(Exec(command, None, None), initState.copy(remainingCommands = Nil))\n    }\n\n    object ReleaseKeys {\n\n      val versions = AttributeKey[Versions](\"releaseVersions\")\n      val commandLineReleaseVersion = AttributeKey[Option[String]](\"release-input-release-version\")\n      val commandLineNextVersion = AttributeKey[Option[String]](\"release-input-next-version\")\n      val useDefaults = AttributeKey[Boolean](\"releaseUseDefaults\")\n      val skipTests = AttributeKey[Boolean](\"releaseSkipTests\")\n      val cross = AttributeKey[Boolean](\"releaseCross\")\n      val tagDefault = AttributeKey[Option[String]](\"release-default-tag-exists-answer\")\n\n      private lazy val releaseCommandKey = \"release\"\n      private val FailureCommand = Compat.FailureCommand\n\n      private[this] val WithDefaults: Parser[ParseResult] =\n        (Space ~> token(\"with-defaults\")) ^^^ ParseResult.WithDefaults\n      private[this] val SkipTests: Parser[ParseResult] =\n        (Space ~> token(\"skip-tests\")) ^^^ ParseResult.SkipTests\n      private[this] val CrossBuild: Parser[ParseResult] =\n        (Space ~> token(\"cross\")) ^^^ ParseResult.CrossBuild\n      private[this] val ReleaseVersion: Parser[ParseResult] =\n        (Space ~> token(\"release-version\") ~> Space ~> token(\n          StringBasic,\n          \"<release version>\"\n        )) map ParseResult.ReleaseVersion\n      private[this] val NextVersion: Parser[ParseResult] =\n        (Space ~> token(\"next-version\") ~> Space ~> token(StringBasic, \"<next version>\")) map ParseResult.NextVersion\n      private[this] val TagDefault: Parser[ParseResult] =\n        (Space ~> token(\"default-tag-exists-answer\") ~> Space ~> token(\n          StringBasic,\n          \"o|k|a|<tag-name>\"\n        )) map ParseResult.TagDefault\n\n      private[this] sealed abstract class ParseResult extends Product with Serializable\n      private[this] object ParseResult {\n        final case class ReleaseVersion(value: String) extends ParseResult\n        object ReleaseVersion extends (String => ParseResult)\n        final case class NextVersion(value: String) extends ParseResult\n        object NextVersion extends (String => ParseResult)\n        final case class TagDefault(value: String) extends ParseResult\n        object TagDefault extends (String => ParseResult)\n        case object WithDefaults extends ParseResult\n        case object SkipTests extends ParseResult\n        case object CrossBuild extends ParseResult\n      }\n\n      private[this] val releaseParser: Parser[Seq[ParseResult]] =\n        (ReleaseVersion | NextVersion | WithDefaults | SkipTests | CrossBuild | TagDefault).*\n\n      val releaseCommand: Command = Command(releaseCommandKey)(_ => releaseParser) { (st, args) =>\n        val extracted = Project.extract(st)\n        val releaseParts = extracted.get(releaseProcess)\n        val crossEnabled = extracted.get(releaseCrossBuild) || args.contains(ParseResult.CrossBuild)\n\n        val startState = st\n          .copy(onFailure = Some(FailureCommand))\n          .put(useDefaults, args.contains(ParseResult.WithDefaults))\n          .put(skipTests, args.contains(ParseResult.SkipTests))\n          .put(cross, crossEnabled)\n          .put(tagDefault, args.collectFirst { case ParseResult.TagDefault(value) => value })\n          .put(commandLineReleaseVersion, args.collectFirst { case ParseResult.ReleaseVersion(value) => value })\n          .put(commandLineNextVersion, args.collectFirst { case ParseResult.NextVersion(value) => value })\n\n        val initialChecks = releaseParts.map(_.check)\n\n        def filterFailure(f: State => State)(s: State): State = {\n          s.remainingCommands match {\n            case FailureCommand :: tail => s.fail\n            case _ => f(s)\n          }\n        }\n\n        val removeFailureCommand = { (s: State) =>\n          s.remainingCommands match {\n            case FailureCommand :: tail => s.copy(remainingCommands = tail)\n            case _ => s\n          }\n        }\n\n        val failureCheck = { (s: State) =>\n          filterFailure(_.copy(onFailure = Some(FailureCommand)))(s)\n        }\n\n        val process = releaseParts.map { step =>\n          if (step.enableCrossBuild && crossEnabled) {\n            filterFailure(ReleaseStateTransformations.runCrossBuild(step.action)) _\n          } else filterFailure(step.action) _\n        }\n\n        initialChecks.foreach(_(startState))\n        Function.chain(\n          (process :+ removeFailureCommand).flatMap(Seq(_, failureCheck))\n        )(startState)\n      }\n    }\n  }\n\n  import ReleaseStateTransformations.*\n  import autoImport.*\n  import autoImport.ReleaseKeys.*\n\n  override def trigger = allRequirements\n\n  val runtimeVersion = Def.task {\n    val v1 = (ThisBuild / version).value\n    val v2 = version.value\n    if (releaseUseGlobalVersion.value) v1 else v2\n  }\n\n  override def projectSettings = Seq[Setting[?]](\n    releaseSnapshotDependencies := {\n      val moduleIds = ReleasePluginCompat.moduleIds.value\n      val snapshots = moduleIds.filter(m => m.isChanging || m.revision.endsWith(\"-SNAPSHOT\"))\n      snapshots\n    },\n    releaseVersion := { rawVersion =>\n      Version(rawVersion).map { version =>\n        releaseVersionBump.value match {\n          case Bump.Next =>\n            if (version.isSnapshot) {\n              version.withoutSnapshot.unapply\n            } else {\n              expectedSnapshotVersionError(rawVersion)\n            }\n          case _ => version.withoutQualifier.unapply\n        }\n      }.getOrElse(versionFormatError(rawVersion))\n    },\n    releaseVersionBump := Version.Bump.default,\n    releaseNextVersion := { ver =>\n      Version(ver).map(_.bump(releaseVersionBump.value).asSnapshot.unapply).getOrElse(versionFormatError(ver))\n    },\n    releaseUseGlobalVersion := true,\n    releaseCrossBuild := false,\n    releaseTagName := s\"v${runtimeVersion.value}\",\n    releaseTagComment := s\"Releasing ${runtimeVersion.value}\",\n    releaseCommitMessage := s\"Setting version to ${runtimeVersion.value}\",\n    releaseNextCommitMessage := s\"Setting version to ${runtimeVersion.value}\",\n    releaseVcs := Vcs.detect(baseDirectory.value),\n    releaseVcsSign := false,\n    releaseVcsSignOff := false,\n    releaseVersionFile := baseDirectory.value / \"version.sbt\",\n    releasePublishArtifactsAction := publish.value,\n    releaseIgnoreUntrackedFiles := false,\n    releaseProcess := Seq[ReleaseStep](\n      checkSnapshotDependencies,\n      inquireVersions,\n      runClean,\n      runTest,\n      setReleaseVersion,\n      commitReleaseVersion,\n      tagRelease,\n      publishArtifacts,\n      setNextVersion,\n      commitNextVersion,\n      pushChanges\n    ),\n    commands += releaseCommand\n  )\n\n  lazy val extraReleaseCommands = {\n    import ExtraReleaseCommands.*\n\n    Seq[Setting[?]](\n      commands ++= Seq(\n        checkSnapshotDependenciesCommand,\n        inquireVersionsCommand,\n        setReleaseVersionCommand,\n        setNextVersionCommand,\n        initialVcsChecksCommand,\n        commitReleaseVersionCommand,\n        commitNextVersionCommand,\n        tagReleaseCommand,\n        pushChangesCommand\n      )\n    )\n  }\n}\n"
  },
  {
    "path": "src/main/scala/Vcs.scala",
    "content": "package sbtrelease\n\nimport java.io.File\nimport sbt.*\nimport sys.process.Process\nimport sys.process.ProcessBuilder\nimport sys.process.ProcessLogger\n\ntrait Vcs {\n  val commandName: String\n\n  val baseDir: File\n\n  def cmd(args: Any*): ProcessBuilder\n  def status: ProcessBuilder\n  def currentHash: String\n  def add(files: String*): ProcessBuilder\n  def commit(message: String, sign: Boolean, signOff: Boolean): ProcessBuilder\n  def existsTag(name: String): Boolean\n  def checkRemote(remote: String): ProcessBuilder\n  def tag(name: String, comment: String, sign: Boolean): ProcessBuilder\n  def hasUpstream: Boolean\n  def trackingRemote: String\n  def isBehindRemote: Boolean\n  def pushChanges: ProcessBuilder\n  def currentBranch: String\n  def hasUntrackedFiles: Boolean = untrackedFiles.nonEmpty\n  def untrackedFiles: Seq[String]\n  def hasModifiedFiles: Boolean = modifiedFiles.nonEmpty\n  def modifiedFiles: Seq[String]\n\n  protected def executableName(command: String) = {\n    val maybeOsName = sys.props.get(\"os.name\").map(_.toLowerCase)\n    val maybeIsWindows = maybeOsName.filter(_.contains(\"windows\"))\n    maybeIsWindows.map(_ => command + \".exe\").getOrElse(command)\n  }\n\n  protected val devnull: ProcessLogger = new ProcessLogger {\n    override def out(s: => String): Unit = {}\n    override def err(s: => String): Unit = {}\n    override def buffer[T](f: => T): T = f\n  }\n\n  def stdErrorToStdOut(delegate: ProcessLogger): ProcessLogger = new ProcessLogger {\n    override def out(s: => String): Unit = delegate.out(s)\n    override def err(s: => String): Unit = delegate.out(s)\n    override def buffer[T](f: => T): T = delegate.buffer(f)\n  }\n}\n\nobject Vcs {\n  def detect(dir: File): Option[Vcs] = {\n    Stream(Git, Mercurial, Subversion).flatMap(comp => comp.isRepository(dir).map(comp.mkVcs(_))).headOption\n  }\n}\n\ntrait GitLike extends Vcs {\n  private lazy val exec = executableName(commandName)\n\n  def cmd(args: Any*): ProcessBuilder = Process(exec +: args.map(_.toString), baseDir)\n\n  def add(files: String*) = cmd((\"add\" +: files)*)\n}\n\ntrait VcsCompanion {\n  protected val markerDirectory: String\n\n  // Using the new git worktree feature the dir is now a file, so checking for exists should be enough\n  def isRepository(dir: File): Option[File] =\n    if (new File(dir, markerDirectory).exists) Some(dir)\n    else Option(dir.getParentFile).flatMap(isRepository)\n\n  def mkVcs(baseDir: File): Vcs\n}\n\nobject Mercurial extends VcsCompanion {\n  protected val markerDirectory = \".hg\"\n\n  def mkVcs(baseDir: File) = new Mercurial(baseDir)\n}\n\nclass Mercurial(val baseDir: File) extends Vcs with GitLike {\n  val commandName = \"hg\"\n\n  private def andSign(sign: Boolean, proc: ProcessBuilder) =\n    if (sign)\n      proc #&& cmd(\"sign\")\n    else\n      proc\n\n  def status = cmd(\"status\")\n\n  def currentHash = cmd(\"identify\", \"-i\").!!.trim\n\n  def existsTag(name: String) = cmd(\"tags\").!!.linesIterator.exists(_.endsWith(\" \" + name))\n\n  def commit(message: String, sign: Boolean, signOff: Boolean) =\n    andSign(sign, cmd(\"commit\", \"-m\", message))\n\n  def tag(name: String, comment: String, sign: Boolean) =\n    andSign(sign, cmd(\"tag\", \"-f\", \"-m\", comment, name))\n\n  def hasUpstream = cmd(\"paths\", \"default\") ! devnull == 0\n\n  def trackingRemote = \"default\"\n\n  def isBehindRemote = cmd(\"incoming\", \"-b\", \".\", \"-q\") ! devnull == 0\n\n  def pushChanges = cmd(\"push\", \"-b\", \".\")\n\n  def currentBranch = cmd(\"branch\").!!.trim\n\n  // FIXME: This is utterly bogus, but I cannot find a good way...\n  def checkRemote(remote: String) = cmd(\"id\", \"-n\")\n\n  def untrackedFiles = cmd(\"status\", \"-un\").lineStream\n  def modifiedFiles = cmd(\"status\", \"-mn\").lineStream\n}\n\nobject Git extends VcsCompanion {\n  protected val markerDirectory = \".git\"\n\n  def mkVcs(baseDir: File) = new Git(baseDir)\n\n  private final case class GitFlag(on: Boolean, flag: String)\n}\n\nclass Git(val baseDir: File) extends Vcs with GitLike {\n  val commandName = \"git\"\n\n  import Git.GitFlag\n\n  private lazy val trackingBranchCmd = cmd(\"config\", s\"branch.${currentBranch}.merge\")\n  private def trackingBranch: String = trackingBranchCmd.!!.trim.stripPrefix(\"refs/heads/\")\n\n  private lazy val trackingRemoteCmd: ProcessBuilder = cmd(\"config\", s\"branch.${currentBranch}.remote\")\n  def trackingRemote: String = trackingRemoteCmd.!!.trim\n\n  def hasUpstream = trackingRemoteCmd ! devnull == 0 && trackingBranchCmd ! devnull == 0\n\n  def currentBranch = cmd(\"symbolic-ref\", \"HEAD\").!!.trim.stripPrefix(\"refs/heads/\")\n\n  def currentHash = revParse(\"HEAD\")\n\n  private def revParse(name: String) = cmd(\"rev-parse\", name).!!.trim\n\n  def isBehindRemote =\n    (cmd(\"rev-list\", s\"${currentBranch}..${trackingRemote}/${trackingBranch}\") !! devnull).trim.nonEmpty\n\n  private def withFlags(flags: Seq[GitFlag])(args: String*): Seq[String] = {\n    val appended = flags.collect { case GitFlag(true, flag) =>\n      s\"-$flag\"\n    }\n\n    args ++ appended\n  }\n\n  def commit(message: String, sign: Boolean, signOff: Boolean) = {\n    val gitFlags = List(GitFlag(sign, \"S\"), GitFlag(signOff, \"s\"))\n    cmd(withFlags(gitFlags)(\"commit\", \"-m\", message)*)\n  }\n\n  def tag(name: String, comment: String, sign: Boolean) =\n    cmd(withFlags(List(GitFlag(sign, \"s\")))(\"tag\", \"-f\", \"-a\", name, \"-m\", comment)*)\n\n  def existsTag(name: String) = cmd(\"show-ref\", \"--quiet\", \"--tags\", \"--verify\", \"refs/tags/\" + name) ! devnull == 0\n\n  def checkRemote(remote: String) = fetch(remote)\n\n  def fetch(remote: String) = cmd(\"fetch\", remote)\n\n  def status = cmd(\"status\", \"--porcelain\")\n\n  def pushChanges = pushCurrentBranch #&& pushTags\n\n  private def pushCurrentBranch = {\n    val localBranch = currentBranch\n    cmd(\"push\", trackingRemote, s\"${localBranch}:${trackingBranch}\")\n  }\n\n  private def pushTags = cmd(\"push\", \"--tags\", trackingRemote)\n\n  def untrackedFiles = cmd(\"ls-files\", \"--other\", \"--exclude-standard\").lineStream\n  def modifiedFiles = cmd(\"ls-files\", \"--modified\", \"--exclude-standard\").lineStream\n}\n\nobject Subversion extends VcsCompanion {\n  override def mkVcs(baseDir: File): Vcs = new Subversion(baseDir)\n\n  override protected val markerDirectory: String = \".svn\"\n}\n\nclass Subversion(val baseDir: File) extends Vcs {\n  override val commandName = \"svn\"\n  private lazy val exec = executableName(commandName)\n\n  override def cmd(args: Any*): ProcessBuilder = Process(exec +: args.map(_.toString), baseDir)\n\n  override def modifiedFiles = cmd(\"status\", \"-q\").lineStream\n  override def untrackedFiles = cmd(\"status\").lineStream.filter(_.startsWith(\"?\"))\n\n  override def add(files: String*) = {\n    val filesToAdd = files.filterNot(isFileUnderVersionControl)\n    if (!filesToAdd.isEmpty) cmd((\"add\" +: filesToAdd)*) else noop\n  }\n\n  override def commit(message: String, sign: Boolean, signOff: Boolean) = {\n    require(!sign, \"Signing not supported in Subversion.\")\n    require(!signOff, \"Signing off not supported in Subversion.\")\n    cmd(\"commit\", \"-m\", message)\n  }\n\n  override def currentBranch: String = workingDirSvnUrl.substring(workingDirSvnUrl.lastIndexOf(\"/\") + 1)\n\n  override def pushChanges: ProcessBuilder = commit(\"push changes\", false, false)\n\n  override def isBehindRemote: Boolean = false\n\n  override def trackingRemote: String = \"\"\n\n  override def hasUpstream: Boolean = true\n\n  override def tag(name: String, comment: String, sign: Boolean): ProcessBuilder = {\n    require(!sign, \"Signing not supported in Subversion.\")\n    val tagUrl = getSvnTagUrl(name)\n    if (existsTag(name)) {\n      val deleteTagComment = comment + \", \\ndelete tag \" + name + \" to create a new one.\"\n      cmd(\"del\", tagUrl, \"-m\", deleteTagComment).!!\n    }\n    cmd(\"copy\", workingDirSvnUrl, tagUrl, \"-m\", comment)\n  }\n\n  override def checkRemote(remote: String): ProcessBuilder = noop\n\n  override def existsTag(name: String): Boolean = {\n    Try(cmd(\"info\", getSvnTagUrl(name)).!!).nonEmpty\n  }\n\n  override def currentHash: String = \"\"\n\n  override def status: ProcessBuilder = cmd(\"status\", \"-q\")\n\n  lazy val workingDirSvnUrl: String = {\n    val svnInfo = cmd(\"info\").!!\n    val svnInfoUrlKey = \"URL: \"\n    val urlStartIdx = svnInfo.indexOf(svnInfoUrlKey) + svnInfoUrlKey.length\n    svnInfo.substring(urlStartIdx, svnInfo.indexOf('\\n', urlStartIdx) - 1)\n  }\n\n  lazy val repoRoot: String = {\n    val svnBaseUrlEndIdxOptions = List(\n      workingDirSvnUrl.indexOf(\"/trunk\"),\n      workingDirSvnUrl.indexOf(\"/branches\"),\n      workingDirSvnUrl.indexOf(\"/tags\")\n    ).filter(_ >= 0)\n    require(\n      !svnBaseUrlEndIdxOptions.isEmpty,\n      \"No /trunk, /branches or /tags part found in svn url. Base url cannot be extracted.\"\n    )\n    val svnBaseUrlEndIdx = svnBaseUrlEndIdxOptions.head\n    workingDirSvnUrl.substring(0, svnBaseUrlEndIdx + 1)\n  }\n\n  private def getSvnTagUrl(name: String): String = repoRoot + \"tags/\" + name\n\n  private def isFileUnderVersionControl(file: String): Boolean = Try(cmd(\"info\", file).!!).nonEmpty\n\n  private def noop: ProcessBuilder = status\n}\n\nprivate[sbtrelease] object Try {\n  def apply[A](f: => A): Option[A] = scala.util.control.Exception.allCatch.opt(f)\n}\n"
  },
  {
    "path": "src/main/scala/Version.scala",
    "content": "package sbtrelease\n\nimport scala.util.matching.Regex\nimport util.control.Exception.*\n\nobject Version {\n  sealed trait Bump {\n    def bump: Version => Version\n  }\n\n  object Bump {\n\n    /**\n     * Strategy to always bump the major version by default. Ex. 1.0.0 would be bumped to 2.0.0\n     */\n    case object Major extends Bump { def bump: Version => Version = _.bumpMajor }\n\n    /**\n     * Strategy to always bump the minor version by default. Ex. 1.0.0 would be bumped to 1.1.0\n     */\n    case object Minor extends Bump { def bump: Version => Version = _.bumpMinor }\n\n    /**\n     * Strategy to always bump the bugfix version by default. Ex. 1.0.0 would be bumped to 1.0.1\n     */\n    case object Bugfix extends Bump { def bump: Version => Version = _.bumpBugfix }\n\n    /**\n     * Strategy to always bump the nano version by default. Ex. 1.0.0.0 would be bumped to 1.0.0.1\n     */\n    case object Nano extends Bump { def bump: Version => Version = _.bumpNano }\n\n    /**\n     * Strategy to always increment to the next version from smallest to greatest, including prerelease versions\n     * Ex:\n     * Major: 1 becomes 2\n     * Minor: 1.0 becomes 1.1\n     * Bugfix: 1.0.0 becomes 1.0.1\n     * Nano: 1.0.0.0 becomes 1.0.0.1\n     * Qualifier with version number: 1.0-RC1 becomes 1.0-RC2\n     * Qualifier without version number: 1.0-alpha becomes 1.0\n     */\n    case object Next extends Bump { def bump: Version => Version = _.bumpNext }\n\n    /**\n     * Strategy to always increment to the next version from smallest to greatest, excluding prerelease versions\n     * Ex:\n     * Major: 1 becomes 2\n     * Minor: 1.0 becomes 1.1\n     * Bugfix: 1.0.0 becomes 1.0.1\n     * Nano: 1.0.0.0 becomes 1.0.0.1\n     * Qualifier with version number: 1.0-RC1 becomes 1.0\n     * Qualifier without version number: 1.0-alpha becomes 1.0\n     */\n    case object NextStable extends Bump { def bump: Version => Version = _.bumpNextStable }\n\n    val default: Bump = Next\n  }\n\n  val VersionR: Regex = \"\"\"([0-9]+)((?:\\.[0-9]+)+)?([\\.\\-0-9a-zA-Z]*)?\"\"\".r\n  val PreReleaseQualifierR: Regex = \"\"\"[\\.-](?i:rc|m|alpha|beta)[\\.-]?[0-9]*\"\"\".r\n\n  def apply(s: String): Option[Version] = {\n    allCatch opt {\n      val VersionR(maj, subs, qual) = s\n      // parse the subversions (if any) to a Seq[Int]\n      val subSeq: Seq[Int] = Option(subs) map { str =>\n        // split on . and remove empty strings\n        str.split('.').filterNot(_.trim.isEmpty).map(_.toInt).toSeq\n      } getOrElse Nil\n      Version(maj.toInt, subSeq, Option(qual).filterNot(_.isEmpty))\n    }\n  }\n}\n\ncase class Version(major: Int, subversions: Seq[Int], qualifier: Option[String]) {\n\n  @deprecated(\"Use .bumpNext or .bumpNextStable instead\")\n  def bump: Version = bumpNext\n\n  def bumpNext: Version = {\n    val bumpedPrereleaseVersionOpt = qualifier.collect { case rawQualifier @ Version.PreReleaseQualifierR() =>\n      val qualifierEndsWithNumberRegex = \"\"\"[0-9]*$\"\"\".r\n\n      val opt = for {\n        versionNumberQualifierStr <- qualifierEndsWithNumberRegex.findFirstIn(rawQualifier)\n        versionNumber <- Try(versionNumberQualifierStr.toInt)\n          .toRight(\n            new Exception(\n              s\"Version number not parseable to a number. Version number received: $versionNumberQualifierStr\"\n            )\n          )\n          .toOption\n        newVersionNumber = versionNumber + 1\n        newQualifier = rawQualifier.replaceFirst(versionNumberQualifierStr, newVersionNumber.toString)\n      } yield Version(major, subversions, Some(newQualifier))\n\n      opt.getOrElse(this.withoutQualifier)\n    }\n\n    bumpNextGeneric(bumpedPrereleaseVersionOpt)\n  }\n  private def bumpNextGeneric(bumpedPrereleaseVersionOpt: Option[Version]): Version = {\n    def maybeBumpedLastSubversion = bumpSubversionOpt(subversions.length - 1)\n\n    def bumpedMajor = copy(major = major + 1)\n\n    bumpedPrereleaseVersionOpt.orElse(maybeBumpedLastSubversion).getOrElse(bumpedMajor)\n  }\n\n  def bumpNextStable: Version = {\n    val bumpedPrereleaseVersionOpt = qualifier.collect { case Version.PreReleaseQualifierR() =>\n      withoutQualifier\n    }\n    bumpNextGeneric(bumpedPrereleaseVersionOpt)\n  }\n\n  def bumpMajor: Version = copy(major = major + 1, subversions = Seq.fill(subversions.length)(0))\n  def bumpMinor: Version = maybeBumpSubversion(0)\n  def bumpBugfix: Version = maybeBumpSubversion(1)\n  def bumpNano: Version = maybeBumpSubversion(2)\n\n  def maybeBumpSubversion(idx: Int): Version = bumpSubversionOpt(idx) getOrElse this\n\n  private def bumpSubversionOpt(idx: Int) = {\n    val bumped = subversions.drop(idx)\n    val reset = bumped.drop(1).length\n    bumped.headOption map { head =>\n      val patch = (head + 1) +: Seq.fill(reset)(0)\n      copy(subversions = subversions.patch(idx, patch, patch.length))\n    }\n  }\n\n  def bump(bumpType: Version.Bump): Version = bumpType.bump(this)\n\n  def withoutQualifier: Version = copy(qualifier = None)\n  def asSnapshot: Version = copy(qualifier = qualifier.map { qualifierStr =>\n    s\"$qualifierStr-SNAPSHOT\"\n  }.orElse(Some(\"-SNAPSHOT\")))\n\n  def isSnapshot: Boolean = qualifier.exists { qualifierStr =>\n    val snapshotRegex = \"\"\"(^.*)-SNAPSHOT$\"\"\".r\n    qualifierStr.matches(snapshotRegex.regex)\n  }\n\n  def withoutSnapshot: Version = copy(qualifier = qualifier.flatMap { qualifierStr =>\n    val snapshotRegex = \"\"\"-SNAPSHOT\"\"\".r\n    val newQualifier = snapshotRegex.replaceFirstIn(qualifierStr, \"\")\n    if (newQualifier == qualifierStr) {\n      None\n    } else {\n      Some(newQualifier)\n    }\n  })\n\n  @deprecated(\"Use .unapply instead\")\n  def string: String = unapply\n\n  def unapply: String = \"\" + major + mkString(subversions) + qualifier.getOrElse(\"\")\n\n  private def mkString(parts: Seq[Int]) = parts.map(\".\" + _).mkString\n}\n"
  },
  {
    "path": "src/main/scala/package.scala",
    "content": "package object sbtrelease {\n  type Versions = (String, String)\n\n  def versionFormatError(version: String) =\n    sys.error(s\"Version [$version] format is not compatible with \" + Version.VersionR.pattern.toString)\n\n  def expectedSnapshotVersionError(version: String) = sys.error(s\"Expected snapshot version. Received: $version\")\n}\n"
  },
  {
    "path": "src/main/scala-2/LoadCompat.scala",
    "content": "package sbtrelease\n\nimport sbt.*\nimport sbt.Def.ScopedKey\nimport sbt.Keys.resolvedScoped\nimport sbt.Keys.streams\n\n// sbt.Load was made private in sbt 1.0\n// the core developers recommend copying the required methods: https://github.com/sbt/sbt/issues/3296#issuecomment-315218050\nobject LoadCompat {\n\n  import Compat.*\n\n  def transformSettings(\n    thisScope: Scope,\n    uri: URI,\n    rootProject: URI => String,\n    settings: Seq[Setting[?]]\n  ): Seq[Setting[?]] =\n    Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)\n  // Reevaluates settings after modifying them.  Does not recompile or reload any build components.\n  def reapply(newSettings: Seq[Setting[?]], structure: BuildStructure)(implicit\n    display: Show[ScopedKey[?]]\n  ): BuildStructure = {\n    val transformed = finalTransforms(newSettings)\n    val (compiledMap, newData) =\n      Def.makeWithCompiledMap(transformed)(using structure.delegates, structure.scopeLocal, display)\n    val newIndex = structureIndex(\n      newData,\n      transformed,\n      index => BuildUtil(structure.root, structure.units, index, newData),\n      structure.units\n    )\n    val newStreams = BuildStreams.mkStreams(structure.units, structure.root, newData)\n    new BuildStructure(\n      units = structure.units,\n      root = structure.root,\n      settings = transformed,\n      data = newData,\n      index = newIndex,\n      streams = newStreams,\n      delegates = structure.delegates,\n      scopeLocal = structure.scopeLocal,\n      compiledMap = compiledMap,\n    )\n  }\n\n  // map dependencies on the special tasks:\n  // 1. the scope of 'streams' is the same as the defining key and has the task axis set to the defining key\n  // 2. the defining key is stored on constructed tasks: used for error reporting among other things\n  // 3. resolvedScoped is replaced with the defining key as a value\n  // Note: this must be idempotent.\n  def finalTransforms(ss: Seq[Setting[?]]): Seq[Setting[?]] = {\n    def mapSpecial(to: ScopedKey[?]) = new (ScopedKey ~> ScopedKey) {\n      def apply[T](key: ScopedKey[T]) =\n        if (key.key == streams.key)\n          ScopedKey(Scope.fillTaskAxis(Scope.replaceThis(to.scope)(key.scope), to.key), key.key)\n        else key\n    }\n    def setDefining[T] = (key: ScopedKey[T], value: T) =>\n      value match {\n        case tk: Task[t] => setDefinitionKey(tk, key).asInstanceOf[T]\n        case ik: InputTask[t] => ik.mapTask(tk => setDefinitionKey(tk, key)).asInstanceOf[T]\n        case _ => value\n      }\n    def setResolved(defining: ScopedKey[?]) = new (ScopedKey ~> Option) {\n      def apply[T](key: ScopedKey[T]): Option[T] =\n        key.key match {\n          case resolvedScoped.key => Some(defining.asInstanceOf[T])\n          case _ => None\n        }\n    }\n    ss.map(s => s mapConstant setResolved(s.key) mapReferenced mapSpecial(s.key) mapInit setDefining)\n  }\n  def structureIndex(\n    data: Settings[Scope],\n    settings: Seq[Setting[?]],\n    extra: KeyIndex => BuildUtil[?],\n    projects: Map[URI, LoadedBuildUnit]\n  ): StructureIndex = {\n    val keys = Index.allKeys(settings)\n    val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)\n    val scopedKeys = keys ++ data.allKeys((s, k) => ScopedKey(s, k)).toVector\n    val projectsMap = projects.map { case (k, v) => k -> v.defined.keySet }\n    val configsMap: Map[String, Seq[Configuration]] =\n      projects.values.flatMap(bu => bu.defined map { case (k, v) => (k, v.configurations) }).toMap\n    val keyIndex = keyIndexApply(scopedKeys.toVector, projectsMap, configsMap)\n    val aggIndex = keyIndexAggregate(scopedKeys.toVector, extra(keyIndex), projectsMap, configsMap)\n    new StructureIndex(\n      Index.stringToKeyMap(attributeKeys),\n      Index.taskToKeyMap(data),\n      Index.triggers(data),\n      keyIndex,\n      aggIndex\n    )\n  }\n\n  def setDefinitionKey[T](tk: Task[T], key: ScopedKey[?]): Task[T] =\n    if (isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)\n\n  private def isDummy(t: Task[?]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false\n  private val Invisible = Int.MaxValue\n  private val isDummyTask = AttributeKey[Boolean](\n    \"is-dummy-task\",\n    \"Internal: used to identify dummy tasks.  sbt injects values for these tasks at the start of task execution.\",\n    Invisible\n  )\n}\n"
  },
  {
    "path": "src/main/scala-2/ReleasePluginCompat.scala",
    "content": "package sbtrelease\n\nimport sbt.*\nimport sbt.Keys.*\nimport sbtrelease.ReleasePlugin.autoImport.ReleaseStep\n\nprivate[sbtrelease] object ReleasePluginCompat {\n  def testTask: TaskKey[?] = sbt.Keys.test\n\n  val runClean: ReleaseStep = ReleaseStep(\n    action = { st =>\n      val extracted = Project.extract(st)\n      val ref = extracted.get(thisProjectRef)\n      extracted.runAggregated(ref / (Global / clean), st)\n    }\n  )\n\n  val moduleIds: Def.Initialize[Task[Seq[ModuleID]]] = Def.task(\n    (Runtime / managedClasspath).value.flatMap(_.get(moduleID.key))\n  )\n}\n"
  },
  {
    "path": "src/main/scala-3/LoadCompat.scala",
    "content": "package sbt\n\nimport sbt.internal.BuildStructure\n\nobject LoadCompat {\n\n  def transformSettings(\n    thisScope: Scope,\n    uri: URI,\n    rootProject: URI => String,\n    settings: Seq[Setting[?]]\n  ): Seq[Setting[?]] =\n    sbt.internal.Load.transformSettings(thisScope, uri, rootProject, settings)\n\n  def reapply(\n    newSettings: Seq[Setting[?]],\n    structure: BuildStructure\n  )(using display: Show[ScopedKey[?]]): BuildStructure =\n    sbt.internal.Load.reapply(newSettings, structure)\n}\n"
  },
  {
    "path": "src/main/scala-3/ReleasePluginCompat.scala",
    "content": "package sbtrelease\n\nimport sbt.*\nimport sbt.Keys.*\nimport sbtrelease.ReleasePlugin.autoImport.ReleaseStep\nimport sbtrelease.ReleasePlugin.autoImport.releaseStepCommandAndRemaining\n\nprivate[sbtrelease] object ReleasePluginCompat {\n  def testTask: TaskKey[?] = sbt.Keys.testFull\n\n  val runClean: ReleaseStep = releaseStepCommandAndRemaining(BasicCommandStrings.CleanFull)\n\n  val moduleIds: Def.Initialize[Task[Seq[ModuleID]]] = Def.task(\n    (Runtime / managedClasspath).value.flatMap(_.get(Keys.moduleIDStr)).map(Classpaths.moduleIdJsonKeyFormat.read)\n  )\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/build.sbt",
    "content": "import ReleaseTransformations._\nimport sbt.complete.DefaultParsers._\n\nname := \"command-line-version-numbers\"\n\npublishTo := Some(Resolver.file(\"file\", file(\".\")))\n\nreleaseProcess := Seq[ReleaseStep](\n  checkSnapshotDependencies,\n  inquireVersions,\n  runTest,\n  setReleaseVersion,\n  publishArtifacts,\n  setNextVersion\n)\n\nscalaVersion := \"2.13.18\"\n\nval checkContentsOfVersionSbt = inputKey[Unit](\"Check that the contents of version.sbt is as expected\")\nval parser = Space ~> StringBasic\n\ncheckContentsOfVersionSbt := {\n  val expected = parser.parsed\n  val versionFile = baseDirectory.value / \"version.sbt\"\n  assert(IO.read(versionFile).contains(expected), s\"does not contains ${expected} in ${versionFile}\")\n}\n\nInputKey[Unit](\"checkJarFile\") := {\n  val dir = file(\n    if (sbtVersion.value.startsWith(\"1\")) {\n      s\"target/scala-${scalaBinaryVersion.value}\"\n    } else {\n      s\"target/out/jvm/scala-${scalaVersion.value}/command-line-version-numbers\"\n    }\n  )\n\n  assert((dir / \"command-line-version-numbers_2.13-36.14.3.jar\").isFile)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/project/build.properties",
    "content": "# not setting the sbt version explicitly will use the version for the current sbt cross-build\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else {\n    addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n  }\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/src/main/scala/Hello.scala",
    "content": "object Main extends App {\n  println(\"hello\")\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/test",
    "content": "-> checkJarFile\n> 'release release-version 36.14.3 next-version 36.14.4-SNAPSHOT'\n> checkJarFile\n> checkContentsOfVersionSbt 36.14.4-SNAPSHOT\n"
  },
  {
    "path": "src/sbt-test/sbt-release/command-line-version-numbers/version.sbt",
    "content": "ThisBuild / version := \"36.14.4-SNAPSHOT\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/.gitignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/A.scala",
    "content": "class A\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/build.sbt",
    "content": "import sbtrelease.ReleaseStateTransformations._\n\nval Scala213 = \"2.13.18\"\nval Scala212 = \"2.12.21\"\n\nscalaVersion := Scala213\n\ncrossScalaVersions := Scala213 :: Scala212 :: Nil\n\nreleaseCrossBuild := false\n\nreleaseProcess := Seq(\n  checkSnapshotDependencies,\n  inquireVersions,\n  runClean,\n  runTest,\n  setReleaseVersion,\n  commitReleaseVersion,\n  tagRelease,\n  setNextVersion,\n  commitNextVersion\n)\n\nname := \"sbt-release-cross-test\"\n\nInputKey[Unit](\"checkTargetDir\") := {\n  import complete.DefaultParsers._\n  val args = spaceDelimited(\"<arg>\").parsed\n  val exists = args(1) match {\n    case \"exists\" =>\n      true\n    case \"not-exists\" =>\n      false\n  }\n  val dir = file {\n    if (sbtVersion.value.startsWith(\"1\")) {\n      val scalaBinaryV = args(0)\n      s\"target/scala-${scalaBinaryV}/classes\"\n    } else {\n      val scalaV = args(0) match {\n        case \"2.12\" =>\n          Scala212\n        case \"2.13\" =>\n          Scala213\n      }\n      s\"target/out/jvm/scala-${scalaV}/sbt-release-cross-test/classes\"\n    }\n  }\n\n  assert(dir.isDirectory == exists)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/test",
    "content": "$ exec git init .\n\n> update\n\n$ exec git add .\n$ exec git commit -m init\n\n> reload\n\n> release with-defaults\n> checkTargetDir 2.13 exists\n> checkTargetDir 2.12 not-exists\n\n> clean\n> checkTargetDir 2.12 not-exists\n> checkTargetDir 2.13 not-exists\n\n> release with-defaults cross\n> checkTargetDir 2.13 exists\n> checkTargetDir 2.12 exists\n"
  },
  {
    "path": "src/sbt-test/sbt-release/cross/version.sbt",
    "content": "ThisBuild / version := \"0.1.0-SNAPSHOT\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/exit-code/.gitignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/exit-code/build.sbt",
    "content": "// import sbtrelease.ReleaseStateTransformations._\nimport scala.sys.process.Process\n\n// credits for the test to: https://github.com/rossabaker/sbt-release-exit-code\n\npublishTo := Some(Resolver.file(\"file\", new File(Path.userHome.absolutePath + \"/.m2/repository\")))\n\nval failingTask = taskKey[Unit](\"A task that will fail\")\n\nfailingTask := { throw new IllegalStateException(\"Meh\") }\n"
  },
  {
    "path": "src/sbt-test/sbt-release/exit-code/foo.scala",
    "content": "This is NOT Scala\n"
  },
  {
    "path": "src/sbt-test/sbt-release/exit-code/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/exit-code/test",
    "content": "$ exec git init .\n\n> update\n\n$ exec git add .\n$ exec git commit -m init\n\n> reload\n\n> set releaseProcess := Seq(sbtrelease.ReleaseStateTransformations.runTest)\n-> release with-defaults\n\n> set releaseProcess := Seq(releaseStepCommand(\"show version\"))\n> release with-defaults\n\n> set releaseProcess := Seq(sbtrelease.ReleaseStateTransformations.runTest, releaseStepCommand(\"show version\"))\n-> release with-defaults\n\n> set releaseProcess := Seq(releaseStepCommandAndRemaining(\"show version\"))\n> release with-defaults\n\n> set releaseProcess := Seq(releaseStepCommandAndRemaining(\"failingTask\"))\n-> release with-defaults\n"
  },
  {
    "path": "src/sbt-test/sbt-release/fail-test/build.sbt",
    "content": "import ReleaseTransformations._\n\nval createFile: ReleaseStep = { (st: State) =>\n  IO.touch(file(\"file\"))\n  st\n}\n\nreleaseProcess := Seq[ReleaseStep](runTest, createFile)\n\nscalaVersion := \"2.13.18\"\n\nlibraryDependencies += \"org.scalatest\" %% \"scalatest-flatspec\" % \"3.2.19\" % \"test\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/fail-test/project/build.properties",
    "content": "# not setting the sbt version explicitly will use the version for the current sbt cross-build\n"
  },
  {
    "path": "src/sbt-test/sbt-release/fail-test/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/fail-test/src/test/scala/FailTest.scala",
    "content": "import org.scalatest.flatspec.AnyFlatSpec\n\nclass FailSpec extends AnyFlatSpec {\n  \"This test\" should \"fail\" in {\n    assert(false)\n  }\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/fail-test/test",
    "content": "$ delete file\n-> release\n$ absent file\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/.hgignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/B.scala",
    "content": "class B\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/build.sbt",
    "content": "import sbtrelease.ReleaseStateTransformations._\n\nscalaVersion := \"2.13.18\"\n\nreleaseProcess := Seq(\n  checkSnapshotDependencies,\n  inquireVersions,\n  runClean,\n  runTest,\n  setReleaseVersion,\n  commitReleaseVersion,\n  tagRelease,\n  setNextVersion,\n  commitNextVersion\n)\n\nname := \"sbt-release-test-mercurial\"\n\nInputKey[Unit](\"check\") := {\n  val f = file(\n    if (sbtVersion.value.startsWith(\"1\")) {\n      s\"target/scala-${scalaBinaryVersion.value}/classes/B.class\"\n    } else {\n      s\"target/out/jvm/scala-${scalaVersion.value}/sbt-release-test-mercurial/classes/B.class\"\n    }\n  )\n  assert(f.isFile)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/test",
    "content": "-> check\n$ exec hg init .\n\n> update\n\n$ exec hg add .\n$ exec hg commit -m init\n\n> reload\n\n> release with-defaults\n> check\n"
  },
  {
    "path": "src/sbt-test/sbt-release/mercurial/version.sbt",
    "content": "ThisBuild / version := \"0.1.0-SNAPSHOT\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/.gitignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/build.sbt",
    "content": "import sbtrelease.ReleaseStateTransformations._\n\nscalaVersion := \"2.13.18\"\n\nlibraryDependencies += \"org.scalatest\" %% \"scalatest-funspec\" % \"3.2.19\" % \"test\"\n\nreleaseProcess := Seq(\n  checkSnapshotDependencies,\n  inquireVersions,\n  runClean,\n  runTest,\n  setReleaseVersion,\n  commitReleaseVersion,\n  tagRelease,\n  setNextVersion,\n  commitNextVersion\n)\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/src/test/scala/Test.scala",
    "content": "package com.example\n\nclass Test extends org.scalatest.funspec.AnyFunSpec {\n  sys.error(\"should fail\")\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/test",
    "content": "$ exec git init .\n\n> update\n\n$ exec git add .\n$ exec git commit -m init\n\n> reload\n\n-> release with-defaults\n\n> release with-defaults skip-tests\n"
  },
  {
    "path": "src/sbt-test/sbt-release/skip-tests/version.sbt",
    "content": "ThisBuild / version := \"0.1.0-SNAPSHOT\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tag-default/.gitignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tag-default/build.sbt",
    "content": "import sbtrelease.ReleaseStateTransformations._\n\nscalaVersion := \"2.13.18\"\n\nreleaseProcess := Seq(\n  checkSnapshotDependencies,\n  inquireVersions,\n  runClean,\n  runTest,\n  setReleaseVersion,\n  commitReleaseVersion,\n  tagRelease,\n  setNextVersion,\n  commitNextVersion\n)\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tag-default/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tag-default/test",
    "content": "$ exec git init .\n\n> update\n\n$ exec git add .\n$ exec git commit -m init\n\n> reload\n\n#prerequisite for the use case covered\n$ exec git tag v0.1.0\n\n#fail since it try to overwrite tag v0.1.0 that is already present\n-> release with-defaults\n#fail since default action is to abort on tag already present\n-> release with-defaults default-tag-exists-answer a\n\n#succeed since we let overwrite existing tag\n> release with-defaults default-tag-exists-answer o\n#succeed since we do not overwrite existing tag\n> release with-defaults default-tag-exists-answer k\n#succeed since ask to tag with an explicit tag that is different from the previous one\n> release with-defaults default-tag-exists-answer v0.1.0-debug\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tag-default/version.sbt",
    "content": "ThisBuild / version := \"0.1.0-SNAPSHOT\"\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tasks-as-steps/build.sbt",
    "content": "organization := \"com.example\"\nversion := \"1.2.3\"\n\nlazy val myTask = taskKey[Unit](\"My task\")\nlazy val myAggregatedTask = taskKey[Unit](\"My aggregated task\")\nlazy val myInputTask = inputKey[Unit](\"My input task\")\nlazy val testOutputDir = settingKey[File](\"\")\n\nlazy val root: Project = (project in file(\".\"))\n  .aggregate(sub)\n  .settings(\n    myAggregatedTaskSetting,\n    testOutputDir := file(\"root-out\"),\n    myTask := {\n      IO.write(testOutputDir.value / \"mytask\", \"ran\")\n    },\n    myInputTask := {\n      val file = Def.spaceDelimited().parsed.headOption.getOrElse(\"myinputtask\")\n      IO.write(testOutputDir.value / file, \"ran\")\n    },\n    commands ++= Seq(myCommand, myInputCommand, myCommand2, myInputCommand2),\n    releaseProcess := Seq[ReleaseStep](\n      releaseStepTask(myTask),\n      releaseStepTaskAggregated(root / myAggregatedTask),\n      releaseStepInputTask(myInputTask),\n      releaseStepInputTask(myInputTask, \" custominputtask\"),\n      releaseStepCommand(myCommand),\n      releaseStepCommand(myInputCommand),\n      releaseStepCommand(myInputCommand, \" custominputcommand\"),\n      releaseStepCommand(\"mycommand2\"),\n      releaseStepCommand(\"myinputcommand2\"),\n      releaseStepCommand(\"myinputcommand2 custominputcommand2\")\n    ),\n  )\n\nlazy val sub = (project in file(\"sub\")).settings(\n  myAggregatedTaskSetting,\n  testOutputDir := file(\"sub-out\"),\n)\n\ndef myAggregatedTaskSetting = myAggregatedTask := {\n  IO.write(testOutputDir.value / \"myaggregatedtask\", \"ran\")\n}\n\nlazy val myCommand = Command.command(\"mycommand\") { state =>\n  IO.write(Project.extract(state).get(testOutputDir) / \"mycommand\", \"ran\")\n  state\n}\nlazy val myInputCommand = Command.make(\"myinputcommand\") { state =>\n  Def.spaceDelimited().map { args => () =>\n    val file = args.headOption.getOrElse(\"myinputcommand\")\n    IO.write(Project.extract(state).get(testOutputDir) / file, \"ran\")\n    state\n  }\n}\nlazy val myCommand2 = Command.command(\"mycommand2\") { state =>\n  IO.write(Project.extract(state).get(testOutputDir) / \"mycommand2\", \"ran\")\n  state\n}\nlazy val myInputCommand2 = Command.make(\"myinputcommand2\") { state =>\n  Def.spaceDelimited().map { args => () =>\n    val file = args.headOption.getOrElse(\"myinputcommand2\")\n    IO.write(Project.extract(state).get(testOutputDir) / file, \"ran\")\n    state\n  }\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tasks-as-steps/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/tasks-as-steps/test",
    "content": "-$ exists root-out\n-$ exists sub-out\n\n> release\n\n$ exists root-out/mytask\n$ exists root-out/myaggregatedtask\n$ exists sub-out/myaggregatedtask\n$ exists root-out/myinputtask\n$ exists root-out/custominputtask\n$ exists root-out/mycommand\n$ exists root-out/myinputcommand\n$ exists root-out/custominputcommand\n$ exists root-out/mycommand2\n$ exists root-out/myinputcommand2\n$ exists root-out/custominputcommand2\n"
  },
  {
    "path": "src/sbt-test/sbt-release/with-defaults/.gitignore",
    "content": "target\nglobal/\n"
  },
  {
    "path": "src/sbt-test/sbt-release/with-defaults/build.sbt",
    "content": "import sbt.complete.DefaultParsers._\nimport sbtrelease.ReleaseStateTransformations._\n\nreleaseVersionFile := file(\"version.sbt\")\n\nreleaseProcess := Seq(\n  checkSnapshotDependencies,\n  inquireVersions,\n  runClean,\n  runTest,\n  setReleaseVersion,\n  commitReleaseVersion,\n  tagRelease,\n  setNextVersion,\n  commitNextVersion\n)\n\nval checkContentsOfVersionSbt = inputKey[Unit](\"Check that the contents of version.sbt is as expected\")\nval parser = Space ~> StringBasic\n\ncheckContentsOfVersionSbt := {\n  val expected = parser.parsed\n  val versionFile = ((baseDirectory).value) / \"version.sbt\"\n  assert(IO.read(versionFile).contains(expected), s\"does not contains ${expected} in ${versionFile}\")\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/with-defaults/project/build.sbt",
    "content": "{\n  val pluginVersion = System.getProperty(\"plugin.version\")\n  if (pluginVersion == null)\n    throw new RuntimeException(\"\"\"|The system property 'plugin.version' is not defined.\n                                  |Specify this property using the scriptedLaunchOpts -D.\"\"\".stripMargin)\n  else addSbtPlugin(\"com.github.sbt\" % \"sbt-release\" % pluginVersion)\n}\n"
  },
  {
    "path": "src/sbt-test/sbt-release/with-defaults/test",
    "content": "# Test Suite Preparation\n    $ exec git init .\n    > update\n    $ exec git add .\n    $ exec git commit -m init\n    > reload\n\n# SCENARIO: When no release versions are specified in the release command\n    # TEST: Should fail to release if \"with-defaults\" is not specified\n        -> release\n\n    # TEST: Should succeed if \"with-defaults\" is specified\n        > release with-defaults\n\n# SCENARIO: When default bumping strategy is used\n    # Test Scenario Preparation\n        > 'release release-version 0.9.9 next-version 1.0.0-RC1-SNAPSHOT'\n        > reload\n        > checkContentsOfVersionSbt 1.0.0-RC1-SNAPSHOT\n\n    # TEST: Snapshot version should be correctly set\n        > release with-defaults\n        > checkContentsOfVersionSbt 1.0.0-RC2-SNAPSHOT\n\n    # TEST: Release version should be correctly set\n        $ exec git reset --hard HEAD~1\n        > reload\n        > checkContentsOfVersionSbt 1.0.0-RC1\n\n# SCENARIO: When NextStable bumping strategy is used\n    # TEST: Snapshot version should be correctly set\n        $ exec git reset --hard HEAD~1\n        > set releaseVersionBump := sbtrelease.Version.Bump.NextStable\n        > release with-defaults\n        > checkContentsOfVersionSbt 1.0.1-SNAPSHOT\n\n    # TEST: Release version should be correctly set\n        $ exec git reset --hard HEAD~1\n        > reload\n        > checkContentsOfVersionSbt 1.0.0\n\n"
  },
  {
    "path": "src/sbt-test/sbt-release/with-defaults/version.sbt",
    "content": "ThisBuild / version := \"0.1.0-SNAPSHOT\"\n"
  },
  {
    "path": "src/test/scala/VersionSpec.scala",
    "content": "package sbtrelease\n\nimport org.specs2.matcher.MatchResult\nimport org.specs2.mutable.Specification\n\nobject VersionSpec extends Specification {\n\n  def version(v: String) = Version(v) match {\n    case Some(parsed) => parsed\n    case None => sys.error(\"Can't parse version \" + v)\n  }\n  \"Next Version bumping\" should {\n    def testBumpNext(input: String, expectedOutput: String): MatchResult[Any] =\n      version(input).bumpNext.unapply must_== expectedOutput\n\n    def testBumpNextStable(input: String, expectedOutput: String): MatchResult[Any] =\n      version(input).bumpNextStable.unapply must_== expectedOutput\n\n    def testBothBumpNextStrategies(input: String, expectedOutput: String): MatchResult[Any] = {\n      testBumpNext(input, expectedOutput)\n      testBumpNextStable(input, expectedOutput)\n    }\n\n    \"bump the major version if there's only a major version\" in {\n      testBothBumpNextStrategies(\"1\", \"2\")\n    }\n    \"bump the minor version if there's only a minor version\" in {\n      testBothBumpNextStrategies(\"1.2\", \"1.3\")\n    }\n    \"bump the bugfix version if there's only a bugfix version\" in {\n      testBothBumpNextStrategies(\"1.2.3\", \"1.2.4\")\n    }\n    \"bump the nano version if there's only a nano version\" in {\n      testBothBumpNextStrategies(\"1.2.3.4\", \"1.2.3.5\")\n    }\n    \"drop the qualifier if it's a pre release and there is no version number at the end\" in {\n      testBothBumpNextStrategies(\"1-rc\", \"1\")\n      testBothBumpNextStrategies(\"1.0-rc\", \"1.0\")\n      testBothBumpNextStrategies(\"1.0.0-rc\", \"1.0.0\")\n      testBothBumpNextStrategies(\"1.0.0.0-rc\", \"1.0.0.0\")\n      testBothBumpNextStrategies(\"1-beta\", \"1\")\n      testBothBumpNextStrategies(\"1-alpha\", \"1\")\n    }\n    \"when the qualifier includes a pre release with a version number at the end\" >> {\n      \"and Next is the bumping strategy\" >> {\n        \"should bump the qualifier\" in {\n          testBumpNext(\"1-rc1\", \"1-rc2\")\n          testBumpNext(\"1.2-rc1\", \"1.2-rc2\")\n          testBumpNext(\"1.2.3-rc1\", \"1.2.3-rc2\")\n          testBumpNext(\"1-RC1\", \"1-RC2\")\n          testBumpNext(\"1-M1\", \"1-M2\")\n          testBumpNext(\"1-rc-1\", \"1-rc-2\")\n          testBumpNext(\"1-rc.1\", \"1-rc.2\")\n          testBumpNext(\"1-beta-1\", \"1-beta-2\")\n          testBumpNext(\"1-beta.1\", \"1-beta.2\")\n        }\n      }\n      \"and NextStable is the bumping strategy\" >> {\n        \"should remove the qualifier\" in {\n          testBumpNextStable(\"1-rc1\", \"1\")\n          testBumpNextStable(\"1.2-rc1\", \"1.2\")\n          testBumpNextStable(\"1.2.3-rc1\", \"1.2.3\")\n          testBumpNextStable(\"1-RC1\", \"1\")\n          testBumpNextStable(\"1-M1\", \"1\")\n          testBumpNextStable(\"1-rc-1\", \"1\")\n          testBumpNextStable(\"1-rc.1\", \"1\")\n          testBumpNextStable(\"1-beta-1\", \"1\")\n          testBumpNextStable(\"1-beta.1\", \"1\")\n        }\n      }\n    }\n    \"never drop the qualifier if it's a final release\" >> {\n      \"when release is major\" in {\n        testBothBumpNextStrategies(\"1-Final\", \"2-Final\")\n      }\n      \"when release is minor\" in {\n        testBothBumpNextStrategies(\"1.2-Final\", \"1.3-Final\")\n      }\n      \"when release is subversion\" in {\n        testBothBumpNextStrategies(\"1.2.3-Final\", \"1.2.4-Final\")\n      }\n      \"when release is nano\" in {\n        testBothBumpNextStrategies(\"1.2.3.4-Final\", \"1.2.3.5-Final\")\n      }\n    }\n  }\n\n  \"Major Version bumping\" should {\n    def bumpMajor(v: String) = version(v).bumpMajor.unapply\n\n    \"bump the major version and reset other versions\" in {\n      bumpMajor(\"1.2.3.4.5\") must_== \"2.0.0.0.0\"\n    }\n    \"not drop the qualifier\" in {\n      bumpMajor(\"1.2.3.4.5-alpha\") must_== \"2.0.0.0.0-alpha\"\n    }\n  }\n\n  \"Minor Version bumping\" should {\n    def bumpMinor(v: String) = version(v).bumpMinor.unapply\n\n    \"bump the minor version\" in {\n      bumpMinor(\"1.2\") must_== \"1.3\"\n    }\n    \"bump the minor version and reset other subversions\" in {\n      bumpMinor(\"1.2.3.4.5\") must_== \"1.3.0.0.0\"\n    }\n    \"not bump the major version when no minor version\" in {\n      bumpMinor(\"1\") must_== \"1\"\n    }\n    \"not drop the qualifier\" in {\n      bumpMinor(\"1.2.3.4.5-alpha\") must_== \"1.3.0.0.0-alpha\"\n    }\n  }\n\n  \"Subversion bumping\" should {\n    def bumpSubversion(v: String)(i: Int) = version(v).maybeBumpSubversion(i).unapply\n\n    \"bump the subversion\" in {\n      bumpSubversion(\"1.2\")(0) must_== \"1.3\"\n    }\n    \"bump the subversion and reset lower subversions\" in {\n      bumpSubversion(\"1.2.3.4.5\")(0) must_== \"1.3.0.0.0\"\n    }\n    \"not change anything with an invalid subversion index\" in {\n      bumpSubversion(\"1.2-beta\")(1) must_== \"1.2-beta\"\n    }\n    \"not drop the qualifier\" in {\n      bumpSubversion(\"1.2.3.4.5-alpha\")(2) must_== \"1.2.3.5.0-alpha\"\n    }\n  }\n  \"#isSnapshot\" should {\n    \"return true when -SNAPSHOT is appended with another qualifier\" in {\n      version(\"1.0.0-RC1-SNAPSHOT\").isSnapshot must_== true\n    }\n    \"return false when -SNAPSHOT is not appended but another qualifier exists\" in {\n      version(\"1.0.0-RC1\").isSnapshot must_== false\n    }\n    \"return false when neither -SNAPSHOT nor qualifier are appended\" in {\n      version(\"1.0.0\").isSnapshot must_== false\n    }\n  }\n  \"#asSnapshot\" should {\n    def snapshot(v: String) = version(v).asSnapshot.unapply\n    \"include qualifier if it exists\" in {\n      snapshot(\"1.0.0-RC1\") must_== \"1.0.0-RC1-SNAPSHOT\"\n    }\n    \"have no qualifier if none exists\" in {\n      snapshot(\"1.0.0\") must_== \"1.0.0-SNAPSHOT\"\n    }\n  }\n  \"#withoutSnapshot\" should {\n    \"remove the snapshot normally\" in {\n      version(\"1.0.0-SNAPSHOT\").withoutSnapshot.unapply must_== \"1.0.0\"\n    }\n    \"remove the snapshot without removing the qualifier\" in {\n      version(\"1.0.0-RC1-SNAPSHOT\").withoutSnapshot.unapply must_== \"1.0.0-RC1\"\n    }\n  }\n}\n"
  }
]