diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bb82d0..89f2405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,272 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Continuous Integration + on: pull_request: branches: ['**', '!update/**', '!pr/**'] push: branches: ['**', '!update/**', '!pr/**'] tags: [v*] + +env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + build: + name: Build and Test + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.12.17] + java: [temurin@11, temurin@17] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Download Java (temurin@11) + id: download-java-temurin-11 + if: matrix.java == 'temurin@11' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 11 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 11 + jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} + + - name: Download Java (temurin@17) + id: download-java-temurin-17 + if: matrix.java == 'temurin@17' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 17 + + - name: Setup Java (temurin@17) + if: matrix.java == 'temurin@17' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 17 + jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Check that workflows are up to date + run: sbt githubWorkflowCheck + + - name: Check headers and formatting + if: matrix.java == 'temurin@11' + run: sbt '++${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck + + - name: Test + run: sbt '++${{ matrix.scala }}' test + + - name: Check scalafix lints + if: matrix.java == 'temurin@11' + run: sbt '++${{ matrix.scala }}' 'scalafixAll --check' + + - name: Check binary compatibility + if: matrix.java == 'temurin@11' + run: sbt '++${{ matrix.scala }}' mimaReportBinaryIssues + + - name: Generate API documentation + if: matrix.java == 'temurin@11' + run: sbt '++${{ matrix.scala }}' doc + + - name: Make target directories + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + run: mkdir -p target site/target core/target project/target + + - name: Compress target directories + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + run: tar cf targets.tar target site/target core/target project/target + + - name: Upload target directories + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + uses: actions/upload-artifact@v2 + with: + name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }} + path: targets.tar + + publish: + name: Publish Artifacts + needs: [build] + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.12.17] + java: [temurin@11] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Download Java (temurin@11) + id: download-java-temurin-11 + if: matrix.java == 'temurin@11' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 11 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 11 + jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} + + - name: Download Java (temurin@17) + id: download-java-temurin-17 + if: matrix.java == 'temurin@17' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 17 + + - name: Setup Java (temurin@17) + if: matrix.java == 'temurin@17' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 17 + jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Download target directories (2.12.17) + uses: actions/download-artifact@v2 + with: + name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.17 + + - name: Inflate target directories (2.12.17) + run: | + tar xf targets.tar + rm targets.tar + + - name: Import signing key + if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == '' + run: echo $PGP_SECRET | base64 -di | gpg --import + + - name: Import signing key and strip passphrase + if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != '' + run: | + echo "$PGP_SECRET" | base64 -di > /tmp/signing-key.gpg + echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg + (echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1) + + - name: Publish + run: sbt '++${{ matrix.scala }}' tlRelease + + site: + name: Generate Site + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.12.17] + java: [temurin@11] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Download Java (temurin@11) + id: download-java-temurin-11 + if: matrix.java == 'temurin@11' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 11 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 11 + jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} + + - name: Download Java (temurin@17) + id: download-java-temurin-17 + if: matrix.java == 'temurin@17' + uses: typelevel/download-java@v1 + with: + distribution: temurin + java-version: 17 + + - name: Setup Java (temurin@17) + if: matrix.java == 'temurin@17' + uses: actions/setup-java@v2 + with: + distribution: jdkfile + java-version: 17 + jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Generate site + run: sbt '++${{ matrix.scala }}' docs/tlSite + + - name: Publish site + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + uses: peaceiris/actions-gh-pages@v3.8.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: site/target/docs/site + keep_files: true diff --git a/.github/workflows/clean.yml b/.github/workflows/clean.yml new file mode 100644 index 0000000..547aaa4 --- /dev/null +++ b/.github/workflows/clean.yml @@ -0,0 +1,59 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Clean + +on: push + +jobs: + delete-artifacts: + name: Delete Artifacts + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Delete artifacts + run: | + # Customize those three lines with your repository and credentials: + REPO=${GITHUB_API_URL}/repos/${{ github.repository }} + + # A shortcut to call GitHub API. + ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } + + # A temporary file which receives HTTP response headers. + TMPFILE=/tmp/tmp.$$ + + # An associative array, key: artifact name, value: number of artifacts of that name. + declare -A ARTCOUNT + + # Process all artifacts on this repository, loop on returned "pages". + URL=$REPO/actions/artifacts + while [[ -n "$URL" ]]; do + + # Get current page, get response headers in a temporary file. + JSON=$(ghapi --dump-header $TMPFILE "$URL") + + # Get URL of next page. Will be empty if we are at the last page. + URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') + rm -f $TMPFILE + + # Number of artifacts on this page: + COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) + + # Loop on all artifacts on this page. + for ((i=0; $i < $COUNT; i++)); do + + # Get name of artifact and count instances of this name. + name=$(jq <<<$JSON -r ".artifacts[$i].name?") + ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) + + id=$(jq <<<$JSON -r ".artifacts[$i].id?") + size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) + printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size + ghapi -X DELETE $REPO/actions/artifacts/$id + done + done diff --git a/.gitignore b/.gitignore index 9c07d4a..eca1de3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ -*.class -*.log +target/ +.idea/ +.vscode +.bloop +.metals +project/metals.sbt +project/project +.bsp/ diff --git a/.scala-steward.conf b/.scala-steward.conf new file mode 100644 index 0000000..ab234af --- /dev/null +++ b/.scala-steward.conf @@ -0,0 +1,3 @@ +updates.pin = [ + { groupId = "org.scala-lang", artifactId = "scala-library", version = "2.12." } +] diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 0000000..edf55ae --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,3 @@ +rules = [ + OrganizeImports +] diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..a7c8cc5 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,21 @@ +version = 3.5.3 +project.git = true +runner.dialect = scala213 +project.layout = StandardConvention +continuationIndent.defnSite = 2 +docstrings.style = Asterisk +docstrings.wrap = no +includeCurlyBraceInSelectChains = false +maxColumn = 120 +newlines.alwaysBeforeElseAfterCurlyIf = false +newlines.alwaysBeforeMultilineDef = false +optIn.breakChainOnFirstMethodDot = false +spaces.inImportCurlyBraces = true +rewrite.rules = [ + AvoidInfix, + RedundantParens, + AsciiSortImports, + PreferCurlyFors +] +newlines.afterCurlyLambda = preserve +newlines.beforeCurlyLambdaParams = multilineWithCaseOnly diff --git a/README.md b/README.md index cc2792f..2dc4843 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ # sbt-circe-org -An SBT plugin to support circe org projects + +This project is intended to set up sensible defaults and standards for projects under `io.circe`. If it's useful +outside `io.circe`, it probably should be pushed further upstream +into [sbt-typelevel](https://github.com/typelevel/sbt-typelevel). + +## Installing it + +The plugin is deployed to Sonatype. + +```scala +addSbtPlugin("io.circe" % "sbt-circe-org" % "@VERSION@") +``` + +## What does it do? + +### `CirceOrgPlugin` + +Triggers automatically. Use on all published `circe` modules. + +* Activates the sensible defaults of [sbt-typelevel](https://github.com/typelevel/sbt-typelevel/) +* Sets the organization info +* Enables automated license headers with appropriate years +* Enables [sbt-github-actions](https://typelevel.org/sbt-typelevel/gha.html) with settings appropriate to our + workflow +* by default does not run code coverage in all `io.circe.examples.*` packages + +### `CirceOrgSitePlugin` + +Must be manually triggered. Extends the `TypelevelSitePlugin` with the `circe.io` theme. See +the [sbt-typelevel-site docs](https://typelevel.org/sbt-typelevel/site.html) for usage. + +#### Provided images + +You can refer to the following images in customizing the `laika` theme: + +- `Path.Root / "images" / "circe_dark.svg"` +- `Path.Root / "images" / "circe_light_no_border.svg"` +- `Path.Root / "images" / "circe_light_no_border_146x173.png"` diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..830599d --- /dev/null +++ b/build.sbt @@ -0,0 +1,27 @@ +lazy val `sbt-circe-org` = + project.in(file(".")).enablePlugins(NoPublishPlugin).aggregate(core) + +// we're not doing code coverage on this plugin. Set to Some("sbt-circe-org") if you wish to run it. +ThisBuild / circeRootOfCodeCoverage := None +ThisBuild / tlCiScalafixCheck := true + +lazy val docs = project + .in(file("site")) + .enablePlugins(CirceOrgSitePlugin) + .settings( + tlSiteRelatedProjects += "sbt-typelevel" -> url("https://typelevel.org/sbt-typelevel/") + ) + +lazy val core = project + .in(file("core")) + .enablePlugins(SbtPlugin) + .settings( + name := "sbt-circe-org" + ) + +ThisBuild / tlBaseVersion := "0.1" +ThisBuild / crossScalaVersions := Seq("2.12.17") +ThisBuild / developers := List( + tlGitHubDev("lorandszakacs", "Loránd Szakács") +) +ThisBuild / startYear := Some(2022) diff --git a/core/build.sbt b/core/build.sbt new file mode 100644 index 0000000..0a18b18 --- /dev/null +++ b/core/build.sbt @@ -0,0 +1,9 @@ +val sbtTypelevelVersion = "0.4.16" // https://github.com/typelevel/sbt-typelevel/releases +val sbtScoverageVersion = "2.0.6" // https://github.com/scoverage/sbt-scoverage/releases +val sbtScalaNativeVersion = "0.4.7" // https://github.com/scala-native/scala-native/releases + +addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion) +addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTypelevelVersion) +addSbtPlugin("org.typelevel" % "sbt-typelevel-scalafix" % sbtTypelevelVersion) +addSbtPlugin("org.scala-native" % "sbt-scala-native" % sbtScalaNativeVersion) +addSbtPlugin("org.scoverage" % "sbt-scoverage" % sbtScoverageVersion) diff --git a/core/src/main/resources/io/circe/sbt/site/images/circe_dark.svg b/core/src/main/resources/io/circe/sbt/site/images/circe_dark.svg new file mode 100644 index 0000000..1c8bbbd --- /dev/null +++ b/core/src/main/resources/io/circe/sbt/site/images/circe_dark.svg @@ -0,0 +1,16 @@ + + + + circe + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border.svg b/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border.svg new file mode 100644 index 0000000..b0de429 --- /dev/null +++ b/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border.svg @@ -0,0 +1,13 @@ + + + + circe + Created with Sketch. + + + + + + + + diff --git a/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border_146x173.png b/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border_146x173.png new file mode 100644 index 0000000..2bcc604 Binary files /dev/null and b/core/src/main/resources/io/circe/sbt/site/images/circe_light_no_border_146x173.png differ diff --git a/core/src/main/resources/io/circe/sbt/site/site/styles.css b/core/src/main/resources/io/circe/sbt/site/site/styles.css new file mode 100644 index 0000000..fad1e6c --- /dev/null +++ b/core/src/main/resources/io/circe/sbt/site/site/styles.css @@ -0,0 +1,5 @@ +header img { + height: 28px; + width: auto; + margin-top: 6px; +} diff --git a/core/src/main/scala/io/circe/sbt/CirceOrgPlugin.scala b/core/src/main/scala/io/circe/sbt/CirceOrgPlugin.scala new file mode 100644 index 0000000..e3f803b --- /dev/null +++ b/core/src/main/scala/io/circe/sbt/CirceOrgPlugin.scala @@ -0,0 +1,109 @@ +/* + * Copyright 2022 circe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.circe.sbt + +import org.typelevel.sbt._ +import org.typelevel.sbt.gha._ +import sbt.Keys._ +import sbt._ +import scalafix.sbt.ScalafixPlugin.autoImport._ +import scoverage.ScoverageSbtPlugin.autoImport._ +import scoverage._ + +import GenerativeKeys._ +import TypelevelSettingsPlugin.autoImport._ +import TypelevelCiPlugin.autoImport._ +import TypelevelSonatypePlugin.autoImport._ + +object CirceOrgPlugin extends AutoPlugin { + object autoImport { + lazy val circeRootOfCodeCoverage = settingKey[Option[String]]( + "None if code coverage is disabled. Defined with name of the root project from which to run code coverage." + ) + } + + import autoImport._ + + override def trigger = allRequirements + + override def requires: Plugins = TypelevelPlugin && ScoverageSbtPlugin + + override def buildSettings: Seq[Setting[_]] = + codeCoverageSettings ++ publishSettings ++ organizationSettings ++ scalafixSettings ++ githubActionsSettings + + override def projectSettings: Seq[Setting[_]] = Seq.empty + + lazy val codeCoverageSettings: Seq[Setting[_]] = Seq( + coverageHighlighting := true, + coverageExcludedPackages := "io.circe.examples.*" + ) + + lazy val publishSettings: Seq[Setting[_]] = + Seq( + tlSonatypeUseLegacyHost := true, + tlJdkRelease := Some(8) + ) + + lazy val organizationSettings: Seq[Setting[_]] = + Seq( + organization := "io.circe", + organizationName := "circe" + ) + + @scala.annotation.nowarn("msg=possible missing interpolator") + lazy val githubActionsSettings: Seq[Setting[_]] = Seq( + githubWorkflowJavaVersions := List("11", "17").map(JavaSpec.temurin(_)), + tlCiScalafixCheck := true, // yolo, let's see how it works on scala 3 :D + tlCiScalafmtCheck := true, + githubWorkflowAddedJobs ++= + (circeRootOfCodeCoverage.value match { + case None => List.empty + case Some(rootProj) => + List( + WorkflowJob( + id = "coverage", + name = "Generate coverage report", + scalas = crossScalaVersions.value.filterNot(_.startsWith("3.")).toList, + steps = List(WorkflowStep.Checkout) ++ WorkflowStep.SetupJava( + List(githubWorkflowJavaVersions.value.last) + ) ++ githubWorkflowGeneratedCacheSteps.value ++ List( + WorkflowStep.Sbt(List("coverage", s"$rootProj/test", "coverageAggregate")), + WorkflowStep.Use( + UseRef.Public( + "codecov", + "codecov-action", + "v2" + ), + params = Map( + "flags" -> List("${{matrix.scala}}", "${{matrix.java}}").mkString(",") + ) + ) + ) + ) + ) + }) + ) + + lazy val scalafixSettings: Seq[Setting[_]] = + Seq( + scalafixScalaBinaryVersion := (LocalRootProject / scalaBinaryVersion).value, + scalafixDependencies ++= Seq( + "com.github.liancheng" %% "organize-imports" % "0.6.0" // https://github.com/liancheng/scalafix-organize-imports/tags + ) + ) + +} diff --git a/core/src/main/scala/io/circe/sbt/CirceOrgScalaJSPlugin.scala b/core/src/main/scala/io/circe/sbt/CirceOrgScalaJSPlugin.scala new file mode 100644 index 0000000..8c7c78e --- /dev/null +++ b/core/src/main/scala/io/circe/sbt/CirceOrgScalaJSPlugin.scala @@ -0,0 +1,37 @@ +/* + * Copyright 2022 circe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.circe.sbt + +/** + * automatically loaded for all projects that are cross-compiled to ScalaJS, no need too reference it explicitly in builds + */ + +import org.scalajs.sbtplugin.ScalaJSPlugin +import sbt.* +import scoverage.ScoverageSbtPlugin.autoImport._ + +object CirceOrgScalaJSPlugin extends AutoPlugin { + + override def trigger = allRequirements + + override def requires: Plugins = CirceOrgPlugin && ScalaJSPlugin + + override def projectSettings: Seq[Setting[_]] = Seq( + coverageEnabled := false + ) + +} diff --git a/core/src/main/scala/io/circe/sbt/CirceOrgScalaNativePlugin.scala b/core/src/main/scala/io/circe/sbt/CirceOrgScalaNativePlugin.scala new file mode 100644 index 0000000..18fb0f3 --- /dev/null +++ b/core/src/main/scala/io/circe/sbt/CirceOrgScalaNativePlugin.scala @@ -0,0 +1,37 @@ +/* + * Copyright 2022 circe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.circe.sbt + +/** + * automatically loaded for all projects that are cross-compiled to ScalaJS, no need too reference it explicitly in builds + */ +import sbt.* +import scoverage.ScoverageSbtPlugin.autoImport._ + +import scala.scalanative.sbtplugin.ScalaNativePlugin + +object CirceOrgScalaNativePlugin extends AutoPlugin { + + override def trigger = allRequirements + + override def requires: Plugins = CirceOrgPlugin && ScalaNativePlugin + + override def projectSettings: Seq[Setting[_]] = Seq( + coverageEnabled := false + ) + +} diff --git a/core/src/main/scala/io/circe/sbt/CirceOrgSitePlugin.scala b/core/src/main/scala/io/circe/sbt/CirceOrgSitePlugin.scala new file mode 100644 index 0000000..dcb44b2 --- /dev/null +++ b/core/src/main/scala/io/circe/sbt/CirceOrgSitePlugin.scala @@ -0,0 +1,136 @@ +/* + * Copyright 2022 circe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.circe.sbt + +import laika.ast.LengthUnit._ +import laika.ast.Path.Root +import laika.ast._ +import laika.helium.Helium +import laika.helium.config.Favicon +import laika.helium.config.HeliumIcon +import laika.helium.config.IconLink +import laika.helium.config.ImageLink +import laika.sbt.LaikaPlugin +import laika.theme.config.Color +import org.typelevel.sbt.TypelevelGitHubPlugin +import org.typelevel.sbt.TypelevelSitePlugin +import sbt.Keys._ +import sbt._ + +object CirceOrgSitePlugin extends AutoPlugin { + + override def requires = TypelevelSitePlugin && LaikaPlugin + + import TypelevelGitHubPlugin.autoImport._ + import TypelevelSitePlugin.autoImport._ + import LaikaPlugin.autoImport._ + + override def projectSettings: Seq[Setting[_]] = Seq( + tlSiteRelatedProjects := { + Seq( + "circe" -> url("https://github.com/circe/circe"), + // streaming helpers + "circe-fs2" -> url("https://github.com/circe/circe-fs2"), + "circe-iteratee" -> url("https://github.com/circe/circe-iteratee"), + // derivation helpers + "circe-generic-extras" -> url("https://github.com/circe/circe-generic-extras"), + "circe-derivation" -> url("https://github.com/circe/circe-derivation"), + // end-user utils + "circe-optics" -> url("https://github.com/circe/circe-optics"), + // 3rd-party integrations + "circe-droste" -> url("https://github.com/circe/circe-droste"), + "circe-spire" -> url("https://github.com/circe/circe-spire"), + "circe-config" -> url("https://github.com/circe/circe-config"), + // other formats + "circe-yaml" -> url("https://github.com/circe/circe-yaml"), + "circe-bson" -> url("https://github.com/circe/circe-bson"), + // schemas + "circe-schema" -> url("https://github.com/circe/circe-schema"), + "circe-golden" -> url("https://github.com/circe/circe-golden"), + "circe-json-schema" -> url("https://github.com/circe/circe-json-schema"), + // other backends + "circe-jackson" -> url("https://github.com/circe/circe-jackson"), + "circe-argus" -> url("https://github.com/circe/circe-argus") + ).filterNot { + case (repo, _) => + tlGitHubRepo.value.contains(repo) // omit ourselves! + } + }, + laikaTheme ~= { _.extend(site.CirceOrgHeliumExtensions) }, + tlSiteHeliumConfig := { + Helium.defaults.all + .metadata( + title = tlGitHubRepo.value, + authors = developers.value.map(_.name), + language = Some("en"), + version = Some(version.value) + ) + .site + .layout( + contentWidth = px(860), + navigationWidth = px(275), + topBarHeight = px(35), + defaultBlockSpacing = px(10), + defaultLineHeight = 1.5, + anchorPlacement = laika.helium.config.AnchorPlacement.Right + ) + .site + /* See scaladoc on laika.helium.config.CommonConfigOps#themeColors() + * old circe website color theme reference point: + * https://github.com/circe/circe/blob/c666f32f3bd02644a927b624f7534fdeccbd62a6/build.sbt#L151 + */ + .themeColors( + primary = Color.hex("222749"), + secondary = Color.hex("222749"), + primaryMedium = Color.hex("292E53"), + primaryLight = Color.hex("5B5988"), + text = Color.hex("5f5f5f"), + background = Color.hex("F4F3F4"), + bgGradient = (Color.hex("F4F3F4"), Color.hex("E5E5E6")) + ) + .site + .favIcons( + Favicon.internal(Root / "images" / "circe_light_no_border_146x173.png", "32x32") + ) + .site + .darkMode + .disabled + .site + .topNavigationBar( + homeLink = ImageLink.external( + "https://github.com/circe/circe", + Image.internal(Root / "images" / "circe_light_no_border_146x173.png") + ), + navLinks = tlSiteApiUrl.value.toList.map { url => + IconLink.external( + url.toString, + HeliumIcon.api, + options = Styles("svg-link") + ) + } ++ Seq( + IconLink.external( + scmInfo.value.fold("https://github.com/circe")(_.browseUrl.toString), + HeliumIcon.github, + options = Styles("svg-link") + ), + IconLink.external("https://discord.gg/XF3CXcMzqD", HeliumIcon.chat) + ) + ) + } + ) + +} diff --git a/core/src/main/scala/io/circe/sbt/site/CirceOrgHeliumExtensions.scala b/core/src/main/scala/io/circe/sbt/site/CirceOrgHeliumExtensions.scala new file mode 100644 index 0000000..c455f5f --- /dev/null +++ b/core/src/main/scala/io/circe/sbt/site/CirceOrgHeliumExtensions.scala @@ -0,0 +1,57 @@ +/* + * Copyright 2022 circe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.circe.sbt.site + +import cats.effect.Resource +import cats.effect.kernel.Sync +import laika.ast.Path +import laika.io.model.InputTree +import laika.theme.Theme +import laika.theme.ThemeBuilder +import laika.theme.ThemeProvider + +object CirceOrgHeliumExtensions extends ThemeProvider { + + override def build[F[_]](implicit F: Sync[F]): Resource[F, Theme[F]] = + ThemeBuilder[F]("Circe Helium Extensions") + .addInputs( + InputTree[F].addStream( + F.blocking(getClass.getResourceAsStream("site/styles.css")), + Path.Root / "site" / "styles.css" + ) + ) + .addInputs( + InputTree[F].addStream( + F.blocking(getClass.getResourceAsStream("images/circe_dark.svg")), + Path.Root / "images" / "circe_dark.svg" + ) + ) + .addInputs( + InputTree[F].addStream( + F.blocking(getClass.getResourceAsStream("images/circe_light_no_border.svg")), + Path.Root / "images" / "circe_light_no_border.svg" + ) + ) + .addInputs( + InputTree[F].addStream( + F.blocking(getClass.getResourceAsStream("images/circe_light_no_border_146x173.png")), + Path.Root / "images" / "circe_light_no_border_146x173.png" + ) + ) + .build + +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2dc4843 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,39 @@ +# sbt-circe-org + +This project is intended to set up sensible defaults and standards for projects under `io.circe`. If it's useful +outside `io.circe`, it probably should be pushed further upstream +into [sbt-typelevel](https://github.com/typelevel/sbt-typelevel). + +## Installing it + +The plugin is deployed to Sonatype. + +```scala +addSbtPlugin("io.circe" % "sbt-circe-org" % "@VERSION@") +``` + +## What does it do? + +### `CirceOrgPlugin` + +Triggers automatically. Use on all published `circe` modules. + +* Activates the sensible defaults of [sbt-typelevel](https://github.com/typelevel/sbt-typelevel/) +* Sets the organization info +* Enables automated license headers with appropriate years +* Enables [sbt-github-actions](https://typelevel.org/sbt-typelevel/gha.html) with settings appropriate to our + workflow +* by default does not run code coverage in all `io.circe.examples.*` packages + +### `CirceOrgSitePlugin` + +Must be manually triggered. Extends the `TypelevelSitePlugin` with the `circe.io` theme. See +the [sbt-typelevel-site docs](https://typelevel.org/sbt-typelevel/site.html) for usage. + +#### Provided images + +You can refer to the following images in customizing the `laika` theme: + +- `Path.Root / "images" / "circe_dark.svg"` +- `Path.Root / "images" / "circe_light_no_border.svg"` +- `Path.Root / "images" / "circe_light_no_border_146x173.png"` diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..563a014 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.7.2 diff --git a/project/build.sbt b/project/build.sbt new file mode 120000 index 0000000..f24fe19 --- /dev/null +++ b/project/build.sbt @@ -0,0 +1 @@ +../core/build.sbt \ No newline at end of file diff --git a/project/src b/project/src new file mode 120000 index 0000000..5bc79fa --- /dev/null +++ b/project/src @@ -0,0 +1 @@ +../core/src \ No newline at end of file