From a01f200ab362eb7d7720fb7c4835017a19fea663 Mon Sep 17 00:00:00 2001 From: Connor James Smith Date: Thu, 21 Dec 2023 19:25:09 -0500 Subject: [PATCH 1/2] Add scalafix rules for 0.4.x -> 0.5.x (cherry picked from commit 9ea606f3387d24f75cac88d175950ff3257c2f65) --- scalafix/build.sbt | 105 ++++++++++++++++++ .../input/src/main/scala/fix/Fs2Pgp.scala | 58 ++++++++++ .../fix/Fs2PgpSignificantIndentation.scala | 5 + .../output/src/main/scala/fix/Fs2Pgp.scala | 56 ++++++++++ scalafix/project/TargetAxis.scala | 47 ++++++++ scalafix/project/build.properties | 1 + scalafix/project/plugins.sbt | 3 + scalafix/readme.md | 7 ++ .../META-INF/services/scalafix.v1.Rule | 1 + .../rules/src/main/scala/fix/Fs2Pgp.scala | 34 ++++++ .../tests/src/test/scala/fix/RuleSuite.scala | 8 ++ 11 files changed, 325 insertions(+) create mode 100644 scalafix/build.sbt create mode 100644 scalafix/input/src/main/scala/fix/Fs2Pgp.scala create mode 100644 scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala create mode 100644 scalafix/output/src/main/scala/fix/Fs2Pgp.scala create mode 100644 scalafix/project/TargetAxis.scala create mode 100644 scalafix/project/build.properties create mode 100644 scalafix/project/plugins.sbt create mode 100644 scalafix/readme.md create mode 100644 scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule create mode 100644 scalafix/rules/src/main/scala/fix/Fs2Pgp.scala create mode 100644 scalafix/tests/src/test/scala/fix/RuleSuite.scala diff --git a/scalafix/build.sbt b/scalafix/build.sbt new file mode 100644 index 0000000..7d223f6 --- /dev/null +++ b/scalafix/build.sbt @@ -0,0 +1,105 @@ +lazy val V = _root_.scalafix.sbt.BuildInfo + +lazy val rulesCrossVersions = Seq(V.scala213) +lazy val scala3Version = "3.3.1" + +inThisBuild( + List( + organization := "com.dwolla", + homepage := Some(url("https://github.com/dwolla/fs2-pgp")), + licenses := List( + "Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0") + ), + developers := List( + Developer( + "cjsmith-0141", + "CJ Smith", + "connor.smith1@octoenergy.com", + url("https://kraken.tech") + ) + ), + semanticdbEnabled := true, + semanticdbVersion := scalafixSemanticdb.revision + ) +) + +lazy val `fs2-pgp` = (project in file(".")) + .aggregate( + rules.projectRefs ++ + input.projectRefs ++ + output.projectRefs ++ + tests.projectRefs: _* + ) + .settings( + publish / skip := true + ) + +lazy val rules = projectMatrix + .settings( + moduleName := "scalafix", + libraryDependencies ++= Seq( + "ch.epfl.scala" %% "scalafix-core" % V.scalafixVersion, + ) + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(rulesCrossVersions) + +lazy val input = projectMatrix + .settings( + publish / skip := true, + libraryDependencies ++= Seq( + "com.dwolla" %% "fs2-pgp" % "0.4.3", + "org.typelevel" %% "log4cats-noop" % "2.5.0" + ) + + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions) + +lazy val output = projectMatrix + .settings( + publish / skip := true, + libraryDependencies ++= Seq( + // TODO: This feels like a chicken/egg problem + // do we cut a release before merging this in? + "net.tazato" %% "fs2-pgp" % "0.5.0", + "org.typelevel" %% "log4cats-noop" % "2.5.0" + ) + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions :+ scala3Version) + +lazy val testsAggregate = Project("tests", file("target/testsAggregate")) + .aggregate(tests.projectRefs: _*) + .settings( + publish / skip := true + ) + +lazy val tests = projectMatrix + .settings( + publish / skip := true, + scalafixTestkitOutputSourceDirectories := + TargetAxis + .resolve(output, Compile / unmanagedSourceDirectories) + .value, + scalafixTestkitInputSourceDirectories := + TargetAxis + .resolve(input, Compile / unmanagedSourceDirectories) + .value, + scalafixTestkitInputClasspath := + TargetAxis.resolve(input, Compile / fullClasspath).value, + scalafixTestkitInputScalacOptions := + TargetAxis.resolve(input, Compile / scalacOptions).value, + scalafixTestkitInputScalaVersion := + TargetAxis.resolve(input, Compile / scalaVersion).value + ) + .defaultAxes( + rulesCrossVersions.map(VirtualAxis.scalaABIVersion) :+ VirtualAxis.jvm: _* + ) + .jvmPlatform( + scalaVersions = Seq(V.scala213), + axisValues = Seq(TargetAxis(V.scala213)), + settings = Seq() + ) + .dependsOn(rules) + .enablePlugins(ScalafixTestkitPlugin) diff --git a/scalafix/input/src/main/scala/fix/Fs2Pgp.scala b/scalafix/input/src/main/scala/fix/Fs2Pgp.scala new file mode 100644 index 0000000..fd114fe --- /dev/null +++ b/scalafix/input/src/main/scala/fix/Fs2Pgp.scala @@ -0,0 +1,58 @@ +package fix +/* +rule = Fs2Pgp +*/ +import cats.effect._ +import cats.syntax.all._ +import cats.effect.unsafe.implicits.global +import org.typelevel.log4cats._ +import fs2._ +import fs2.text._ +import com.dwolla.security.crypto._ +import eu.timepit.refined.types.numeric.PosInt +object Fs2Pgp { + implicit val lf: LoggerFactory[IO] = org.typelevel.log4cats.noop.NoOpFactory.impl[IO] + val key = + """-----BEGIN PGP PUBLIC KEY BLOCK----- + |Version: GnuPG v1 + | + |mQENBFVoyeYBCACy0S/9y/c/CpoYLL6aD3TMCV1Pe/0jcWN0ykULf9l4znYODZLr + |f10BGAJETj9ghrJCNXMib2ogz0wo43KVAp9o3mkg01vVyqs1rzM5jw+yCZmyGPFf + |GsE2lxZFMX+rS0dyq2w0FQN2IjYsELwIFeQ02GXTLyTlhY+u5wwCXo4e7AEXaEo7 + |jl8129NA46gf6l+6lUMyFpKnunO7L4W5rCCrIizP4Fmll1adYfClSX6cztIfz4vg + |Fs2HuViPin5y8THodkg9cIkCfyNHivEbfBbx0xfx67BCwxFcYgF/84H8TASRhjRl + |4s1fZDA7rETWDJIcC+neNV/qtF0kY1ECSd3nABEBAAG0IFRlc3QgVXNlciA8ZnJl + |ZCt0ZXN0QGR3b2xsYS5jb20+iQE4BBMBAgAiBQJVaMnmAhsDBgsJCAcDAgYVCAIJ + |CgsEFgIDAQIeAQIXgAAKCRA2OYfNakCqV1bYB/9QNR5DN5J27Z4DIGoOto/PuVvs + |bQHZj8NLcvIZL1cUyKOg+oRICq2z4BXHAMqyouhs/GLiR5P74I9cJTSIudAvBhwi + |du9AcMQy+Qg3K1rUQGlNU+iamD8DFNUhLoK+Oicij0Mw4TSWBsoR3+Pg/jZ5SDUc + |dUsGGaBJthYoiJR8vZ6Uf9oCn+mpVhrso0zemBDud4AHKaVa+8o7VUWGa6jeyRHX + |RKVbHn7GGYiHZkl+qfpthcxyPHOIkZo+t8GVTItLpvVuU+X36N70+rIzXj5t8NDZ + |KfD3M4p6BSq6Cu6DtJOZ1F28hwaWiRoCdbPrJfW33fo1RxLB6+nLf/ttYGmhuQEN + |BFVoyeYBCADiZfKA98YQip/kvj5rBS2ilQDycBX7Ls2IftuwzO6Q9QSF2lDiz708 + |zyvg0czQPZYaZkFgziZEmjbvOc7hDG+icVWRLCjCcZk4i2TXy7bGcTZmBQ31iVMJ + |ia7GxsJhu4ngrP15pZakAYcCwEk3QH17TdhOwvV8ixHmv9USCMJyiNnuhVAP2tY/ + |Ef0EoCV6qAMoP3dNPT30sFI8+55Ce9yAtWQItT5q4vYOmC9Q34XtSxvpLsLzVByd + |rdvgXe0acjvMiTGcYBdjitawFYeLuz2s5mQAi4X1vcJqxBSBjG7X+1PiDqFFIid3 + |+6rIQtR3ho+Xqz/ucGglKxtn6m49wMHJABEBAAGJAR8EGAECAAkFAlVoyeYCGwwA + |CgkQNjmHzWpAqldxFgf/SZIT1AiBAOLkqdWEObg0cU7n1YOXbj56sUeUCFxdbnl9 + |V2paf2SaMB6EEGLTk9PN0GG3hPyDkl4O6w3mn2J46uP8ecVaNvTSxoq2OmkMmD1H + |/OSnF8a/jB6R1ODiAwekVuUMtAS7JiaAAcKcenG1f0XRKwQs52uavGXPgUuJbVtK + |bB0SyLBhvGG8YIWTXRMHoJRt/Ls4JEuYaoBYqfV2eDn4WhW1LVuXP13gXixy0RiV + |8rHs9aH8BAU7Dy0BBnaS3R9m8vtfdFxMI3/+1iGt0+xh/B4w++9oFE2DgyoZXUF8 + |mbjKYhiRPKNoj6Rn/mHUGcnuPlKvKP+1X5bObpDbQQ== + |=TJUS + |-----END PGP PUBLIC KEY BLOCK-----""".stripMargin + val wrappedKey = PGPKeyAlg[IO].readPublicKey(key).unsafeRunSync() + val pos = PosInt(100) + val chunkSize = tagChunkSize(PosInt(100)) + val chunkSize2 = tagChunkSize(pos) + (for { + crypto <- Stream.resource(CryptoAlg[IO]) + output <- Stream.emit("hello world") + .through(utf8.encode) + .through(crypto.encrypt(wrappedKey, fileName = "BigMoney.txt".some)) + .through(crypto.armor()) + .through(utf8.decode) + } yield output).compile.string.unsafeRunSync() +} \ No newline at end of file diff --git a/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala b/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala new file mode 100644 index 0000000..de4ae1c --- /dev/null +++ b/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala @@ -0,0 +1,5 @@ +package fix + +object Fs2PgpSignificantIndentation: + val a = 1 + // Add code that needs fixing here. diff --git a/scalafix/output/src/main/scala/fix/Fs2Pgp.scala b/scalafix/output/src/main/scala/fix/Fs2Pgp.scala new file mode 100644 index 0000000..1cd6315 --- /dev/null +++ b/scalafix/output/src/main/scala/fix/Fs2Pgp.scala @@ -0,0 +1,56 @@ +package fix + +import cats.effect._ +import cats.syntax.all._ +import cats.effect.unsafe.implicits.global +import org.typelevel.log4cats._ +import fs2._ +import fs2.text._ +import com.dwolla.security.crypto._ +import eu.timepit.refined.types.numeric.PosInt +object Fs2Pgp { + implicit val lf: LoggerFactory[IO] = org.typelevel.log4cats.noop.NoOpFactory.impl[IO] + val key = + """-----BEGIN PGP PUBLIC KEY BLOCK----- + |Version: GnuPG v1 + | + |mQENBFVoyeYBCACy0S/9y/c/CpoYLL6aD3TMCV1Pe/0jcWN0ykULf9l4znYODZLr + |f10BGAJETj9ghrJCNXMib2ogz0wo43KVAp9o3mkg01vVyqs1rzM5jw+yCZmyGPFf + |GsE2lxZFMX+rS0dyq2w0FQN2IjYsELwIFeQ02GXTLyTlhY+u5wwCXo4e7AEXaEo7 + |jl8129NA46gf6l+6lUMyFpKnunO7L4W5rCCrIizP4Fmll1adYfClSX6cztIfz4vg + |Fs2HuViPin5y8THodkg9cIkCfyNHivEbfBbx0xfx67BCwxFcYgF/84H8TASRhjRl + |4s1fZDA7rETWDJIcC+neNV/qtF0kY1ECSd3nABEBAAG0IFRlc3QgVXNlciA8ZnJl + |ZCt0ZXN0QGR3b2xsYS5jb20+iQE4BBMBAgAiBQJVaMnmAhsDBgsJCAcDAgYVCAIJ + |CgsEFgIDAQIeAQIXgAAKCRA2OYfNakCqV1bYB/9QNR5DN5J27Z4DIGoOto/PuVvs + |bQHZj8NLcvIZL1cUyKOg+oRICq2z4BXHAMqyouhs/GLiR5P74I9cJTSIudAvBhwi + |du9AcMQy+Qg3K1rUQGlNU+iamD8DFNUhLoK+Oicij0Mw4TSWBsoR3+Pg/jZ5SDUc + |dUsGGaBJthYoiJR8vZ6Uf9oCn+mpVhrso0zemBDud4AHKaVa+8o7VUWGa6jeyRHX + |RKVbHn7GGYiHZkl+qfpthcxyPHOIkZo+t8GVTItLpvVuU+X36N70+rIzXj5t8NDZ + |KfD3M4p6BSq6Cu6DtJOZ1F28hwaWiRoCdbPrJfW33fo1RxLB6+nLf/ttYGmhuQEN + |BFVoyeYBCADiZfKA98YQip/kvj5rBS2ilQDycBX7Ls2IftuwzO6Q9QSF2lDiz708 + |zyvg0czQPZYaZkFgziZEmjbvOc7hDG+icVWRLCjCcZk4i2TXy7bGcTZmBQ31iVMJ + |ia7GxsJhu4ngrP15pZakAYcCwEk3QH17TdhOwvV8ixHmv9USCMJyiNnuhVAP2tY/ + |Ef0EoCV6qAMoP3dNPT30sFI8+55Ce9yAtWQItT5q4vYOmC9Q34XtSxvpLsLzVByd + |rdvgXe0acjvMiTGcYBdjitawFYeLuz2s5mQAi4X1vcJqxBSBjG7X+1PiDqFFIid3 + |+6rIQtR3ho+Xqz/ucGglKxtn6m49wMHJABEBAAGJAR8EGAECAAkFAlVoyeYCGwwA + |CgkQNjmHzWpAqldxFgf/SZIT1AiBAOLkqdWEObg0cU7n1YOXbj56sUeUCFxdbnl9 + |V2paf2SaMB6EEGLTk9PN0GG3hPyDkl4O6w3mn2J46uP8ecVaNvTSxoq2OmkMmD1H + |/OSnF8a/jB6R1ODiAwekVuUMtAS7JiaAAcKcenG1f0XRKwQs52uavGXPgUuJbVtK + |bB0SyLBhvGG8YIWTXRMHoJRt/Ls4JEuYaoBYqfV2eDn4WhW1LVuXP13gXixy0RiV + |8rHs9aH8BAU7Dy0BBnaS3R9m8vtfdFxMI3/+1iGt0+xh/B4w++9oFE2DgyoZXUF8 + |mbjKYhiRPKNoj6Rn/mHUGcnuPlKvKP+1X5bObpDbQQ== + |=TJUS + |-----END PGP PUBLIC KEY BLOCK-----""".stripMargin + val wrappedKey = PGPKeyAlg[IO].readPublicKey(key).unsafeRunSync() + val pos = PosInt.unsafeFrom(100) + val chunkSize = ChunkSize(PosInt.unsafeFrom(100)) + val chunkSize2 = ChunkSize(pos) + (for { + crypto <- Stream.resource(CryptoAlg.resource[IO]) + output <- Stream.emit("hello world") + .through(utf8.encode) + .through(crypto.encrypt(EncryptionConfig().withFileName("BigMoney.txt".some), wrappedKey)) + .through(crypto.armor) + .through(utf8.decode) + } yield output).compile.string.unsafeRunSync() +} diff --git a/scalafix/project/TargetAxis.scala b/scalafix/project/TargetAxis.scala new file mode 100644 index 0000000..ee36836 --- /dev/null +++ b/scalafix/project/TargetAxis.scala @@ -0,0 +1,47 @@ +import sbt._ +import sbt.internal.ProjectMatrix +import sbtprojectmatrix.ProjectMatrixPlugin.autoImport._ + +/** Use on ProjectMatrix rows to tag an affinity to a custom scalaVersion */ +case class TargetAxis(scalaVersion: String) extends VirtualAxis.WeakAxis { + + private val scalaBinaryVersion = CrossVersion.binaryScalaVersion(scalaVersion) + + override val idSuffix = s"Target${scalaBinaryVersion.replace('.', '_')}" + override val directorySuffix = s"target$scalaBinaryVersion" +} + +object TargetAxis { + + private def targetScalaVersion(virtualAxes: Seq[VirtualAxis]): String = + virtualAxes.collectFirst { case a: TargetAxis => a.scalaVersion }.get + + /** When invoked on a ProjectMatrix with a TargetAxis, lookup the project + * generated by `matrix` with a scalaVersion matching the one declared in + * that TargetAxis, and resolve `key`. + */ + def resolve[T]( + matrix: ProjectMatrix, + key: TaskKey[T] + ): Def.Initialize[Task[T]] = + Def.taskDyn { + val sv = targetScalaVersion(virtualAxes.value) + val project = matrix.finder().apply(sv) + Def.task((project / key).value) + } + + /** When invoked on a ProjectMatrix with a TargetAxis, lookup the project + * generated by `matrix` with a scalaVersion matching the one declared in + * that TargetAxis, and resolve `key`. + */ + def resolve[T]( + matrix: ProjectMatrix, + key: SettingKey[T] + ): Def.Initialize[T] = + Def.settingDyn { + val sv = targetScalaVersion(virtualAxes.value) + val project = matrix.finder().apply(sv) + Def.setting((project / key).value) + } + +} diff --git a/scalafix/project/build.properties b/scalafix/project/build.properties new file mode 100644 index 0000000..abbbce5 --- /dev/null +++ b/scalafix/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 diff --git a/scalafix/project/plugins.sbt b/scalafix/project/plugins.sbt new file mode 100644 index 0000000..f9fba95 --- /dev/null +++ b/scalafix/project/plugins.sbt @@ -0,0 +1,3 @@ +resolvers += Resolver.sonatypeRepo("releases") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.1") diff --git a/scalafix/readme.md b/scalafix/readme.md new file mode 100644 index 0000000..b2bbae5 --- /dev/null +++ b/scalafix/readme.md @@ -0,0 +1,7 @@ +# Scalafix rules for FS2 PGP + +To develop rule: +``` +sbt ~tests/test +# edit rules/src/main/scala/fix/Fs2Pgp.scala +``` diff --git a/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule new file mode 100644 index 0000000..73752c9 --- /dev/null +++ b/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -0,0 +1 @@ +fix.Fs2Pgp diff --git a/scalafix/rules/src/main/scala/fix/Fs2Pgp.scala b/scalafix/rules/src/main/scala/fix/Fs2Pgp.scala new file mode 100644 index 0000000..83cdf21 --- /dev/null +++ b/scalafix/rules/src/main/scala/fix/Fs2Pgp.scala @@ -0,0 +1,34 @@ +package fix + +import scalafix.v1._ +import scala.meta._ + +class Fs2Pgp extends SemanticRule("Fs2Pgp") { + + override def fix(implicit doc: SemanticDocument): Patch = { + doc.tree.collect { + case t@Term.ApplyType.After_4_6_0(Term.Name("CryptoAlg"), Type.ArgClause(List(Type.Name(name)))) => + Patch.replaceTree(t, s"CryptoAlg.resource[$name]").atomic + case t@Term.Apply.After_4_6_0(Term.Select(Term.Name(name), Term.Name("armor")), Term.ArgClause(List(), None)) => + Patch.replaceTree(t, s"$name.armor").atomic + case t@Term.Apply.After_4_6_0(Term.Name("tagChunkSize"), _) => + Patch.renameSymbol(t.symbol, "ChunkSize") + case t@Term.Apply.After_4_6_0( + Term.Name("PosInt"), + Term.ArgClause(List(Lit.Int(posIntArg)), None) + ) => Patch.replaceTree(t, s"PosInt.unsafeFrom($posIntArg)").atomic + case t@ + Term.Apply.After_4_6_0( + Term.Select(Term.Name(cryptoRName), Term.Name("encrypt")), + Term.ArgClause( + List( + Term.Name(keyName), + Term.Assign( Term.Name("fileName"), fileNameTerm ) + ), + None + ) + ) => + Patch.replaceTree(t, s"$cryptoRName.encrypt(EncryptionConfig().withFileName($fileNameTerm), $keyName)") + }.asPatch + } +} \ No newline at end of file diff --git a/scalafix/tests/src/test/scala/fix/RuleSuite.scala b/scalafix/tests/src/test/scala/fix/RuleSuite.scala new file mode 100644 index 0000000..ab80d00 --- /dev/null +++ b/scalafix/tests/src/test/scala/fix/RuleSuite.scala @@ -0,0 +1,8 @@ +package fix + +import scalafix.testkit._ +import org.scalatest.funsuite.AnyFunSuiteLike + +class RuleSuite extends AbstractSemanticRuleSuite with AnyFunSuiteLike { + runAllTests() +} From 6b5a058641a8f05ca804e0d46d99236ef22d9eb5 Mon Sep 17 00:00:00 2001 From: Connor James Smith Date: Tue, 2 Jan 2024 14:07:58 -0500 Subject: [PATCH 2/2] addresses @bpholt comment on [#232](https://github.com/Dwolla/fs2-pgp/pull/232#discussion_r1439704568) --- .../src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala diff --git a/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala b/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala deleted file mode 100644 index de4ae1c..0000000 --- a/scalafix/output/src/main/scala-3/fix/Fs2PgpSignificantIndentation.scala +++ /dev/null @@ -1,5 +0,0 @@ -package fix - -object Fs2PgpSignificantIndentation: - val a = 1 - // Add code that needs fixing here.