{"id":24534414,"url":"https://github.com/agboom/sbt-steps","last_synced_at":"2025-08-31T17:44:39.479Z","repository":{"id":267776513,"uuid":"900360720","full_name":"agboom/sbt-steps","owner":"agboom","description":"Consistently configure task steps across sbt projects. Run locally or in CI with a single command. Generate summaries in HTML.","archived":false,"fork":false,"pushed_at":"2025-07-21T19:57:23.000Z","size":216,"stargazers_count":21,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-21T21:35:36.879Z","etag":null,"topics":["cd","ci","plugin","sbt","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agboom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-08T15:28:07.000Z","updated_at":"2025-07-16T11:37:58.000Z","dependencies_parsed_at":"2024-12-12T11:35:52.898Z","dependency_job_id":"1f1fa309-0e73-4d9d-ac07-7663fd2b12dc","html_url":"https://github.com/agboom/sbt-steps","commit_stats":null,"previous_names":["agboom/sbt-steps"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/agboom/sbt-steps","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agboom%2Fsbt-steps","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agboom%2Fsbt-steps/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agboom%2Fsbt-steps/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agboom%2Fsbt-steps/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agboom","download_url":"https://codeload.github.com/agboom/sbt-steps/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agboom%2Fsbt-steps/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273017549,"owners_count":25031594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cd","ci","plugin","sbt","scala"],"created_at":"2025-01-22T11:17:08.491Z","updated_at":"2025-08-31T17:44:39.417Z","avatar_url":"https://github.com/agboom.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sbt steps\n\nConfigure, run and share a list of tasks and commands to run in your sbt projects. Run the\nsteps from the sbt shell with a single command. Generate reports in HTML or in ASCII\nformat. Use in GitHub Actions or any other CI environment.\n\n## Demo\n\nHere are two report examples after running `ci` using `CIStepsPlugin`:\n\n\u003ctable align=left\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eHTML report\u003c/b\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr valign=top\u003e\n    \u003ctd height=526\u003e\n      \u003cp\u003e\u003ccode\u003e\u003cb\u003esbt:root\u003e ci/stepsStatusReport -\u003c/b\u003e\u003c/code\u003e\u003c/p\u003e\n      \u003csup\u003e\n        \u003ctable\u003e\u003ctr height=30\u003e\u003ctd colspan=5 width=400\u003e(\u003cb\u003e\u003ccode\u003e+Test / test\u003c/code\u003e\u003c/b\u003e) Cross test\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"succeeded\" width=40\u003e:white_check_mark:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+bar / Test / test\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"succeeded\" width=40\u003e:white_check_mark:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+foo / Test / test\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"succeeded\" width=40\u003e:white_check_mark:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+root / Test / test\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd colspan=5 width=400\u003e(\u003cb\u003e\u003ccode\u003e+publishLocal\u003c/code\u003e\u003c/b\u003e) Cross publish\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"succeeded\" width=40\u003e:white_check_mark:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+bar / publishLocal\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd width=40\u003e:green_circle:\u003c/td\u003e\u003ctd\u003eSuccessfully published bar \u003ccode\u003e0.1.0-SNAPSHOT\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"succeeded\" width=40\u003e:white_check_mark:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+foo / publishLocal\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd width=40\u003e:green_circle:\u003c/td\u003e\u003ctd\u003eSuccessfully published foo \u003ccode\u003e0.1.0-SNAPSHOT\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"skipped\" width=40\u003e:white_large_square:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003e+root / publishLocal\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd width=40\u003e:white_circle:\u003c/td\u003e\u003ctd\u003e\u003ccode\u003epublishLocal / skip\u003c/code\u003e is set to true\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd colspan=5 width=400\u003e(\u003cb\u003e\u003ccode\u003eCompile / unidoc\u003c/code\u003e\u003c/b\u003e) Generate unified Scaladoc\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd title=\"failed\" width=40\u003e:red_square:\u003c/td\u003e\u003ctd colspan=2\u003e\u003ccode\u003eroot / Compile / unidoc\u003c/code\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr height=30\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd width=40\u003e:x:\u003c/td\u003e\u003ctd\u003e(\u003ccode\u003eScalaunidoc / doc\u003c/code\u003e) Scaladoc generation failed\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e  \n    \u003c/sup\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\u003ctable align=left\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eASCII report\u003c/b\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr valign=top\u003e\n    \u003ctd height=526\u003e\n      \u003cp\u003e\u003ccode\u003e\u003cb\u003esbt:root\u003e ci/stepsTree --status\u003c/b\u003e\u003c/code\u003e\u003c/p\u003e\n      \u003cimg alt=\"steps tree\" src=\"docs/stepstree.png\" width=370 /\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\u003cbr clear=left /\u003e\n\n\u003e [!NOTE]\n\u003e See the [demo scripted test] for the `build.sbt` of these examples. Run `sbt scripted\n\u003e sbt-steps/demo` to see the full demo.\n\n[demo scripted test]: src/sbt-test/sbt-steps/demo/build.sbt\n\n## Getting started\n\nThere are two ways to use this plugin: enable `CIStepsPlugin` or create your own\n`StepsPlugin`. For first use it's recommended to start with `CIStepsPlugin`.\n\n### Enable `CIStepsPlugin`\n\nTo quickly get started, first add the plugin to your `project/plugins.sbt`:\n\n```sbt\naddSbtPlugin(\"io.github.agboom\" % \"sbt-steps\" % \"\u003cversion\u003e\")\n```\n\nThen enable the `CIStepsPlugin` in your `build.sbt`:\n\n```sbt\nlazy val myProject = (project in file(\".\"))\n  .enablePlugins(CIStepsPlugin)\n  .settings(\n    name := \"my-project\",\n  )\n```\n\nHead over to [Usage](#usage).\n\n### Create your own `StepsPlugin`\n\nSee the [plugin development docs][create-steps-plugin].\n\n[create-steps-plugin]: docs/plugin-development/#create-your-own-steps-plugin\n\n## Usage\n\nThe `ci` task is central to `CIStepsPlugin`. It runs the configured `ci/steps` for all\nsubprojects in the build definition. All settings and tasks from `StepsPlugin`, like\n`stepsTree` and `stepsStatusReport`, are scoped in this task.\n\n### From the sbt shell\n\nRun `ci` from an sbt shell (or `sbt ci` for batch mode). By default `+test` and `+publish`\nare run. For the example project above, this results in the following steps sequence:\n\n```\nsbt:my-project\u003e ci/stepsTree\n[info] task: +Test / test\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +myProject / Test / test\n[info] \n[info] task: +publish\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +myProject / publish\n```\n\nAfter `ci` has completed, run `ci/stepsTree --status` to print the steps tree with the\ncompleted status. A status optionally has a message, such as:\n```\n[info] +- status: succeeded\n[info]  +- Successfully published my-project 0.1.0-SNAPSHOT.\n```\n\nA detailed HTML report is created during `ci` that you can use as a job summary in [GitHub\nActions](#use-with-github-actions) or any CI that accepts Markdown or HTML. By default the\nreport is written to `target/ci-status.html`. At any time you can also write a report to\nfile or stdout with `ci/stepsStatusReport`.\n\nTo include skipped steps and more information in the report, pass the `--verbose` or `-v`\nflag, e.g. `ci -v` or `ci/stepsTree -v`.\n\nFor a complete list of available tasks and settings, execute `help ^steps.*` from your sbt\nshell.\n\n\u003e [!IMPORTANT]\n\u003e Currently, a steps task like `ci` cannot be run for a specific subproject (e.g. don't run\n\u003e `sbt core/ci`). It uses all projects in the build regardless.\n\n\u003e [!NOTE] \n\u003e In the commands above we have used `ci` as task scope, which is part of `CIStepsPlugin`.\n\u003e If you create your own `StepsPlugin` `ci` is replaced by a different task created for\n\u003e that steps plugin (e.g. `deploy` or `release`). All configurations, runs and reports are\n\u003e scoped to this task. This enables having multiple steps configurations for different use\n\u003e cases simultaneously. Read the [plugin developer documentation][create-steps-plugin] for\n\u003e more information.\n\n### With GitHub Actions\n\nBelow is an example workflow for GitHub Actions:\n\n```yml\nname: CI\non:\n  pull_request:\n  push:\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: 17\n          cache: sbt\n      - uses: sbt/setup-sbt@v1\n      - name: Build\n        shell: bash\n        run: sbt ci\n      - name: Submit summary\n        # try to create a summary on success or failure, but not on cancelled\n        if: ${{ !cancelled() }}\n        shell: bash\n        run: |\n          status_file=./target/ci-status.html\n          if [ -f $status_file ]; then\n            echo \"# CI summary\" \u003e\u003e $GITHUB_STEP_SUMMARY\n            cat $status_file \u003e\u003e $GITHUB_STEP_SUMMARY\n            echo \u003e\u003e $GITHUB_STEP_SUMMARY\n            echo \u003e\u003e $GITHUB_STEP_SUMMARY\n          else\n            echo \"Cannot create CI summary, because $status_file does not exist.\"\n          fi\n```\n\nThe workflow looks like any other sbt workflow, except instead of invoking sbt commands\ndirectly, `sbt ci` is used. At runtime the steps tree is printed. In addition the CI steps\nreport is added to the [job summary]. If you have additional `StepsPlugin`s, you can\nappend their reports the same way.\n\n[job summary]: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#adding-a-job-summary\n\n## Configuration\n\nThe configuration examples below use the `ci` task scope, but please note that `ci` can be\nreplaced by any other `StepsPlugin` task.\n\n### Customize steps\n\nBy default, `CIStepsPlugin` sets `ci/steps` to run `+test` and `+publish`. This can be\ncustomized as follows:\n\n```sbt\nlazy val foo = (project in file(\"foo\"))\n  .enablePlugins(ScalaUnidocPlugin)\n  .settings(\n    ci / steps := Seq(\n      Test / test,\n      publish,\n      Compile / unidoc,\n    )\n  )\nlazy val bar = (project in file(\".\"))\n  .settings(\n    ci / steps := Seq(\n      Test / test,\n      publish,\n    )\n  )\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:bar \u003e ci/stepsTree\n[info] task: Test / test\n[info]   +-project steps:\n[info]     +-task: foo / Test / test\n[info]     +-task: bar / Test / test\n[info] \n[info] task: publish\n[info]   +-project steps:\n[info]     +-task: foo / publish\n[info]     +-task: bar / publish\n[info] \n[info] task: Compile / unidoc\n[info]   +-project steps:\n[info]     +-task: foo / Compile / unidoc\n```\n\n\u003e [!IMPORTANT]\n\u003e Task steps do not run for project aggregates configured by `aggregate()`. Instead, they\n\u003e are run for the subproject they are configured on. To share steps between subprojects,\n\u003e see the next section. \n\n\u003e [!TIP]\n\u003e Because the `steps` setting is a list, they can also be appended (`ci/steps += Compile /\n\u003e unidoc`) or removed (`ci/steps -= publish`). Do always check the resulting\n\u003e `ci/stepsTree` after customizing.\n\n\u003e [!NOTE]\n\u003e Notice that the tree shows the steps in a different grouping than configured in the\n\u003e build definition. The tree shows the order in which the steps will actually be run.\n\u003e Project steps with the same step are grouped together, while the configured order is\n\u003e kept in tact. This mimics sbt aggregation and prevents steps to be run in an unexpected\n\u003e order. For example, in this build all test must succeed before continuing to publish. If\n\u003e you prefer per project grouping, use the [`stepsGrouping`\n\u003e setting](#change-the-steps-grouping).\n\n### Share steps between subprojects\n\nLike any sbt setting, use the `ThisBuild` scope (or use a shared setting) to share steps\nbetween subprojects. For example:\n\n```sbt\nThisBuild / ci / steps = Seq(\n  Test / test,\n  publish,\n)\n\nlazy val foo = (project in file(\"foo\"))\n\nlazy val root = (project in file(\".\"))\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:root\u003e ci/stepsTree\n[info] task: Test / test\n[info]   +-project steps:\n[info]     +-task: foo / Test / test\n[info]     +-task: root / Test / test\n[info] \n[info] task: publish\n[info]   +-project steps:\n[info]     +-task: foo / publish\n[info]     +-task: root / publish\n```\n\n\u003e [!TIP]\n\u003e To skip a step for a particular project, use [`skip := true`](#skip-a-project-step) or\n\u003e [project filters](#set-project-filters-on-a-step)\n\n\u003e [!TIP]\n\u003e Sharing steps across builds is possible by [creating a plugin].\n\n[creating a plugin]: docs/plugin-development/#share-steps-across-builds-with-a-plugin\n\n\u003e [!IMPORTANT]\n\u003e If you use an external plugin that declares shared `steps` on the project level, please\n\u003e note that the `ThisBuild` scope in your `build.sbt` will not work, because once enabled\n\u003e it's overwritten by the plugin setting. In that case a shared setting is needed: \n```sbt\nlazy val sharedSettings = Def.settings(\n  ci / steps := Seq(\n    Test / test,\n    publish,\n  ),\n)\n\nlazy val foo = (project in file(\"foo\"))\n  .settings(sharedSettings)\n```\n\n### Declare steps for cross Scala versions\n\nAny (input) task step can be run for the configured `crossScalaVersions` using the `+`\nprefix. For example:\n\n```sbt\ninThisBuild(Seq(\n  ci / steps := Seq(\n    +(Test / test),\n    +publish,\n  ),\n  crossScalaVersions := Seq(\"3.3.4\", \"2.13.15\"),\n))\n\nlazy val foo = (project in file(\"foo\"))\n\nlazy val root = (project in file(\".\"))\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:root\u003e ci/stepsTree\n[info] task: Test / +test\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / Test / test\n[info]     +-task: +root / Test / test\n[info] \n[info] task: +publish\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / publish\n[info]     +-task: +root / publish\n```\n\n\u003e [!NOTE]\n\u003e Cross build steps can be safely declared without setting `crossScalaVersions`, because\n\u003e `scalaVersion` is used by default.\n\n\u003e [!IMPORTANT] \n\u003e For performance reasons, cross build steps mimic sbt cross build aggregation. This means\n\u003e that for each cross Scala version, all project tasks are run before going to the next\n\u003e cross Scala version. In the example above, this results the following order: `++ 3.3.4;\n\u003e foo/test; root/test; ++ 2.13.15; foo/test; root/test`. This behavior may lead to\n\u003e incomplete steps when its subsequent step has failed, which can be confusing. Even\n\u003e though this is the intended behavior, we may need to look into making this more clear.\n\u003e [This scripted test] can be used to test the performance limits.\n\n[This scripted test]: src/sbt-test/sbt-steps-by-step/cross-source\n\n### Use input task steps\n\nThe examples above only use task steps that run a [`Task`][sbt-task]. An input task step\nis similar, except that it parses an input string before running the\n[`InputTask`][sbt-input-task]. For example:\n\n```sbt\nlazy val foo = (project in file(\".\"))\n  .settings(\n    ci / steps := Seq(\n      (Test / testOnly) withInput \"*MySpec\",\n    )\n  )\n```\n\nIf the input fails to parse, an error is shown in the step status when running `ci`.\n\n\u003e [!NOTE]\n\u003e Without invoking `withInput` the input is left empty. This will succeed only if the\n\u003e task's parser supports empty input. A leading space is not needed for inputs, because\n\u003e it's automatically added.\n\n### Use command steps\n\nA command step runs an sbt [command][sbt-command]. Command steps are different from task\nsteps, because they behave exactly like commands executed from the sbt console. This means\nthat, unlike task steps, commands that run a task are also executed in project aggregates.\nFor this reason it's recommended to use command step only if there's no alternative task\nstep. To declare steps for multiple subprojects, use [`ThisBuild` or a shared\nsetting](#share-steps-between-subprojects).\n\nHowever, command steps can be useful for running command aliases or actual commands. For\nexample:\n\n```sbt\nlazy val foo = (project in file(\".\"))\n  .enablePlugins(ScoverageSbtPlugin)\n  .settings(\n    ci / steps := Seq(\n      \"coverageOn\",\n      (Test / test),\n      \"coverageOff\",\n    )\n  )\n```\n\n\u003e [!TIP]\n\u003e Commands work well together with [project filters](#set-project-filters-on-a-step).\n\n[sbt-task]: https://www.scala-sbt.org/1.x/docs/Tasks.html\n[sbt-input-task]: https://www.scala-sbt.org/1.x/docs/Input-Tasks.html\n[sbt-command]: https://www.scala-sbt.org/1.x/docs/Commands.html\n\n### Skip a project step\n\nTo skip a task step in a particular project, set `skip := true` like you would for any sbt\ntask. For example:\n\n```sbt\ninThisBuild(Seq(\n  ci / steps := Seq(\n    +(Test / test),\n    +publish,\n  ),\n  crossScalaVersions := Seq(\"3.3.4\", \"2.13.15\"),\n))\n\nlazy val foo = (project in file(\"foo\"))\n\nlazy val root = (project in file(\".\"))\n  .settings(\n    publish / skip := true,\n  )\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:root\u003e ci/stepsTree --verbose\n[info] task: +Test / test\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / Test / test\n[info]     +-task: +root / Test / test\n[info] \n[info] task: +publish\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / publish\n[info]     +-task: +root / publish\n[info]       +-skipped: root / publish / skip is set to true\n```\n\n\u003e [!WARNING]\n\u003e To skip a command step it's better to use [project\n\u003e filters](#set-project-filters-on-a-step). The `skip := true` setting may work if the\n\u003e command refers to a task, but this behavior is untested in sbt-steps.\n\n### Set project filters on a step\n\nProject filters allow you to declare a (shared) step, but only run it for a specific\nsubproject. Use the `forProject` combinator to achieve this. The default is `ThisProject`.\nProject filters are especially useful for command steps, as they complement the `skip :=\ntrue` feature. For example, the following steps will run the `coverageOn` and\n`coverageOff` command for the root project only.\n\n```sbt\nThisBuild / ci / steps := Seq(\n  \"coverageOn\" forProject LocalRootProject,\n  +(Test / test),\n  \"coverageOff\" forProject LocalRootProject,\n)\n\nlazy val foo = (project in file(\"foo\"))\n  .enablePlugins(ScoverageSbtPlugin)\n\nlazy val root = (project in file(\".\"))\n  .enablePlugins(ScoverageSbtPlugin)\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:root\u003e ci/stepsTree\n[info] command: coverageOn\n[info]   +-project filter: LocalRootProject\n[info]   +-project steps:\n[info]     +-command: project root; coverageOn\n[info] \n[info] task: +Test / test\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / Test / test\n[info]     +-task: +root / Test / test\n[info] \n[info] command: coverageOff\n[info]   +-project filter: LocalRootProject\n[info]   +-project steps:\n[info]     +-command: project root; coverageOff\n```\n\nNote the absence of `coverageOn` and `coverageOff` for project foo, because of the project filter.\n\n### Declare a step to run only once\n\nIn some cases you want a step to be run only once in the entire build. To achieve this,\nuse the `.once` combinator. Its dual is `.whenever`. This is slightly different from a\n[project filter](#set-project-filters-on-a-step), because the task will be run at the\nfirst opportunity, instead of a specific project. For example, the following steps will\nrun the `authenticate` task only once:\n\n```sbt\nThisBuild / ci / steps := Seq(\n  authenticate.once,\n  +publish,\n)\n\nlazy val foo = (project in file(\"foo\"))\n  .settings(\n    name := \"foo\",\n  )\n\nlazy val root = (project in file(\".\"))\n  .settings(\n    name := \"root\",\n  )\n```\n\nThis configuration will result in the following steps sequence:\n\n```\nsbt:root\u003e ci/stepsTree\n[info] task: authenticate\n[info]   +-run once: true\n[info]   +-project steps:\n[info]     +-task: foo / authenticate\n[info] \n[info] task: +publish\n[info]   +-cross build: true\n[info]   +-project steps:\n[info]     +-task: +foo / publish\n[info]     +-task: +root / publish\n```\n\nNote the added `+-run once: true` line in the steps tree.\n\n### Continue to next step on error\n\nIf you have a step that is not critical to the complete success or want to continue in any\ncase, use the `.continueOnError` combinator on a step. Its dual is `.abortOnError`, which\nis the default. For example:\n\n```sbt\nlazy val myLibrary = (project in file(\".\"))\n  .settings(\n    ci / steps := Seq(\n      +(Test / test).continueOnError,\n      +publish,\n    )\n  )\n```\n\nThe settings above will run `+test` for and proceed to `+publish` regardless of its\noutcome.\n\n\u003e [!IMPORTANT]\n\u003e A failed step will always result in a failure status of the step and the entire run,\n\u003e whether `.continueOnError` is enabled or not.\n\n## Advanced configuration\n\n### Change the steps grouping\n\nBy default, steps are grouped by step to mimic sbt aggregation as explained in [this\nsection](#customize-steps). While this is a sensible default, there are use cases to keep\nthe by-project grouping. The grouping can be changed with the `stepsGrouping` setting:\n\n```sbt\nThisBuild / ci / steps := Seq(\n  +(Test / test),\n  +publish,\n)\n\nGlobal / ci / stepsGrouping := StepsGrouping.ByProject\n\nlazy val foo = (project in file(\"foo\"))\n  .settings(\n    name := \"foo\",\n  )\n\nlazy val root = (project in file(\".\"))\n  .settings(\n    name := \"root\",\n  )\n```\n\nThis configuration will result in the following steps sequence:\n\n```\n\u003e ci/stepsTree\n[info] project: root\n[info]   +-task: +Test / test\n[info]     +-cross build: true\n[info]   +-task: +publish\n[info]     +-cross build: true\n[info]\n[info] project: foo\n[info]   +-task: +foo / Test / test\n[info]     +-cross build: true\n[info]   +-task: +foo / publish\n[info]     +-cross build: true\n```\n\n\u003e [!WARNING]\n\u003e If `+foo / Test / test` fails the foo project is not published, but the root project is.\n\u003e Only use this setting if you accept this behavior.\n\n\u003e [!CAUTION]\n\u003e `Global / stepsGrouping := StepsGrouping.ByProject` will set the grouping for all\n\u003e enabled steps plugins. It's recommended to set it per steps task scope, e.g. `Global /\n\u003e ci / stepsGrouping := StepsGrouping.ByProject`.\n\n### Add custom result messages to the report\n\nResult messages are shown in the report with a completed step or when you pass the `-s`\nflag to `stepsTree`. You can add custom messages both to existing tasks or commands and to\nnew tasks, for example in a [custom steps plugin][create-steps-plugin]. Custom messages\nare added with `MessageBuilder`s:\n\n```sbt\nGlobal / stepsMessagesForSuccess += TaskMessageBuilder.forSuccessSingle(Test / test) { _ =\u003e \n  CustomSuccessMessage(\"All tests passed!\")\n}\nlazy val myLibrary = (project in file(\".\"))\n  .settings(\n    ci / steps := Seq(\n      Test / test,\n    )\n  )\n```\n\nThis will result in the following steps report:\n\n```\nsbt:myLibrary\u003e ci\n...\nsbt:myLibrary\u003e ci/stepsTree -s\n[info] task: Test / test\n[info]   +-project steps:\n[info]     +-task: myLibrary / Test / test\n[info]       +-status: succeeded\n[info]         +-All tests passed!\n```\n\n\u003e [!NOTE]\n\u003e Always set these settings in the `Global` scope, e.g. `Global / ci /\n\u003e stepsMessagesForSuccess`. Otherwise it is unused and has no effect. Fortunately, sbt\n\u003e will warn you about this.\n\n\u003e [!IMPORTANT]\n\u003e Never reset `stepsMessagesFor...` with the `:=` operator. Always append with `+=` or\n\u003e `++=`, unless you want to lose the defaults.\n\n\u003e [!TIP]\n\u003e Custom message can be created in several ways. The above example shows the shorthand\n\u003e method. You can also create a separate case class. See the [plugin development\n\u003e documentation][create-steps-plugin] for an example.\n\n## Acknowledgements\n\n- [sbt-release](https://github.com/sbt/sbt-release) for the inspiration for this plugin\n- [Simacan](https://github.com/simacan) where this plugin's development started\n- [Pascal](https://github.com/steinerpascal) for providing valuable feedback before going public\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagboom%2Fsbt-steps","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagboom%2Fsbt-steps","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagboom%2Fsbt-steps/lists"}