diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b177da0f..4b8b32e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,16 +5,15 @@ name: CI env: JDK_JAVA_OPTIONS: -XX:+PrintCommandLineFlags 'on': - workflow_dispatch: {} release: types: - - published - push: - branches: - - main + - published pull_request: branches-ignore: - - gh-pages + - gh-pages + push: + branches: + - main concurrency: group: ${{ github.workflow }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.run_id || github.ref }} cancel-in-progress: true @@ -24,234 +23,244 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: '17' - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Check all code compiles - run: sbt +Test/compile - - name: Check artifacts build process - run: sbt +publishLocal - - name: Check website build process - run: sbt docs/clean; sbt docs/buildWebsite + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: '17' + check-latest: true + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Check all code compiles + run: sbt +Test/compile + - name: Check artifacts build process + run: sbt +publishLocal + - name: Check website build process + run: sbt docs/clean; sbt docs/buildWebsite lint: name: Lint runs-on: ubuntu-latest continue-on-error: false steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: '17' - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Check if the site workflow is up to date - run: sbt ciCheckGithubWorkflow - - name: Lint - run: sbt lint + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: '17' + check-latest: true + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Check if the site workflow is up to date + run: sbt ciCheckGithubWorkflow + - name: Lint + run: sbt lint test: name: Test runs-on: ubuntu-latest continue-on-error: false strategy: - fail-fast: false matrix: java: - - '11' - - '17' - - '21' + - '11' + - '17' + - '21' + fail-fast: false steps: - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: ${{ matrix.java }} - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Test - run: sbt +test + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: ${{ matrix.java }} + check-latest: true + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Test + run: sbt +test update-readme: name: Update README runs-on: ubuntu-latest continue-on-error: false if: ${{ github.event_name == 'push' }} steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: '17' - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Generate Readme - run: sbt docs/generateReadme - - name: Commit Changes - run: | - git config --local user.email "zio-assistant[bot]@users.noreply.github.com" - git config --local user.name "ZIO Assistant" - git add README.md - git commit -m "Update README.md" || echo "No changes to commit" - - name: Generate Token - id: generate-token - uses: zio/generate-github-app-token@v1.0.0 - with: - app_id: ${{ secrets.APP_ID }} - app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Create Pull Request - id: cpr - uses: peter-evans/create-pull-request@v6 - with: - body: |- - Autogenerated changes after running the `sbt docs/generateReadme` command of the [zio-sbt-website](https://zio.dev/zio-sbt) plugin. + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: '17' + check-latest: true + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Generate Readme + run: sbt docs/generateReadme + - name: Commit Changes + run: | + git config --local user.email "zio-assistant[bot]@users.noreply.github.com" + git config --local user.name "ZIO Assistant" + git add README.md + git commit -m "Update README.md" || echo "No changes to commit" + - name: Generate Token + id: generate-token + uses: zio/generate-github-app-token@v1.0.0 + with: + app_id: ${{ secrets.APP_ID }} + app_private_key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + title: Update README.md + commit-message: Update README.md + branch: zio-sbt-website/update-readme + delete-branch: true + body: |- + Autogenerated changes after running the `sbt docs/generateReadme` command of the [zio-sbt-website](https://zio.dev/zio-sbt) plugin. - I will automatically update the README.md file whenever there is new change for README.md, e.g. - - After each release, I will update the version in the installation section. - - After any changes to the "docs/index.md" file, I will update the README.md file accordingly. - branch: zio-sbt-website/update-readme - commit-message: Update README.md - token: ${{ steps.generate-token.outputs.token }} - delete-branch: true - title: Update README.md - - name: Approve PR - if: ${{ steps.cpr.outputs.pull-request-number }} - run: gh pr review "$PR_URL" --approve - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_URL: ${{ steps.cpr.outputs.pull-request-url }} - - name: Enable Auto-Merge - if: ${{ steps.cpr.outputs.pull-request-number }} - run: gh pr merge --auto --squash "$PR_URL" || gh pr merge --squash "$PR_URL" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_URL: ${{ steps.cpr.outputs.pull-request-url }} + I will automatically update the README.md file whenever there is new change for README.md, e.g. + - After each release, I will update the version in the installation section. + - After any changes to the "docs/index.md" file, I will update the README.md file accordingly. + token: ${{ steps.generate-token.outputs.token }} + - name: Approve PR + if: ${{ steps.cpr.outputs.pull-request-number }} + run: gh pr review "$PR_URL" --approve + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ steps.cpr.outputs.pull-request-url }} + - name: Enable Auto-Merge + if: ${{ steps.cpr.outputs.pull-request-number }} + run: gh pr merge --auto --squash "$PR_URL" || gh pr merge --squash "$PR_URL" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ steps.cpr.outputs.pull-request-url }} ci: name: ci runs-on: ubuntu-latest continue-on-error: false needs: - - lint - - test - - build + - lint + - test + - build steps: - - name: Report Successful CI - run: echo "ci passed" + - name: Report Successful CI + run: echo "ci passed" release: name: Release runs-on: ubuntu-latest continue-on-error: false needs: - - ci + - ci if: ${{ github.event_name != 'pull_request' }} steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: '17' - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Release - run: sbt ci-release - env: - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - PGP_SECRET: ${{ secrets.PGP_SECRET }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: '17' + check-latest: true + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Release + run: sbt ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} release-docs: name: Release Docs runs-on: ubuntu-latest continue-on-error: false needs: - - release + - release if: ${{ ((github.event_name == 'release') && (github.event.action == 'published')) || (github.event_name == 'workflow_dispatch') }} steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: Install libuv - run: sudo apt-get update && sudo apt-get install -y libuv1-dev - - name: Setup Scala - uses: actions/setup-java@v4 - with: - distribution: corretto - java-version: '17' - check-latest: true - - name: Cache Dependencies - uses: coursier/cache-action@v6 - - name: Setup NodeJs - uses: actions/setup-node@v4 - with: - node-version: 16.x - registry-url: https://registry.npmjs.org - - name: Publish Docs to NPM Registry - run: sbt docs/publishToNpm - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: Install libuv + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Setup Scala + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: '17' + check-latest: true + - name: Cache Dependencies + uses: coursier/cache-action@v6 + - name: Setup NodeJs + uses: actions/setup-node@v4 + with: + node-version: 16.x + registry-url: https://registry.npmjs.org + - name: Publish Docs to NPM Registry + run: sbt docs/publishToNpm + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} notify-docs-release: name: Notify Docs Release runs-on: ubuntu-latest continue-on-error: false needs: - - release-docs + - release-docs if: ${{ (github.event_name == 'release') && (github.event.action == 'published') }} steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - name: notify the main repo about the new release of docs package - run: | - PACKAGE_NAME=$(cat docs/package.json | grep '"name"' | awk -F'"' '{print $4}') - PACKAGE_VERSION=$(npm view $PACKAGE_NAME version) - curl -L \ - -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: token ${{ secrets.PAT_TOKEN }}"\ - https://api.github.com/repos/zio/zio/dispatches \ - -d '{ - "event_type":"update-docs", - "client_payload":{ - "package_name":"'"${PACKAGE_NAME}"'", - "package_version": "'"${PACKAGE_VERSION}"'" - } - }' + - name: Git Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + - name: notify the main repo about the new release of docs package + run: | + PACKAGE_NAME=$(cat docs/package.json | grep '"name"' | awk -F'"' '{print $4}') + PACKAGE_VERSION=$(npm view $PACKAGE_NAME version) + curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${{ secrets.PAT_TOKEN }}"\ + https://api.github.com/repos/zio/zio/dispatches \ + -d '{ + "event_type":"update-docs", + "client_payload":{ + "package_name":"'"${PACKAGE_NAME}"'", + "package_version": "'"${PACKAGE_VERSION}"'" + } + }' diff --git a/.sbtopts b/.sbtopts new file mode 100644 index 00000000..3b630f6a --- /dev/null +++ b/.sbtopts @@ -0,0 +1 @@ +-Dsbt.io.implicit.relative.glob.conversion=allow diff --git a/README.md b/README.md index a619532d..7e49c0e9 100644 --- a/README.md +++ b/README.md @@ -168,12 +168,12 @@ In some cases, we may have multiple submodules in our project and we want to tes The `ciTargetScalaVersions` setting key is used to define a mapping of project names to the Scala versions that should be used for testing phase of continuous integration (CI). -For example, suppose we have a project with the name "submoduleA" and we want to test it against Scala `2.12.19`, and for the "submoduleB" we want to test it against Scala `2.12.19` and `2.13.13` and `3.3.3`, We can define the `ciTargetScalaVersions` setting as follows: +For example, suppose we have a project with the name "submoduleA" and we want to test it against Scala `2.12.20`, and for the "submoduleB" we want to test it against Scala `2.12.20` and `2.13.15` and `3.3.4`, We can define the `ciTargetScalaVersions` setting as follows: ```scala ThisBuild / ciTargetScalaVersions := Map( - "submoduleA" -> Seq("2.12.19"), - "submoduleB" -> Seq("2.12.19", "2.13.13", "3.3.3") + "submoduleA" -> Seq("2.12.20"), + "submoduleB" -> Seq("2.12.20", "2.13.15", "3.3.4") ) ``` @@ -206,10 +206,10 @@ test: matrix: java: ['11', '17', '21'] scala-project: - - ++2.12.19 submoduleA - - ++2.12.19 submoduleB - - ++2.13.13 submoduleB - - ++3.3.3 submoduleB + - ++2.12.20 submoduleA + - ++2.12.20 submoduleB + - ++2.13.15 submoduleB + - ++3.3.4 submoduleB steps: - name: Install libuv run: sudo apt-get update && sudo apt-get install -y libuv1-dev diff --git a/docs/index.md b/docs/index.md index 31d06d14..5489d349 100644 --- a/docs/index.md +++ b/docs/index.md @@ -167,12 +167,12 @@ In some cases, we may have multiple submodules in our project and we want to tes The `ciTargetScalaVersions` setting key is used to define a mapping of project names to the Scala versions that should be used for testing phase of continuous integration (CI). -For example, suppose we have a project with the name "submoduleA" and we want to test it against Scala `2.12.19`, and for the "submoduleB" we want to test it against Scala `2.12.19` and `2.13.13` and `3.3.3`, We can define the `ciTargetScalaVersions` setting as follows: +For example, suppose we have a project with the name "submoduleA" and we want to test it against Scala `2.12.20`, and for the "submoduleB" we want to test it against Scala `2.12.20` and `2.13.15` and `3.3.4`, We can define the `ciTargetScalaVersions` setting as follows: ```scala ThisBuild / ciTargetScalaVersions := Map( - "submoduleA" -> Seq("2.12.19"), - "submoduleB" -> Seq("2.12.19", "2.13.13", "3.3.3") + "submoduleA" -> Seq("2.12.20"), + "submoduleB" -> Seq("2.12.20", "2.13.15", "3.3.4") ) ``` @@ -205,10 +205,10 @@ test: matrix: java: ['11', '17', '21'] scala-project: - - ++2.12.19 submoduleA - - ++2.12.19 submoduleB - - ++2.13.13 submoduleB - - ++3.3.3 submoduleB + - ++2.12.20 submoduleA + - ++2.12.20 submoduleB + - ++2.13.15 submoduleB + - ++3.3.4 submoduleB steps: - name: Install libuv run: sudo apt-get update && sudo apt-get install -y libuv1-dev diff --git a/project/Versions.scala b/project/Versions.scala index 03da9412..ff018dfb 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -1,6 +1,6 @@ object Versions { - val Scala212 = "2.12.19" - val Scala213 = "2.13.13" - val Scala3 = "3.3.3" - val zio = "2.0.21" + val Scala212 = "2.12.20" + val Scala213 = "2.13.15" + val Scala3 = "3.3.4" + val zio = "2.1.13" } diff --git a/project/build.properties b/project/build.properties index ee4c672c..1767a6f8 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version = 1.10.5 diff --git a/project/plugins.sbt b/project/plugins.sbt index b9292898..9f1882ac 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,9 @@ // Build Server Plugins -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.5") // Linting Plugins addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0") addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.3.1") // Versioning and Release Plugins @@ -11,20 +11,23 @@ addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") // Docs Plugins -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.4") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") // Cross-Compiler Plugins -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.4") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") addSbtPlugin("org.portable-scala" % "sbt-platform-deps" % "1.0.2") // Benchmarking Plugins addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") -libraryDependencies += "org.snakeyaml" % "snakeyaml-engine" % "2.7" -libraryDependencies += "dev.zio" %% "zio" % "2.1.8" -libraryDependencies += "io.circe" %% "circe-yaml" % "0.16.0" +libraryDependencies += "org.snakeyaml" % "snakeyaml-engine" % "2.8" +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" +libraryDependencies += "dev.zio" %% "zio-json" % "0.7.3+19-9339fbba-SNAPSHOT" +libraryDependencies += "dev.zio" %% "zio-json-yaml" % "0.7.3+19-9339fbba-SNAPSHOT" + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-sbt-ci/build.sbt b/zio-sbt-ci/build.sbt index b7d36a60..aaba83b4 100644 --- a/zio-sbt-ci/build.sbt +++ b/zio-sbt-ci/build.sbt @@ -1,2 +1,5 @@ -libraryDependencies += "dev.zio" %% "zio" % "2.1.8" -libraryDependencies += "io.circe" %% "circe-yaml" % "0.16.0" +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" +libraryDependencies += "dev.zio" %% "zio-json" % "0.7.3+19-9339fbba-SNAPSHOT" +libraryDependencies += "dev.zio" %% "zio-json-yaml" % "0.7.3+19-9339fbba-SNAPSHOT" + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-sbt-ci/src/main/scala/zio/sbt/V.scala b/zio-sbt-ci/src/main/scala/zio/sbt/V.scala index d123c12a..770fd1a9 100644 --- a/zio-sbt-ci/src/main/scala/zio/sbt/V.scala +++ b/zio-sbt-ci/src/main/scala/zio/sbt/V.scala @@ -5,10 +5,11 @@ object V { Map( "peter-evans/create-pull-request" -> "v6", "zio/generate-github-app-token" -> "v1.0.0", - "pierotofy/set-swap-space" -> "master", + "pierotofy/set-swap-space" -> "49819abfb41bd9b44fb781159c033dba90353a7c", // 1.0, "actions/checkout" -> "v4", "coursier/cache-action" -> "v6", "actions/setup-java" -> "v4", - "actions/setup-node" -> "v4" + "actions/setup-node" -> "v4", + "sbt/setup-sbt" -> "v1" ).map { case (k, v) => (k, s"$k@$v") }.apply(packageName) } diff --git a/zio-sbt-ci/src/main/scala/zio/sbt/ZioSbtCiPlugin.scala b/zio-sbt-ci/src/main/scala/zio/sbt/ZioSbtCiPlugin.scala index 70371ac6..9be4cb1c 100644 --- a/zio-sbt-ci/src/main/scala/zio/sbt/ZioSbtCiPlugin.scala +++ b/zio-sbt-ci/src/main/scala/zio/sbt/ZioSbtCiPlugin.scala @@ -15,14 +15,14 @@ */ package zio.sbt +import scala.collection.immutable.ListMap import scala.language.experimental.macros import scala.sys.process._ -import io.circe._ -import io.circe.syntax._ -import io.circe.yaml.Printer.{LineBreak, YamlVersion} import sbt.{Def, io => _, _} +import zio.json._ +import zio.json.yaml._ import zio.sbt.githubactions.Step.SingleStep import zio.sbt.githubactions.{Job, Step, _} @@ -101,7 +101,6 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "build", name = "Build", continueOnError = true, steps = { @@ -110,6 +109,7 @@ object ZioSbtCiPlugin extends AutoPlugin { checkout, SetupLibuv, SetupJava(javaVersion), + SetupSbt, CacheDependencies ) ++ checkAllCodeCompiles ++ checkArtifactBuildProcess ++ checkWebsiteBuildProcess } @@ -127,10 +127,12 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "lint", name = "Lint", steps = (if (swapSizeGB > 0) Seq(setSwapSpace) else Seq.empty) ++ - Seq(checkout, SetupLibuv, SetupJava(javaVersion), CacheDependencies) ++ checkGithubWorkflow ++ Seq(lint) + Seq(checkout, SetupLibuv, SetupJava(javaVersion), SetupSbt, CacheDependencies) ++ + checkGithubWorkflow.flatMap( + _.flatten + ) ++ Seq(lint) ) ) } @@ -155,13 +157,12 @@ object ZioSbtCiPlugin extends AutoPlugin { }.map(e => e._1 + "/test").mkString(" ")}" Job( - id = "test", name = "Test", strategy = Some( Strategy( - matrix = Map( - "java" -> javaPlatforms.toList, - "scala" -> scalaVersionMatrix.values.flatten.toSet.toList + matrix = ListMap( + "java" -> javaPlatforms.toList.sorted, + "scala" -> scalaVersionMatrix.values.flatten.toList.distinct.sorted ), maxParallel = matrixMaxParallel, failFast = false @@ -171,13 +172,14 @@ object ZioSbtCiPlugin extends AutoPlugin { (if (swapSizeGB > 0) Seq(setSwapSpace) else Seq.empty) ++ Seq( SetupLibuv, SetupJava("${{ matrix.java }}"), + SetupSbt, CacheDependencies, checkout ) ++ (if (javaPlatformMatrix.values.toSet.isEmpty) { scalaVersionMatrix.values.toSeq.flatten.distinct.map { scalaVersion: String => Step.SingleStep( name = "Test", - condition = Some(Condition.Expression(s"matrix.scala == '$scalaVersion'")), + `if` = Some(Condition.Expression(s"matrix.scala == '$scalaVersion'")), run = Some( prefixJobs + "sbt ++${{ matrix.scala }}" + makeTests( scalaVersion @@ -200,7 +202,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Step.SingleStep( name = "Test", - condition = Some( + `if` = Some( Condition.Expression(s"matrix.java == '$javaPlatform'") && Condition.Expression( s"matrix.scala == '$scalaVersion'" ) @@ -218,15 +220,14 @@ object ZioSbtCiPlugin extends AutoPlugin { val FlattenTests = Job( - id = "test", name = "Test", strategy = Some( Strategy( - matrix = Map( - "java" -> javaPlatforms.toList + matrix = ListMap( + "java" -> javaPlatforms.toList.sorted ) ++ (if (javaPlatformMatrix.isEmpty) { - Map("scala-project" -> scalaVersionMatrix.flatMap { case (moduleName, versions) => + ListMap("scala-project" -> scalaVersionMatrix.flatMap { case (moduleName, versions) => versions.map { version => s"++$version $moduleName" } @@ -251,51 +252,53 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( SetupLibuv, SetupJava("${{ matrix.java }}"), + SetupSbt, CacheDependencies, - checkout, + checkout + ) ++ ( if (javaPlatformMatrix.values.toSet.isEmpty) { - Step.SingleStep( - name = "Test", - run = Some(prefixJobs + "sbt ${{ matrix.scala-project }}/test") + Seq( + Step.SingleStep( + name = "Test", + run = Some(prefixJobs + "sbt ${{ matrix.scala-project }}/test") + ) ) } else { - Step.StepSequence( - Seq( - Step.SingleStep( - name = "Java 11 Tests", - condition = Some(Condition.Expression("matrix.java == '11'")), - run = Some( - prefixJobs + "sbt ${{ matrix.scala-project-java11 }}/test" - ) - ), - Step.SingleStep( - name = "Java 17 Tests", - condition = Some(Condition.Expression("matrix.java == '17'")), - run = Some( - prefixJobs + "sbt ${{ matrix.scala-project-java17 }}/test" - ) - ), - Step.SingleStep( - name = "Java 21 Tests", - condition = Some(Condition.Expression("matrix.java == '21'")), - run = Some( - prefixJobs + "sbt ${{ matrix.scala-project-java21 }}/test" - ) + Seq( + Step.SingleStep( + name = "Java 11 Tests", + `if` = Some(Condition.Expression("matrix.java == '11'")), + run = Some( + prefixJobs + "sbt ${{ matrix.scala-project-java11 }}/test" + ) + ), + Step.SingleStep( + name = "Java 17 Tests", + `if` = Some(Condition.Expression("matrix.java == '17'")), + run = Some( + prefixJobs + "sbt ${{ matrix.scala-project-java17 }}/test" + ) + ), + Step.SingleStep( + name = "Java 21 Tests", + `if` = Some(Condition.Expression("matrix.java == '21'")), + run = Some( + prefixJobs + "sbt ${{ matrix.scala-project-java21 }}/test" ) ) ) - } ) ) val DefaultTestStrategy = Job( - id = "test", name = "Test", strategy = Some( Strategy( - matrix = Map("java" -> javaPlatforms.toList), + matrix = ListMap( + "java" -> javaPlatforms.toList.sorted + ), maxParallel = matrixMaxParallel, failFast = false ) @@ -304,6 +307,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( SetupLibuv, SetupJava("${{ matrix.java }}"), + SetupSbt, CacheDependencies, checkout, Step.SingleStep( @@ -324,9 +328,8 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "ci", name = "ci", - need = pullRequestApprovalJobs, + needs = pullRequestApprovalJobs, steps = Seq( SingleStep( name = "Report Successful CI", @@ -347,14 +350,14 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "update-readme", name = "Update README", - condition = updateReadmeCondition orElse Some(Condition.Expression("github.event_name == 'push'")), + `if` = updateReadmeCondition orElse Some(Condition.Expression("github.event_name == 'push'")), steps = (if (swapSizeGB > 0) Seq(setSwapSpace) else Seq.empty) ++ Seq( checkout, SetupLibuv, SetupJava(javaVersion), + SetupSbt, CacheDependencies, generateReadme, Step.SingleStep( @@ -369,33 +372,33 @@ object ZioSbtCiPlugin extends AutoPlugin { name = "Generate Token", id = Some("generate-token"), uses = Some(ActionRef(V("zio/generate-github-app-token"))), - parameters = Map( - "app_id" -> "${{ secrets.APP_ID }}".asJson, - "app_private_key" -> "${{ secrets.APP_PRIVATE_KEY }}".asJson + `with` = ListMap( + "app_id" -> "${{ secrets.APP_ID }}".toJsonAST.right.get, + "app_private_key" -> "${{ secrets.APP_PRIVATE_KEY }}".toJsonAST.right.get ) ), Step.SingleStep( name = "Create Pull Request", id = Some("cpr"), uses = Some(ActionRef(V("peter-evans/create-pull-request"))), - parameters = Map( - "title" -> "Update README.md".asJson, - "commit-message" -> "Update README.md".asJson, - "branch" -> "zio-sbt-website/update-readme".asJson, - "delete-branch" -> true.asJson, + `with` = ListMap( + "title" -> "Update README.md".toJsonAST.right.get, + "commit-message" -> "Update README.md".toJsonAST.right.get, + "branch" -> "zio-sbt-website/update-readme".toJsonAST.right.get, + "delete-branch" -> true.toJsonAST.right.get, "body" -> """|Autogenerated changes after running the `sbt docs/generateReadme` command of the [zio-sbt-website](https://zio.dev/zio-sbt) plugin. | |I will automatically update the README.md file whenever there is new change for README.md, e.g. | - After each release, I will update the version in the installation section. - | - After any changes to the "docs/index.md" file, I will update the README.md file accordingly.""".stripMargin.asJson, - "token" -> "${{ steps.generate-token.outputs.token }}".asJson + | - After any changes to the "docs/index.md" file, I will update the README.md file accordingly.""".stripMargin.toJsonAST.right.get, + "token" -> "${{ steps.generate-token.outputs.token }}".toJsonAST.right.get ) ), Step.SingleStep( name = "Approve PR", - condition = Some(Condition.Expression("steps.cpr.outputs.pull-request-number")), - env = Map( + `if` = Some(Condition.Expression("steps.cpr.outputs.pull-request-number")), + env = ListMap( "GITHUB_TOKEN" -> "${{ secrets.GITHUB_TOKEN }}", "PR_URL" -> "${{ steps.cpr.outputs.pull-request-url }}" ), @@ -403,8 +406,8 @@ object ZioSbtCiPlugin extends AutoPlugin { ), Step.SingleStep( name = "Enable Auto-Merge", - condition = Some(Condition.Expression("steps.cpr.outputs.pull-request-number")), - env = Map( + `if` = Some(Condition.Expression("steps.cpr.outputs.pull-request-number")), + env = ListMap( "GITHUB_TOKEN" -> "${{ secrets.GITHUB_TOKEN }}", "PR_URL" -> "${{ steps.cpr.outputs.pull-request-url }}" ), @@ -425,15 +428,15 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "release", name = "Release", - need = jobs, - condition = Some(Condition.Expression("github.event_name != 'pull_request'")), + needs = jobs, + `if` = Some(Condition.Expression("github.event_name != 'pull_request'")), steps = (if (swapSizeGB > 0) Seq(setSwapSpace) else Seq.empty) ++ Seq( checkout, SetupLibuv, SetupJava(javaVersion), + SetupSbt, CacheDependencies, release ) @@ -450,10 +453,9 @@ object ZioSbtCiPlugin extends AutoPlugin { Seq( Job( - id = "release-docs", name = "Release Docs", - need = Seq("release"), - condition = Some( + needs = Seq("release"), + `if` = Some( Condition.Expression("github.event_name == 'release'") && Condition.Expression("github.event.action == 'published'") || Condition.Expression( "github.event_name == 'workflow_dispatch'" @@ -461,23 +463,18 @@ object ZioSbtCiPlugin extends AutoPlugin { ), steps = (if (swapSizeGB > 0) Seq(setSwapSpace) else Seq.empty) ++ Seq( - Step.StepSequence( - Seq( - checkout, - SetupLibuv, - SetupJava(javaVersion), - CacheDependencies, - SetupNodeJs, - publishToNpmRegistry - ) - ) + checkout, + SetupLibuv, + SetupJava(javaVersion), + CacheDependencies, + SetupNodeJs, + publishToNpmRegistry ) ), Job( - id = "notify-docs-release", name = "Notify Docs Release", - need = Seq("release-docs"), - condition = Some( + needs = Seq("release-docs"), + `if` = Some( Condition.Expression("github.event_name == 'release'") && Condition.Expression("github.event.action == 'published'") ), @@ -520,40 +517,48 @@ object ZioSbtCiPlugin extends AutoPlugin { val jvmOptions = Seq("-XX:+PrintCommandLineFlags") ++ ciJvmOptions.value val nodeOptions = ciNodeOptions.value - val jvmMap = Map( + val jvmMap = ListMap( "JDK_JAVA_OPTIONS" -> jvmOptions.mkString(" ") ) - val nodeMap: Map[String, String] = - if (nodeOptions.nonEmpty) Map("NODE_OPTIONS" -> nodeOptions.mkString(" ")) else Map.empty - - val workflow = yaml - .Printer( - preserveOrder = true, - dropNullKeys = true, - splitLines = false, - lineBreak = LineBreak.Unix, - version = YamlVersion.Auto + val nodeMap: ListMap[String, String] = + if (nodeOptions.nonEmpty) ListMap("NODE_OPTIONS" -> nodeOptions.mkString(" ")) else ListMap.empty + + val yamlOptions = + YamlOptions.default.copy( + dropNulls = true, + lineBreak = org.yaml.snakeyaml.DumperOptions.LineBreak.UNIX, + maxScalarWidth = Some(1024) ) - .pretty( - Workflow( - name = workflowName, - env = jvmMap ++ nodeMap, - triggers = Seq( - Trigger.WorkflowDispatch(), - Trigger.Release(Seq("published")), - Trigger.Push(branches = enabledBranches.map(Branch.Named)), - Trigger.PullRequest(ignoredBranches = Seq(Branch.Named("gh-pages"))) - ), - jobs = - buildJobs ++ lintJobs ++ testJobs ++ updateReadmeJobs ++ reportSuccessful ++ releaseJobs ++ postReleaseJobs - ).asJson + + val workflow = + Workflow( + name = workflowName, + env = jvmMap ++ nodeMap, + on = Some( + Triggers( + release = Some(Trigger.Release(Seq(Trigger.ReleaseType.Published))), + push = Some(Trigger.Push(branches = enabledBranches.map(Branch.Named))).filter(_.branches.nonEmpty), + pullRequest = Some(Trigger.PullRequest(branchesIgnore = Seq(Branch.Named("gh-pages")))) + ) + ), + jobs = ListMap( + (buildJobs ++ lintJobs ++ testJobs ++ updateReadmeJobs ++ reportSuccessful ++ releaseJobs ++ postReleaseJobs) + .map(job => job.id -> job): _* + ) ) + val yaml: String = zio.json.ast.Json.decoder + .decodeJson(workflow.toJson) + .flatMap(_.toYaml(yamlOptions).left.map(_.getMessage())) match { + case Right(value) => value + case Left(error) => sys.error(s"Error generating workflow yaml: $error") + } + val template = s"""|# This file was autogenerated using `zio-sbt-ci` plugin via `sbt ciGenerateGithubWorkflow` |# task and should be included in the git repository. Please do not edit it manually. | - |$workflow""".stripMargin + |$yaml""".stripMargin IO.write(new File(s".github/workflows/${ciWorkflowName.value.toLowerCase}.yml"), template) } @@ -641,7 +646,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Step.SingleStep( name = "Set Swap Space", uses = Some(ActionRef(V("pierotofy/set-swap-space"))), - parameters = Map("swap-size-gb" -> swapSizeGB.asJson) + `with` = ListMap("swap-size-gb" -> swapSizeGB.toString.toJsonAST.right.get) ) } @@ -650,7 +655,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Step.SingleStep( name = "Git Checkout", uses = Some(ActionRef(V("actions/checkout"))), - parameters = Map("fetch-depth" -> "0".asJson) + `with` = ListMap("fetch-depth" -> "0".toJsonAST.right.get) ) } @@ -662,13 +667,18 @@ object ZioSbtCiPlugin extends AutoPlugin { def SetupJava(version: String = "17"): Step.SingleStep = Step.SingleStep( name = "Setup Scala", uses = Some(ActionRef(V("actions/setup-java"))), - parameters = Map( - "distribution" -> "corretto".asJson, - "java-version" -> version.asJson, - "check-latest" -> true.asJson + `with` = ListMap( + "distribution" -> "corretto".toJsonAST.right.get, + "java-version" -> version.toJsonAST.right.get, + "check-latest" -> true.toJsonAST.right.get ) ) + val SetupSbt: Step.SingleStep = Step.SingleStep( + name = "Setup SBT", + uses = Some(ActionRef(V("sbt/setup-sbt"))) + ) + lazy val CacheDependencies: Step.SingleStep = Step.SingleStep( name = "Cache Dependencies", uses = Some(ActionRef(V("coursier/cache-action"))) @@ -705,7 +715,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Step.SingleStep( name = "Release", run = Some(prefixJobs + "sbt ci-release"), - env = Map( + env = ListMap( "PGP_PASSPHRASE" -> "${{ secrets.PGP_PASSPHRASE }}", "PGP_SECRET" -> "${{ secrets.PGP_SECRET }}", "SONATYPE_PASSWORD" -> "${{ secrets.SONATYPE_PASSWORD }}", @@ -717,9 +727,9 @@ object ZioSbtCiPlugin extends AutoPlugin { val SetupNodeJs: Step.SingleStep = Step.SingleStep( name = "Setup NodeJs", uses = Some(ActionRef(V("actions/setup-node"))), - parameters = Map( - "node-version" -> "16.x".asJson, - "registry-url" -> "https://registry.npmjs.org".asJson + `with` = ListMap( + "node-version" -> "16.x".toJsonAST.right.get, + "registry-url" -> "https://registry.npmjs.org".toJsonAST.right.get ) ) @@ -732,7 +742,7 @@ object ZioSbtCiPlugin extends AutoPlugin { Step.SingleStep( name = "Publish Docs to NPM Registry", run = Some(prefixJobs + s"sbt docs/${docsVersioning.npmCommand}"), - env = Map("NODE_AUTH_TOKEN" -> "${{ secrets.NPM_TOKEN }}") + env = ListMap("NODE_AUTH_TOKEN" -> "${{ secrets.NPM_TOKEN }}") ) } diff --git a/zio-sbt-ecosystem/build.sbt b/zio-sbt-ecosystem/build.sbt index c673cdb8..87193342 100644 --- a/zio-sbt-ecosystem/build.sbt +++ b/zio-sbt-ecosystem/build.sbt @@ -1,9 +1,9 @@ // Build Server Plugins -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.2") // Linting Plugins addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0") addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.3.1") // Versioning and Release Plugins @@ -11,12 +11,12 @@ addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") // Docs Plugins -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.4") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") // Cross-Compiler Plugins -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.4") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") @@ -27,8 +27,10 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") addSbtPlugin("pl.project13.scala" % "sbt-jcstress" % "0.2.0") // Binary Compatibility Plugin -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.3") +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" -libraryDependencies += "org.snakeyaml" % "snakeyaml-engine" % "2.7" -libraryDependencies += "dev.zio" %% "zio" % "2.1.8" -libraryDependencies += "io.circe" %% "circe-yaml" % "0.16.0" +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" +libraryDependencies += "dev.zio" %% "zio-json" % "0.7.3+19-9339fbba-SNAPSHOT" +libraryDependencies += "dev.zio" %% "zio-json-yaml" % "0.7.3+19-9339fbba-SNAPSHOT" + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-sbt-ecosystem/src/main/scala/zio/sbt/ScalaCompilerSettings.scala b/zio-sbt-ecosystem/src/main/scala/zio/sbt/ScalaCompilerSettings.scala index 1fc5b90a..43ba56c3 100644 --- a/zio-sbt-ecosystem/src/main/scala/zio/sbt/ScalaCompilerSettings.scala +++ b/zio-sbt-ecosystem/src/main/scala/zio/sbt/ScalaCompilerSettings.scala @@ -190,7 +190,7 @@ trait ScalaCompilerSettings { ) } else Seq.empty }, - Test / parallelExecution := scalaBinaryVersion.value != "3", + Test / parallelExecution := scalaBinaryVersion.value != "3", // why not parallel execution for Scala 3? incOptions ~= (_.withLogRecompileOnMacro(false)), autoAPIMappings := true, unusedCompileDependenciesFilter -= moduleFilter("org.scala-js", "scalajs-library") @@ -233,8 +233,7 @@ trait ScalaCompilerSettings { libraryDependencies ++= Seq( "dev.zio" %%% "zio-test" % ZioSbtEcosystemPlugin.autoImport.zioVersion.value % Test, "dev.zio" %%% "zio-test-sbt" % ZioSbtEcosystemPlugin.autoImport.zioVersion.value % Test - ), - testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") + ) ) else Seq.empty) ++ { if (enableStreaming) diff --git a/zio-sbt-ecosystem/src/main/scala/zio/sbt/Versions.scala b/zio-sbt-ecosystem/src/main/scala/zio/sbt/Versions.scala index 0a971aa1..5da8b57a 100644 --- a/zio-sbt-ecosystem/src/main/scala/zio/sbt/Versions.scala +++ b/zio-sbt-ecosystem/src/main/scala/zio/sbt/Versions.scala @@ -22,11 +22,11 @@ object Versions { val KindProjectorVersion = "0.13.3" val ScaluzziVersion = "0.1.23" - val scala3 = "3.3.3" - val scala212 = "2.12.19" - val scala213 = "2.13.13" + val scala3 = "3.3.4" + val scala212 = "2.12.20" + val scala213 = "2.13.15" - val zioVersion = "2.0.21" + val zioVersion = "2.1.13" lazy val betterMonadFor: ModuleID = "com.olegpy" %% "better-monadic-for" % "0.3.1" } diff --git a/zio-sbt-ecosystem/src/sbt-test/zio-sbt-ecosystem/verifySettingsWithCI/.github/workflows/ci.yml b/zio-sbt-ecosystem/src/sbt-test/zio-sbt-ecosystem/verifySettingsWithCI/.github/workflows/ci.yml index f861c9d0..b02f9e67 100644 --- a/zio-sbt-ecosystem/src/sbt-test/zio-sbt-ecosystem/verifySettingsWithCI/.github/workflows/ci.yml +++ b/zio-sbt-ecosystem/src/sbt-test/zio-sbt-ecosystem/verifySettingsWithCI/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: matrix: java: ['11', '17', '21'] # These version must be different than the versions in V.scala to verify that we are reading from the ci.yml file. - scala: ['2.12.19', '2.13.13', '3.3.3' ] + scala: ['2.12.20', '2.13.15', '3.3.4' ] steps: - uses: actions/checkout@v4.1.1 - uses: actions/setup-java@v3.13.0 diff --git a/zio-sbt-githubactions/build.sbt b/zio-sbt-githubactions/build.sbt index b7d36a60..aaba83b4 100644 --- a/zio-sbt-githubactions/build.sbt +++ b/zio-sbt-githubactions/build.sbt @@ -1,2 +1,5 @@ -libraryDependencies += "dev.zio" %% "zio" % "2.1.8" -libraryDependencies += "io.circe" %% "circe-yaml" % "0.16.0" +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" +libraryDependencies += "dev.zio" %% "zio-json" % "0.7.3+19-9339fbba-SNAPSHOT" +libraryDependencies += "dev.zio" %% "zio-json-yaml" % "0.7.3+19-9339fbba-SNAPSHOT" + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") diff --git a/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/ScalaWorkflow.scala b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/ScalaWorkflow.scala index a9270149..f8a99987 100644 --- a/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/ScalaWorkflow.scala +++ b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/ScalaWorkflow.scala @@ -16,9 +16,9 @@ package zio.sbt.githubactions -import io.circe.syntax._ +import scala.collection.immutable.ListMap -import zio.sbt.githubactions.ScalaWorkflow.JavaVersion.JDK11 +import zio.json._ // The original code of the githubactions package was originally copied from the zio-aws-codegen project: // https://github.com/zio/zio-aws/tree/master/zio-aws-codegen/src/main/scala/zio/aws/codegen/githubactions @@ -29,8 +29,8 @@ object ScalaWorkflow { SingleStep( name = "Checkout current branch", uses = Some(ActionRef("actions/checkout@v2")), - parameters = Map( - "fetch-depth" := fetchDepth + `with` = ListMap( + "fetch-depth" -> fetchDepth.toJsonAST.right.get ) ) @@ -38,11 +38,11 @@ object ScalaWorkflow { SingleStep( name = "Setup Java and Scala", uses = Some(ActionRef("olafurpg/setup-scala@v11")), - parameters = Map( - "java-version" := (javaVersion match { + `with` = ListMap( + "java-version" -> (javaVersion match { case None => "${{ matrix.java }}" case Some(version) => version.asString - }) + }).toJsonAST.right.get ) ) @@ -50,12 +50,12 @@ object ScalaWorkflow { SingleStep( name = "Setup NodeJS", uses = Some(ActionRef("actions/setup-node@v3")), - parameters = Map( - "node-version" := (javaVersion match { + `with` = ListMap( + "node-version" -> (javaVersion match { case None => "16.x" case Some(version) => version.asString - }), - "registry-url" := "https://registry.npmjs.org" + }).toJsonAST.right.get, + "registry-url" -> "https://registry.npmjs.org".toJsonAST.right.get ) ) @@ -75,14 +75,14 @@ object ScalaWorkflow { SingleStep( name = "Cache SBT", uses = Some(ActionRef("actions/cache@v2")), - parameters = Map( - "path" := Seq( + `with` = ListMap( + "path" -> Seq( "~/.ivy2/cache", "~/.sbt", "~/.coursier/cache/v1", "~/.cache/coursier/v1" - ).mkString("\n"), - "key" := s"$osS-sbt-$scalaS-$${{ hashFiles('**/*.sbt') }}-$${{ hashFiles('**/build.properties') }}" + ).mkString("\n").toJsonAST.right.get, + "key" -> s"$osS-sbt-$scalaS-$${{ hashFiles('**/*.sbt') }}-$${{ hashFiles('**/build.properties') }}".toJsonAST.right.get ) ) } @@ -98,7 +98,7 @@ object ScalaWorkflow { parameters: List[String], heapGb: Int = 6, stackMb: Int = 16, - env: Map[String, String] = Map.empty + env: ListMap[String, String] = ListMap.empty ): Step = SingleStep( name, @@ -130,9 +130,9 @@ object ScalaWorkflow { SingleStep( s"Upload $id targets", uses = Some(ActionRef("actions/upload-artifact@v2")), - parameters = Map( - "name" := s"target-$id-$osS-$scalaS-$javaS", - "path" := "targets.tar" + `with` = ListMap( + "name" -> s"target-$id-$osS-$scalaS-$javaS".toJsonAST.right.get, + "path" -> "targets.tar".toJsonAST.right.get ) ) ) @@ -154,8 +154,8 @@ object ScalaWorkflow { SingleStep( s"Download stored $id targets", uses = Some(ActionRef("actions/download-artifact@v2")), - parameters = Map( - "name" := s"target-$id-$osS-$scalaS-$javaS" + `with` = ListMap( + "name" -> s"target-$id-$osS-$scalaS-$javaS".toJsonAST.right.get ) ), SingleStep( @@ -182,14 +182,14 @@ object ScalaWorkflow { SingleStep( "Load PGP secret", run = Some(".github/import-key.sh"), - env = Map("PGP_SECRET" -> "${{ secrets.PGP_SECRET }}") + env = ListMap("PGP_SECRET" -> "${{ secrets.PGP_SECRET }}") ) def turnstyle(): Step = SingleStep( "Turnstyle", uses = Some(ActionRef("softprops/turnstyle@v1")), - env = Map( + env = ListMap( "GITHUB_TOKEN" -> "${{ secrets.ADMIN_GITHUB_TOKEN }}" ) ) @@ -217,30 +217,39 @@ object ScalaWorkflow { case class ScalaVersion(version: String) - trait JavaVersion { - val asString: String + sealed trait JavaVersion { + def distribution: String + def version: String + def asString: String = s"$distribution:$version" } + object JavaVersion { - case class CorrettoJDK(javaVersion: String) extends JavaVersion { - override val asString: String = s"corretto:$javaVersion" + def apply(distribution: String, version: String): JavaVersion = CustomJDK(distribution, version) + + case class CustomJDK(distribution: String, version: String) extends JavaVersion + + case class CorrettoJDK(version: String) extends JavaVersion { + override def distribution: String = "corretto" } - val JDK11: JavaVersion = CorrettoJDK("11") - val JDK17: JavaVersion = CorrettoJDK("17") - val JDK21: JavaVersion = CorrettoJDK("21") + object CorrettoJDK { + val `11`: JavaVersion = CorrettoJDK("11") + val `17`: JavaVersion = CorrettoJDK("17") + val `21`: JavaVersion = CorrettoJDK("21") + } } implicit class JobOps(job: Job) { def matrix( scalaVersions: Seq[ScalaVersion], operatingSystems: Seq[OS] = Seq(OS.UbuntuLatest), - javaVersions: Seq[JavaVersion] = Seq(JDK11) + javaVersions: Seq[JavaVersion] = Seq(JavaVersion.CorrettoJDK.`11`) ): Job = job.copy( strategy = Some( Strategy( - matrix = Map( + matrix = ListMap( "os" -> operatingSystems.map(_.asString).toList, "scala" -> scalaVersions.map(_.version).toList, "java" -> javaVersions.map(_.asString).toList @@ -250,5 +259,4 @@ object ScalaWorkflow { runsOn = "${{ matrix.os }}" ) } - } diff --git a/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/model.scala b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/model.scala index a3a6d142..026836aa 100644 --- a/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/model.scala +++ b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/model.scala @@ -16,16 +16,24 @@ package zio.sbt.githubactions -import io.circe._ -import io.circe.syntax._ +import scala.collection.immutable.ListMap +import scala.util.{Failure, Success, Try} -import zio.sbt.githubactions.Step.StepSequence +import zio.json._ +import zio.json.ast.Json -sealed trait OS { - val asString: String +abstract class OS(name: String) { + val asString: String = name } object OS { - case object UbuntuLatest extends OS { val asString = "ubuntu-latest" } + + def apply(name: String): OS = Custom(name) + + case class Custom(name: String) extends OS(name) + + case object UbuntuLatest extends OS("ubuntu-latest") + case object Ubuntu2404 extends OS("ubuntu-24.04") + case object Ubuntu2204 extends OS("ubuntu-22.04") } sealed trait Branch @@ -33,96 +41,126 @@ object Branch { case object All extends Branch case class Named(name: String) extends Branch - implicit val encoder: Encoder[Branch] = { - case All => Json.fromString("*") - case Named(name) => Json.fromString(name) - } + implicit lazy val codec: JsonCodec[Branch] = JsonCodec.string.transform( + { + case "*" => All + case name => Named(name) + }, + { + case All => "*" + case Named(name) => name + } + ) } -sealed trait Trigger { - def toKeyValuePair: (String, Json) +@jsonMemberNames(SnakeCase) +case class Triggers( + workflowDispatch: Trigger.WorkflowDispatch = Trigger.WorkflowDispatch(), + release: Option[Trigger.Release] = None, + pullRequest: Option[Trigger.PullRequest] = None, + push: Option[Trigger.Push] = None, + create: Option[Trigger.Create] = None +) + +object Triggers { + + implicit lazy val codec: JsonCodec[Triggers] = DeriveJsonCodec.gen[Triggers] } -case class Input(key: String, description: String, required: Boolean, defaultValue: String) +sealed trait Trigger object Trigger { + case class InputValue(description: String, required: Boolean, default: String) + object InputValue { + implicit lazy val jsonCodec: JsonCodec[InputValue] = DeriveJsonCodec.gen[InputValue] + } + case class WorkflowDispatch( - inputs: Seq[Input] = Seq.empty - ) extends Trigger { - override def toKeyValuePair: (String, Json) = - "workflow_dispatch" := inputs.map { i => - i.key -> - Json.obj( - ("description", i.description.asJson), - ("required", i.required.asJson), - ("default", i.defaultValue.asJson) - ) - }.toMap.asJson + inputs: ListMap[String, InputValue] = ListMap.empty + ) extends Trigger + + object WorkflowDispatch { + implicit def listMapCodec[K: JsonFieldDecoder: JsonFieldEncoder, V: JsonCodec]: JsonCodec[ListMap[K, V]] = + JsonCodec( + JsonEncoder.keyValueIterable[K, V, ListMap], + JsonDecoder.keyValueChunk[K, V].map(c => ListMap(c: _*)) + ) + + implicit lazy val jsonCodec: JsonCodec[WorkflowDispatch] = DeriveJsonCodec.gen[WorkflowDispatch] } case class Release( - releaseTypes: Seq[String] = Seq.empty - ) extends Trigger { - override def toKeyValuePair: (String, Json) = - "release" := Json.obj("types" := releaseTypes) + types: Seq[ReleaseType] = Seq.empty + ) extends Trigger + + object Release { + implicit lazy val jsonCodec: JsonCodec[Release] = DeriveJsonCodec.gen[Release] + } + + sealed trait ReleaseType + object ReleaseType { + case object Created extends ReleaseType + case object Published extends ReleaseType + case object Prereleased extends ReleaseType + + implicit lazy val codec: JsonCodec[ReleaseType] = JsonCodec.string.transformOrFail( + { + case "created" => Right(Created) + case "published" => Right(Published) + case "prereleased" => Right(Prereleased) + case other => Left(s"Invalid release type: $other") + }, + { + case Created => "created" + case Published => "published" + case Prereleased => "prereleased" + } + ) } + @jsonMemberNames(KebabCase) case class PullRequest( + // types: Seq[PullRequestType] = Seq.empty, branches: Seq[Branch] = Seq.empty, - ignoredBranches: Seq[Branch] = Seq.empty - ) extends Trigger { - override def toKeyValuePair: (String, Json) = - "pull_request" := Json.obj( - Seq( - "branches" := branches, - "branches-ignore" := ignoredBranches - ).filter { case (_, data) => data.asArray.exists(_.nonEmpty) }: _* - ) + branchesIgnore: Seq[Branch] = Seq.empty, + paths: Seq[String] = Seq.empty + ) extends Trigger + + object PullRequest { + implicit lazy val jsonCodec: JsonCodec[PullRequest] = DeriveJsonCodec.gen[PullRequest] } case class Push( branches: Seq[Branch] = Seq.empty, - ignoredBranches: Seq[Branch] = Seq.empty - ) extends Trigger { - override def toKeyValuePair: (String, Json) = - "push" := Json.obj( - Seq( - "branches" := branches, - "branches-ignore" := ignoredBranches - ).filter { case (_, data) => data.asArray.exists(_.nonEmpty) }: _* - ) + branchesIgnore: Seq[Branch] = Seq.empty + ) extends Trigger + + object Push { + implicit lazy val jsonCodec: JsonCodec[Push] = DeriveJsonCodec.gen[Push] } case class Create( branches: Seq[Branch] = Seq.empty, - ignoredBranches: Seq[Branch] = Seq.empty - ) extends Trigger { - override def toKeyValuePair: (String, Json) = - "create" := Json.obj( - Seq( - "branches" := branches, - "branches-ignore" := ignoredBranches - ).filter { case (_, data) => data.asArray.exists(_.nonEmpty) }: _* - ) + branchesIgnore: Seq[Branch] = Seq.empty + ) extends Trigger + + object Create { + implicit lazy val jsonCodec: JsonCodec[Create] = DeriveJsonCodec.gen[Create] } } -case class Strategy(matrix: Map[String, List[String]], maxParallel: Option[Int] = None, failFast: Boolean = true) +@jsonMemberNames(KebabCase) +case class Strategy(matrix: ListMap[String, List[String]], maxParallel: Option[Int] = None, failFast: Boolean = true) object Strategy { - implicit val encoder: Encoder[Strategy] = - (s: Strategy) => - Json.obj( - "fail-fast" := s.failFast, - "max-parallel" := s.maxParallel, - "matrix" := s.matrix - ) + import Workflow.listMapCodec + + implicit lazy val codec: JsonCodec[Strategy] = DeriveJsonCodec.gen[Strategy] } case class ActionRef(ref: String) object ActionRef { - implicit val encoder: Encoder[ActionRef] = - (action: ActionRef) => Json.fromString(action.ref) + implicit lazy val codec: JsonCodec[ActionRef] = JsonCodec.string.transform(ActionRef(_), _.ref) } sealed trait Condition { @@ -152,6 +190,10 @@ object Condition { def asString: String = s"$${{ $expression }}" } + object Expression { + implicit lazy val codec: JsonCodec[Expression] = JsonCodec.string.transform(Expression(_), _.asString) + } + case class Function(expression: String) extends Condition { def &&(other: Condition): Condition = throw new IllegalArgumentException("Not supported currently") @@ -162,8 +204,17 @@ object Condition { def asString: String = expression } - implicit val encoder: Encoder[Condition] = - (c: Condition) => Json.fromString(c.asString) + object Function { + implicit lazy val codec: JsonCodec[Function] = JsonCodec.string.transform(Function(_), _.expression) + } + + implicit lazy val codec: JsonCodec[Condition] = JsonCodec.string.transform( + { + case expression if expression.startsWith("${{") => Expression(expression) + case expression => Function(expression) + }, + _.asString + ) } sealed trait Step { @@ -175,17 +226,27 @@ object Step { name: String, id: Option[String] = None, uses: Option[ActionRef] = None, - condition: Option[Condition] = None, - parameters: Map[String, Json] = Map.empty, + `if`: Option[Condition] = None, + `with`: ListMap[String, Json] = ListMap.empty, run: Option[String] = None, - env: Map[String, String] = Map.empty + env: ListMap[String, String] = ListMap.empty ) extends Step { + + @deprecated("Use `if` instead", "0.4.0-alpha.29") + def condition: Option[Condition] = `if` + override def when(condition: Condition): Step = - copy(condition = Some(condition)) + copy(`if` = Some(condition)) override def flatten: Seq[Step.SingleStep] = Seq(this) } + object SingleStep { + import Workflow.listMapCodec + + implicit lazy val codec: JsonCodec[SingleStep] = DeriveJsonCodec.gen[SingleStep] + } + case class StepSequence(steps: Seq[Step]) extends Step { override def when(condition: Condition): Step = copy(steps = steps.map(_.when(condition))) @@ -193,32 +254,24 @@ object Step { override def flatten: Seq[SingleStep] = steps.flatMap(_.flatten) } - - implicit val encoder: Encoder[SingleStep] = - (s: SingleStep) => - Json - .obj( - "name" := s.name, - "id" := s.id, - "uses" := s.uses, - "if" := s.condition, - "with" := (if (s.parameters.nonEmpty) s.parameters.asJson - else Json.Null), - "run" := s.run, - "env" := (if (s.env.nonEmpty) s.env.asJson else Json.Null) - ) } case class ImageRef(ref: String) object ImageRef { - implicit val encoder: Encoder[ImageRef] = - (image: ImageRef) => Json.fromString(image.ref) + implicit lazy val codec: JsonCodec[ImageRef] = JsonCodec.string.transform(ImageRef(_), _.ref) } case class ServicePort(inner: Int, outer: Int) object ServicePort { - implicit val encoder: Encoder[ServicePort] = - (sp: ServicePort) => Json.fromString(s"${sp.inner}:${sp.outer}") + implicit lazy val codec: JsonCodec[ServicePort] = JsonCodec.string.transformOrFail( + v => + Try(v.split(":", 2).map(_.toInt).toList) match { + case Success(inner :: outer :: Nil) => Right(ServicePort(inner.toInt, outer.toInt)) + case Success(_) => Left("Invalid service port format: " + v) + case Failure(_) => Left("Invalid service port format: " + v) + }, + sp => s"${sp.inner}:${sp.outer}" + ) } case class Service( @@ -228,100 +281,104 @@ case class Service( ports: Seq[ServicePort] = Seq.empty ) object Service { - implicit val encoder: Encoder[Service] = - (s: Service) => - Json.obj( - "image" := s.image, - "env" := s.env, - "ports" := s.ports - ) + implicit lazy val codec: JsonCodec[Service] = DeriveJsonCodec.gen[Service] } +@jsonMemberNames(KebabCase) case class Job( - id: String, name: String, runsOn: String = "ubuntu-latest", - timeoutMinutes: Int = 30, + timeoutMinutes: Option[Int] = None, continueOnError: Boolean = false, strategy: Option[Strategy] = None, - steps: Seq[Step] = Seq.empty, - need: Seq[String] = Seq.empty, + needs: Seq[String] = Seq.empty, services: Seq[Service] = Seq.empty, - condition: Option[Condition] = None + `if`: Option[Condition] = None, + steps: Seq[Step] = Seq.empty ) { + + def id: String = name.toLowerCase().replace(" ", "-") + + @deprecated("Use `if` instead", "0.4.0-alpha.29") + def condition: Option[Condition] = `if` + def withStrategy(strategy: Strategy): Job = copy(strategy = Some(strategy)) - def withSteps(steps: Step*): Job = - copy(steps = steps) + def withSteps(steps: Step*): Job = steps match { + case steps: Step.StepSequence => + copy(steps = steps.flatten) + case step: Step.SingleStep => + copy(steps = step :: Nil) + } def withServices(services: Service*): Job = copy(services = services) + + def withRunsOn(runsOn: String): Job = + copy(runsOn = runsOn) + + def withName(name: String): Job = + copy(name = name) + + def withTimeoutMinutes(timeoutMinutes: Option[Int]): Job = + copy(timeoutMinutes = timeoutMinutes) + + def withContinueOnError(continueOnError: Boolean): Job = + copy(continueOnError = continueOnError) + + def withStrategy(strategy: Option[Strategy]): Job = + copy(strategy = strategy) + + def withNeeds(needs: Seq[String]): Job = + copy(needs = needs) } object Job { - implicit val encoder: Encoder[Job] = - (job: Job) => - Json - .obj( - "name" := job.name, - "runs-on" := job.runsOn, - "continue-on-error" := job.continueOnError, - "strategy" := job.strategy, - "needs" := (if (job.need.nonEmpty) job.need.asJson - else Json.Null), - "services" := (if (job.services.nonEmpty) { - Json.obj( - job.services.map(svc => svc.name := svc): _* - ) - } else { - Json.Null - }), - "if" := job.condition, - "steps" := StepSequence(job.steps).flatten - ) + implicit lazy val stepsCodec: JsonCodec[Seq[Step]] = + JsonCodec.seq[Step.SingleStep].transform[Seq[Step]](identity, _.flatMap(_.flatten)) + implicit lazy val codec: JsonCodec[Job] = DeriveJsonCodec.gen[Job] + +} + +@jsonMemberNames(KebabCase) +case class Concurrency( + group: String, + cancelInProgress: Boolean = true +) + +object Concurrency { + implicit lazy val codec: JsonCodec[Concurrency] = DeriveJsonCodec.gen[Concurrency] } case class Workflow( name: String, - env: Map[String, String] = Map.empty, - triggers: Seq[Trigger] = Seq.empty, - jobs: Seq[Job] = Seq.empty + env: ListMap[String, String] = ListMap.empty, + on: Option[Triggers] = None, + concurrency: Concurrency = Concurrency( + "${{ github.workflow }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.run_id || github.ref }}" + ), + jobs: ListMap[String, Job] = ListMap.empty ) { - def on(triggers: Trigger*): Workflow = - copy(triggers = triggers) + def withOn(on: Triggers): Workflow = + copy(on = Some(on)) - def withJobs(jobs: Job*): Workflow = - copy(jobs = jobs) + def withJobs(jobs: (String, Job)*): Workflow = + copy(jobs = ListMap(jobs: _*)) - def addJob(job: Job): Workflow = - copy(jobs = jobs :+ job) + def addJob(job: (String, Job)): Workflow = + copy(jobs = jobs + job) - def addJobs(newJobs: Seq[Job]): Workflow = + def addJobs(newJobs: (String, Job)*): Workflow = copy(jobs = jobs ++ newJobs) } object Workflow { - implicit val encoder: Encoder[Workflow] = - (wf: Workflow) => - Json - .obj( - "name" := wf.name, - "env" := wf.env, - "on" := (if (wf.triggers.isEmpty) - Json.Null - else { - Json.obj( - wf.triggers - .map(_.toKeyValuePair): _* - ) - }), - "concurrency" := Json.obj( - "group" := Json.fromString( - "${{ github.workflow }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.run_id || github.ref }}" - ), - "cancel-in-progress" := true - ), - "jobs" := Json.obj(wf.jobs.map(job => job.id := job): _*) - ) + + implicit def listMapCodec[K: JsonFieldDecoder: JsonFieldEncoder, V: JsonCodec]: JsonCodec[ListMap[K, V]] = + JsonCodec( + JsonEncoder.keyValueIterable[K, V, ListMap], + JsonDecoder.keyValueChunk[K, V].map(c => ListMap(c: _*)) + ) + implicit lazy val codec: JsonCodec[Workflow] = DeriveJsonCodec.gen[Workflow] } diff --git a/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/package.scala b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/package.scala new file mode 100644 index 00000000..112f9c6b --- /dev/null +++ b/zio-sbt-githubactions/src/main/scala/zio/sbt/githubactions/package.scala @@ -0,0 +1,8 @@ +package zio.sbt + +import zio.json.JsonCodecConfiguration + +package object githubactions { + implicit val jsonConfig: JsonCodecConfiguration = + JsonCodecConfiguration.default.copy(explicitEmptyCollections = false) +} diff --git a/zio-sbt-website/build.sbt b/zio-sbt-website/build.sbt index f45bd37d..a899ecea 100644 --- a/zio-sbt-website/build.sbt +++ b/zio-sbt-website/build.sbt @@ -1,6 +1,9 @@ -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.4") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2") -libraryDependencies += "dev.zio" %% "zio" % "2.1.8" -libraryDependencies += "io.circe" %% "circe-yaml" % "0.16.0" +libraryDependencies += "dev.zio" %% "zio" % "2.1.13" +libraryDependencies += "dev.zio" %% "zio-json" % "0.7.3+19-9339fbba-SNAPSHOT" +libraryDependencies += "dev.zio" %% "zio-json-yaml" % "0.7.3+19-9339fbba-SNAPSHOT" + +resolvers ++= Resolver.sonatypeOssRepos("snapshots")