Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose API checks as a CLI #1061

Merged
merged 3 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ kotlin.mpp.androidSourceSetLayoutVersion1.nowarn=true
kotlin.mpp.stability.nowarn=true
kotlin.mpp.commonizerLogLevel=info
kotlin.mpp.enableCInteropCommonization=true

# Signals to our own plugin that we are building within the repo.
app.cash.zipline.internal=true
4 changes: 1 addition & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ kotlinx-serialization = "1.5.0"
okHttp = "4.11.0"
okio = "3.3.0"
minSdk = "18"
picocli = "4.7.4"
sqldelight = "1.5.5"

[libraries]
Expand Down Expand Up @@ -56,8 +55,7 @@ okHttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okHttp" }
okHttp-mockWebServer = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okHttp" }
okio-core = { module = "com.squareup.okio:okio", version.ref = "okio" }
okio-fakeFileSystem = { module = "com.squareup.okio:okio-fakefilesystem", version.ref = "okio" }
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
picocli-compiler = { module = "info.picocli:picocli-codegen", version.ref = "picocli" }
clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.5.4" }
shadowJar-gradle-plugin = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "8.0.0" }
sqldelight-gradle-plugin = { module = "com.squareup.sqldelight:gradle-plugin", version.ref = "sqldelight" }
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
Expand Down
16 changes: 6 additions & 10 deletions samples/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,11 @@ dependencyResolutionManagement {

includeBuild('..') {
dependencySubstitution {
substitute module("app.cash.zipline:zipline") using project(
":zipline")
substitute module("app.cash.zipline:zipline-gradle-plugin") using project(
":zipline-gradle-plugin")
substitute module("app.cash.zipline:zipline-kotlin-plugin") using project(
":zipline-kotlin-plugin")
substitute module("app.cash.zipline:zipline-loader") using project(
":zipline-loader")
substitute module("app.cash.zipline:zipline-profiler") using project(
":zipline-profiler")
substitute module("app.cash.zipline:zipline") using project(":zipline")
substitute module("app.cash.zipline:zipline-cli") using project(":zipline-cli")
substitute module("app.cash.zipline:zipline-gradle-plugin") using project(":zipline-gradle-plugin")
substitute module("app.cash.zipline:zipline-kotlin-plugin") using project(":zipline-kotlin-plugin")
substitute module("app.cash.zipline:zipline-loader") using project(":zipline-loader")
substitute module("app.cash.zipline:zipline-profiler") using project(":zipline-profiler")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ import org.jetbrains.kotlin.types.Variance

fun readFirZiplineApi(
sources: Collection<File>,
dependencies: Collection<File>,
classpath: Collection<File>,
): FirZiplineApi {
return KotlinFirLoader(sources, dependencies).use { loader ->
return KotlinFirLoader(sources, classpath).use { loader ->
val output = loader.load("zipline-api-dump")
FirZiplineApiReader(output).read()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
*/
internal class KotlinFirLoader(
private val sources: Collection<File>,
private val dependencies: Collection<File>,
private val classpath: Collection<File>,
) : AutoCloseable {
private val disposable = Disposer.newDisposable()

Expand Down Expand Up @@ -79,7 +79,7 @@ internal class KotlinFirLoader(
configuration.addKotlinSourceRoots(sources.map { it.absolutePath })
// TODO Figure out how to add the JDK modules to the classpath. Currently importing the stdlib
// allows a typealias to resolve to a JDK type which doesn't exist and thus breaks analysis.
configuration.addJvmClasspathRoots(dependencies.filter { "kotlin-stdlib-" !in it.path })
configuration.addJvmClasspathRoots(classpath.filter { "kotlin-stdlib-" !in it.path })

val environment = KotlinCoreEnvironment.createForProduction(
disposable,
Expand Down
5 changes: 1 addition & 4 deletions zipline-cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import com.vanniktech.maven.publish.KotlinJvm

plugins {
kotlin("jvm")
kotlin("kapt")
application
id("com.github.gmazzo.buildconfig")
id("com.vanniktech.maven.publish.base")
Expand Down Expand Up @@ -47,10 +46,8 @@ kotlin {
dependencies {
api(projects.ziplineApiValidator)
api(projects.ziplineLoader)
implementation(libs.clikt)
implementation(libs.okHttp.core)
implementation(libs.picocli)

kapt(libs.picocli.compiler)

testImplementation(projects.ziplineLoaderTesting)
testImplementation(libs.assertk)
Expand Down
46 changes: 17 additions & 29 deletions zipline-cli/src/main/kotlin/app/cash/zipline/cli/Download.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,34 @@

package app.cash.zipline.cli

import app.cash.zipline.cli.Download.Companion.NAME
import app.cash.zipline.loader.ManifestVerifier.Companion.NO_SIGNATURE_CHECKS
import app.cash.zipline.loader.ZiplineLoader
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.path
import java.io.File
import java.util.concurrent.Executors
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okio.FileSystem
import okio.Path.Companion.toOkioPath
import picocli.CommandLine.Command
import picocli.CommandLine.Option

@Command(
name = NAME,
description = ["Recursively download Zipline code to a directory from a URL"],
mixinStandardHelpOptions = true,
versionProvider = Main.VersionProvider::class,
)
class Download : Runnable {
@Option(
names = ["-A", "--application-name"],
description = ["Application name for the Zipline Manifest."],
required = true,
)
lateinit var applicationName: String
class Download : CliktCommand(NAME) {
private val applicationName by option("-A", "--application-name")
.required()
.help("Application name for the Zipline Manifest.")

@Option(
names = ["-M", "--manifest-url"],
description = ["URL to the Zipline Manifest for the code to download."],
required = true,
)
lateinit var manifestUrl: String
private val manifestUrl by option("-M", "--manifest-url")
.required()
.help("URL to the Zipline Manifest for the code to download.")

@Option(
names = ["-D", "--download-dir"],
description = ["Directory where code will be downloaded to."],
required = true,
)
lateinit var downloadDir: File
private val downloadDir by option("-D", "--download-dir")
.path(canBeFile = false)
.required()
.help("Directory where code will be downloaded to.")

private val executorService = Executors.newSingleThreadExecutor { Thread(it, "Zipline") }
private val dispatcher = executorService.asCoroutineDispatcher()
Expand All @@ -82,7 +70,7 @@ class Download : Runnable {
download(
applicationName = applicationName,
manifestUrl = manifestUrl,
downloadDir = downloadDir,
downloadDir = downloadDir.toFile(),
)
}

Expand Down
37 changes: 17 additions & 20 deletions zipline-cli/src/main/kotlin/app/cash/zipline/cli/GenerateKeyPair.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,33 @@

package app.cash.zipline.cli

import app.cash.zipline.cli.GenerateKeyPair.Companion.NAME
import app.cash.zipline.loader.SignatureAlgorithmId
import app.cash.zipline.loader.SignatureAlgorithmId.Ed25519
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum
import java.io.PrintStream
import picocli.CommandLine.Command
import picocli.CommandLine.Option

@Command(
name = NAME,
description = [
"Generate an Ed25519 key pair for ManifestSigner and ManifestVerifier",
],
mixinStandardHelpOptions = true,
versionProvider = Main.VersionProvider::class,
)
class GenerateKeyPair(
private val out: PrintStream = System.out,
) : Runnable {
@Option(
names = ["-a", "--algorithm"],
description = ["Signing algorithm to use."],
)
var algorithm: SignatureAlgorithmId = Ed25519
) : CliktCommand(NAME) {
private val algorithm by option("-a", "--algorithm")
.enum<SignatureAlgorithmId>()
.default(Ed25519)
.help("Signing algorithm to use.")

@Suppress("INVISIBLE_MEMBER") // Access :zipline-loader internals.
override fun run() {
val keyPair = app.cash.zipline.loader.internal.generateKeyPair(algorithm)
out.println(" ALGORITHM: $algorithm")
out.println(" PUBLIC KEY: ${keyPair.publicKey.hex()}")
out.println("PRIVATE KEY: ${keyPair.privateKey.hex()}")
out.println(
"""
| ALGORITHM: $algorithm
| PUBLIC KEY: ${keyPair.publicKey.hex()}
|PRIVATE KEY: ${keyPair.privateKey.hex()}
""".trimMargin(),
)
}

companion object {
Expand Down
70 changes: 18 additions & 52 deletions zipline-cli/src/main/kotlin/app/cash/zipline/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,62 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:JvmName("Main")

package app.cash.zipline.cli

import app.cash.zipline.cli.Main.Companion.NAME
import kotlin.system.exitProcess
import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.HelpCommand
import picocli.CommandLine.IVersionProvider
import picocli.CommandLine.Model.CommandSpec
import picocli.CommandLine.Option
import picocli.CommandLine.ParameterException
import picocli.CommandLine.Spec
import app.cash.zipline.cli.ValidateZiplineApi.Companion.NAME_CHECK
import app.cash.zipline.cli.ValidateZiplineApi.Companion.NAME_DUMP
import com.github.ajalt.clikt.core.NoOpCliktCommand
import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.options.versionOption

@Command(
name = NAME,
description = ["Use Zipline without Gradle."],
mixinStandardHelpOptions = true,
synopsisSubcommandLabel = "COMMAND",
subcommands = [Download::class, GenerateKeyPair::class, HelpCommand::class],
versionProvider = Main.VersionProvider::class,
)
class Main : Runnable {
@Spec
lateinit var spec: CommandSpec

@Option(names = ["--completionScript"], hidden = true)
var completionScript: Boolean = false

override fun run() {
if (completionScript) {
println(picocli.AutoComplete.bash("zipline-cli", CommandLine(Main())))
return
}

throw ParameterException(spec.commandLine(), "Missing required subcommand")
}

class VersionProvider : IVersionProvider {
override fun getVersion(): Array<String> {
return arrayOf(
"$NAME ${BuildConfig.VERSION}",
)
}
fun main(vararg args: String) {
if (System.getProperty("javax.net.debug") == null) {
System.setProperty("javax.net.debug", "")
}

companion object {
internal const val NAME = "zipline-cli"

@JvmStatic
fun main(args: Array<String>) {
if (System.getProperty("javax.net.debug") == null) {
System.setProperty("javax.net.debug", "")
}

exitProcess(CommandLine(Main()).execute(*args))
}
}
NoOpCliktCommand()
.subcommands(
Download(),
GenerateKeyPair(),
ValidateZiplineApi(NAME_CHECK),
ValidateZiplineApi(NAME_DUMP),
)
.versionOption(BuildConfig.VERSION)
.main(args)
}
Loading