mirror of
https://github.com/apple/pkl.git
synced 2026-04-24 09:18:35 +02:00
Allow referring to remote project dependencies on the CLI with @-notation (#1434)
This commit is contained in:
@@ -52,8 +52,6 @@ constructor(
|
|||||||
private val errStream: OutputStream = System.err,
|
private val errStream: OutputStream = System.err,
|
||||||
) : CliCommand(options) {
|
) : CliCommand(options) {
|
||||||
|
|
||||||
private val normalizedSourceModule = options.normalizedSourceModules.first()
|
|
||||||
|
|
||||||
override fun doRun() {
|
override fun doRun() {
|
||||||
val builder = evaluatorBuilder()
|
val builder = evaluatorBuilder()
|
||||||
try {
|
try {
|
||||||
@@ -68,7 +66,7 @@ constructor(
|
|||||||
val evaluator = builder.build()
|
val evaluator = builder.build()
|
||||||
evaluator.use {
|
evaluator.use {
|
||||||
evaluator.evaluateCommand(
|
evaluator.evaluateCommand(
|
||||||
uri(normalizedSourceModule),
|
uri(resolvedSourceModules.first()),
|
||||||
reservedFlagNames,
|
reservedFlagNames,
|
||||||
reservedFlagShortNames,
|
reservedFlagShortNames,
|
||||||
) { spec ->
|
) { spec ->
|
||||||
|
|||||||
@@ -111,12 +111,11 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveOutputPaths(pathStr: String): Map<URI, Path> {
|
private fun resolveOutputPaths(pathStr: String): Map<URI, Path> {
|
||||||
val moduleUris = options.base.normalizedSourceModules
|
|
||||||
val workingDir = options.base.normalizedWorkingDir
|
val workingDir = options.base.normalizedWorkingDir
|
||||||
// used just to resolve the `%{moduleName}` placeholder
|
// used just to resolve the `%{moduleName}` placeholder
|
||||||
val moduleResolver = ModuleResolver(moduleKeyFactories(ModulePathResolver.empty()))
|
val moduleResolver = ModuleResolver(moduleKeyFactories(ModulePathResolver.empty()))
|
||||||
|
|
||||||
return moduleUris.associateWith { uri ->
|
return resolvedSourceModules.associateWith { uri ->
|
||||||
val moduleDir: String? =
|
val moduleDir: String? =
|
||||||
IoUtils.toPath(uri)?.let {
|
IoUtils.toPath(uri)?.let {
|
||||||
IoUtils.relativize(it.parent, workingDir).toString().ifEmpty { "." }
|
IoUtils.relativize(it.parent, workingDir).toString().ifEmpty { "." }
|
||||||
@@ -192,7 +191,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var outputWritten = false
|
var outputWritten = false
|
||||||
for (moduleUri in options.base.normalizedSourceModules) {
|
for (moduleUri in resolvedSourceModules) {
|
||||||
val moduleSource = toModuleSource(moduleUri, inputStream)
|
val moduleSource = toModuleSource(moduleUri, inputStream)
|
||||||
if (options.expression != null) {
|
if (options.expression != null) {
|
||||||
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
|
val output = evaluator.evaluateExpressionString(moduleSource, options.expression)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -66,7 +66,7 @@ constructor(
|
|||||||
try {
|
try {
|
||||||
return builder
|
return builder
|
||||||
.apply {
|
.apply {
|
||||||
for ((idx, sourceModule) in options.base.normalizedSourceModules.withIndex()) {
|
for ((idx, sourceModule) in resolvedSourceModules.withIndex()) {
|
||||||
addExternalProperty("pkl.analyzeImports.$idx", sourceModule.toString())
|
addExternalProperty("pkl.analyzeImports.$idx", sourceModule.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ constructor(
|
|||||||
|
|
||||||
private fun evalTest(builder: EvaluatorBuilder) {
|
private fun evalTest(builder: EvaluatorBuilder) {
|
||||||
val sources =
|
val sources =
|
||||||
options.normalizedSourceModules.ifEmpty { project?.tests?.map { it.toUri() } }
|
resolvedSourceModules.ifEmpty { project?.tests?.map { it.toUri() } }
|
||||||
?:
|
?:
|
||||||
// keep in sync with error message thrown by clikt
|
// keep in sync with error message thrown by clikt
|
||||||
throw CliException(
|
throw CliException(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
package org.pkl.cli.commands
|
package org.pkl.cli.commands
|
||||||
|
|
||||||
import com.github.ajalt.clikt.core.Context
|
import com.github.ajalt.clikt.core.Context
|
||||||
import com.github.ajalt.clikt.core.NoOpCliktCommand
|
|
||||||
import com.github.ajalt.clikt.core.subcommands
|
import com.github.ajalt.clikt.core.subcommands
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
@@ -24,9 +23,10 @@ import java.nio.file.Path
|
|||||||
import org.pkl.cli.CliImportAnalyzer
|
import org.pkl.cli.CliImportAnalyzer
|
||||||
import org.pkl.cli.CliImportAnalyzerOptions
|
import org.pkl.cli.CliImportAnalyzerOptions
|
||||||
import org.pkl.commons.cli.commands.ModulesCommand
|
import org.pkl.commons.cli.commands.ModulesCommand
|
||||||
|
import org.pkl.commons.cli.commands.NoOpCommand
|
||||||
import org.pkl.commons.cli.commands.single
|
import org.pkl.commons.cli.commands.single
|
||||||
|
|
||||||
class AnalyzeCommand : NoOpCliktCommand(name = "analyze") {
|
class AnalyzeCommand : NoOpCommand(name = "analyze") {
|
||||||
override fun help(context: Context) = "Commands related to static analysis"
|
override fun help(context: Context) = "Commands related to static analysis"
|
||||||
|
|
||||||
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,7 +17,6 @@ package org.pkl.cli.commands
|
|||||||
|
|
||||||
import com.github.ajalt.clikt.completion.CompletionCandidates
|
import com.github.ajalt.clikt.completion.CompletionCandidates
|
||||||
import com.github.ajalt.clikt.core.Context
|
import com.github.ajalt.clikt.core.Context
|
||||||
import com.github.ajalt.clikt.core.NoOpCliktCommand
|
|
||||||
import com.github.ajalt.clikt.core.subcommands
|
import com.github.ajalt.clikt.core.subcommands
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.arguments.multiple
|
import com.github.ajalt.clikt.parameters.arguments.multiple
|
||||||
@@ -30,10 +29,11 @@ import java.nio.file.Path
|
|||||||
import org.pkl.cli.CliProjectPackager
|
import org.pkl.cli.CliProjectPackager
|
||||||
import org.pkl.cli.CliProjectResolver
|
import org.pkl.cli.CliProjectResolver
|
||||||
import org.pkl.commons.cli.commands.BaseCommand
|
import org.pkl.commons.cli.commands.BaseCommand
|
||||||
|
import org.pkl.commons.cli.commands.NoOpCommand
|
||||||
import org.pkl.commons.cli.commands.TestOptions
|
import org.pkl.commons.cli.commands.TestOptions
|
||||||
import org.pkl.commons.cli.commands.single
|
import org.pkl.commons.cli.commands.single
|
||||||
|
|
||||||
class ProjectCommand : NoOpCliktCommand(name = "project") {
|
class ProjectCommand : NoOpCommand(name = "project") {
|
||||||
override fun help(context: Context) = "Run commands related to projects"
|
override fun help(context: Context) = "Run commands related to projects"
|
||||||
|
|
||||||
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
||||||
|
|||||||
@@ -17,21 +17,22 @@ package org.pkl.cli.commands
|
|||||||
|
|
||||||
import com.github.ajalt.clikt.completion.CompletionCommand
|
import com.github.ajalt.clikt.completion.CompletionCommand
|
||||||
import com.github.ajalt.clikt.core.Context
|
import com.github.ajalt.clikt.core.Context
|
||||||
import com.github.ajalt.clikt.core.NoOpCliktCommand
|
|
||||||
import com.github.ajalt.clikt.core.context
|
import com.github.ajalt.clikt.core.context
|
||||||
import com.github.ajalt.clikt.core.subcommands
|
import com.github.ajalt.clikt.core.subcommands
|
||||||
|
import org.pkl.commons.cli.commands.NoOpCommand
|
||||||
import org.pkl.commons.cli.commands.installCommonOptions
|
import org.pkl.commons.cli.commands.installCommonOptions
|
||||||
import org.pkl.core.Release
|
import org.pkl.core.Release
|
||||||
|
|
||||||
internal val helpLink = "${Release.current().documentation.homepage}pkl-cli/index.html#usage"
|
internal val helpLink = "${Release.current().documentation.homepage}pkl-cli/index.html#usage"
|
||||||
|
|
||||||
class RootCommand : NoOpCliktCommand(name = "pkl") {
|
class RootCommand : NoOpCommand(name = "pkl") {
|
||||||
override val printHelpOnEmptyArgs = true
|
override val printHelpOnEmptyArgs = true
|
||||||
|
|
||||||
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
context {
|
context {
|
||||||
|
readArgumentFile = null
|
||||||
suggestTypoCorrection = { given, possible ->
|
suggestTypoCorrection = { given, possible ->
|
||||||
if (!given.startsWith("-")) {
|
if (!given.startsWith("-")) {
|
||||||
registeredSubcommands().map { it.commandName }
|
registeredSubcommands().map { it.commandName }
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ class RunCommand : BaseCommand(name = "run", helpLink = helpLink) {
|
|||||||
private val showHelp by option("-h", "--help", help = "Show this message and exit").flag()
|
private val showHelp by option("-h", "--help", help = "Show this message and exit").flag()
|
||||||
|
|
||||||
val module: URI? by
|
val module: URI? by
|
||||||
argument(name = "module", completionCandidates = CompletionCandidates.Path)
|
argument(
|
||||||
|
name = "module",
|
||||||
|
help = "Root pkl:Command module to invoke.",
|
||||||
|
completionCandidates = CompletionCandidates.Path,
|
||||||
|
)
|
||||||
.convert { BaseOptions.parseModuleName(it) }
|
.convert { BaseOptions.parseModuleName(it) }
|
||||||
.optional()
|
.optional()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,7 +31,7 @@ class CliJavaCodeGenerator(private val options: CliJavaCodeGeneratorOptions) :
|
|||||||
val builder = evaluatorBuilder()
|
val builder = evaluatorBuilder()
|
||||||
try {
|
try {
|
||||||
builder.build().use { evaluator ->
|
builder.build().use { evaluator ->
|
||||||
for (moduleUri in options.base.normalizedSourceModules) {
|
for (moduleUri in resolvedSourceModules) {
|
||||||
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
|
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
|
||||||
val codeGenerator = JavaCodeGenerator(schema, options.toJavaCodeGeneratorOptions())
|
val codeGenerator = JavaCodeGenerator(schema, options.toJavaCodeGeneratorOptions())
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,7 +32,7 @@ class CliKotlinCodeGenerator(private val options: CliKotlinCodeGeneratorOptions)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
builder.build().use { evaluator ->
|
builder.build().use { evaluator ->
|
||||||
for (moduleUri in options.base.normalizedSourceModules) {
|
for (moduleUri in resolvedSourceModules) {
|
||||||
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
|
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
|
||||||
val codeGenerator = KotlinCodeGenerator(schema, options.toKotlinCodeGeneratorOptions())
|
val codeGenerator = KotlinCodeGenerator(schema, options.toKotlinCodeGeneratorOptions())
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -175,11 +175,19 @@ data class CliBaseOptions(
|
|||||||
/** [rootDir] after normalization. */
|
/** [rootDir] after normalization. */
|
||||||
val normalizedRootDir: Path? = rootDir?.let(normalizedWorkingDir::resolve)
|
val normalizedRootDir: Path? = rootDir?.let(normalizedWorkingDir::resolve)
|
||||||
|
|
||||||
|
/** The effective project directory, if exists. */
|
||||||
|
val normalizedProjectFile: Path? by lazy {
|
||||||
|
projectDir?.resolve(ProjectDependenciesManager.PKL_PROJECT_FILENAME)
|
||||||
|
?: normalizedWorkingDir.getProjectFile(rootDir)
|
||||||
|
}
|
||||||
|
|
||||||
/** [sourceModules] after normalization. */
|
/** [sourceModules] after normalization. */
|
||||||
val normalizedSourceModules: List<URI> =
|
val normalizedSourceModules: List<URI> =
|
||||||
sourceModules
|
sourceModules
|
||||||
.map { uri ->
|
.map { uri ->
|
||||||
if (uri.isAbsolute) uri else IoUtils.resolve(normalizedWorkingDir.toUri(), uri)
|
if (uri.isAbsolute) uri
|
||||||
|
else if (uri.path.startsWith("@") && !noProject && normalizedProjectFile != null) uri
|
||||||
|
else IoUtils.resolve(normalizedWorkingDir.toUri(), uri)
|
||||||
}
|
}
|
||||||
// sort modules to make cli output independent of source module order
|
// sort modules to make cli output independent of source module order
|
||||||
.sorted()
|
.sorted()
|
||||||
@@ -195,12 +203,6 @@ data class CliBaseOptions(
|
|||||||
/** [moduleCacheDir] after normalization. */
|
/** [moduleCacheDir] after normalization. */
|
||||||
val normalizedModuleCacheDir: Path? = moduleCacheDir?.let(normalizedWorkingDir::resolve)
|
val normalizedModuleCacheDir: Path? = moduleCacheDir?.let(normalizedWorkingDir::resolve)
|
||||||
|
|
||||||
/** The effective project directory, if exists. */
|
|
||||||
val normalizedProjectFile: Path? by lazy {
|
|
||||||
projectDir?.resolve(ProjectDependenciesManager.PKL_PROJECT_FILENAME)
|
|
||||||
?: normalizedWorkingDir.getProjectFile(rootDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [caCertificates] after normalization. */
|
/** [caCertificates] after normalization. */
|
||||||
val normalizedCaCertificates: List<Path> = caCertificates.map(normalizedWorkingDir::resolve)
|
val normalizedCaCertificates: List<Path> = caCertificates.map(normalizedWorkingDir::resolve)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,35 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun resolveModuleUri(uri: URI): URI =
|
||||||
|
if (uri.isAbsolute) uri
|
||||||
|
else { // must be @dep/mod.pkl notation!!
|
||||||
|
if (!uri.path.startsWith('@'))
|
||||||
|
throw CliBugException(
|
||||||
|
RuntimeException("tried to resolve project URI `$uri` with no @ prefix")
|
||||||
|
)
|
||||||
|
if (project == null)
|
||||||
|
throw CliBugException(
|
||||||
|
RuntimeException("tried to resolve project URI `$uri` with no project present")
|
||||||
|
)
|
||||||
|
val dep = uri.path.substringBefore('/').drop(1)
|
||||||
|
val path = uri.path.dropWhile { it != '/' }
|
||||||
|
if (path.isEmpty()) throw CliException("Invalid project dependency URI `$uri`.")
|
||||||
|
|
||||||
|
val remoteDep =
|
||||||
|
project!!.dependencies.remoteDependencies()[dep]
|
||||||
|
?: if (project!!.dependencies.localDependencies().containsKey(dep))
|
||||||
|
throw CliException(
|
||||||
|
"Only remote project dependencies may be referenced using @-notation. Dependency `@$dep` is a local dependency."
|
||||||
|
)
|
||||||
|
else throw CliException("Project does not contain dependency `@$dep`.")
|
||||||
|
remoteDep.packageUri.toPackageAssetUri(path).uri
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val resolvedSourceModules: List<URI> =
|
||||||
|
if (project == null) cliOptions.normalizedSourceModules
|
||||||
|
else cliOptions.normalizedSourceModules.map(::resolveModuleUri)
|
||||||
|
|
||||||
protected fun loadProject(projectFile: Path): Project {
|
protected fun loadProject(projectFile: Path): Project {
|
||||||
val securityManager =
|
val securityManager =
|
||||||
SecurityManagers.standard(
|
SecurityManagers.standard(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,9 +17,14 @@ package org.pkl.commons.cli.commands
|
|||||||
|
|
||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.core.Context
|
import com.github.ajalt.clikt.core.Context
|
||||||
|
import com.github.ajalt.clikt.core.context
|
||||||
import com.github.ajalt.clikt.parameters.groups.provideDelegate
|
import com.github.ajalt.clikt.parameters.groups.provideDelegate
|
||||||
|
|
||||||
abstract class BaseCommand(name: String, private val helpLink: String) : CliktCommand(name = name) {
|
abstract class BaseCommand(name: String, private val helpLink: String) : CliktCommand(name = name) {
|
||||||
|
init {
|
||||||
|
context { readArgumentFile = null }
|
||||||
|
}
|
||||||
|
|
||||||
abstract val helpString: String
|
abstract val helpString: String
|
||||||
|
|
||||||
override fun help(context: Context) = helpString
|
override fun help(context: Context) = helpString
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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 org.pkl.commons.cli.commands
|
||||||
|
|
||||||
|
import com.github.ajalt.clikt.core.NoOpCliktCommand
|
||||||
|
import com.github.ajalt.clikt.core.context
|
||||||
|
|
||||||
|
open class NoOpCommand(name: String? = null) : NoOpCliktCommand(name) {
|
||||||
|
init {
|
||||||
|
context { readArgumentFile = null }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,18 @@
|
|||||||
package org.pkl.commons.cli
|
package org.pkl.commons.cli
|
||||||
|
|
||||||
import com.github.ajalt.clikt.core.parse
|
import com.github.ajalt.clikt.core.parse
|
||||||
|
import java.net.URI
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.ExperimentalPathApi
|
||||||
|
import kotlin.io.path.writeText
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
import org.pkl.commons.cli.commands.BaseCommand
|
import org.pkl.commons.cli.commands.BaseCommand
|
||||||
import org.pkl.core.SecurityManagers
|
import org.pkl.core.SecurityManagers
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPathApi::class)
|
||||||
class CliCommandTest {
|
class CliCommandTest {
|
||||||
|
|
||||||
class CliTest(options: CliBaseOptions) : CliCommand(options) {
|
class CliTest(options: CliBaseOptions) : CliCommand(options) {
|
||||||
@@ -28,6 +35,7 @@ class CliCommandTest {
|
|||||||
|
|
||||||
val myAllowedResources = allowedResources
|
val myAllowedResources = allowedResources
|
||||||
val myAllowedModules = allowedModules
|
val myAllowedModules = allowedModules
|
||||||
|
val myResolvedSourceModules = resolvedSourceModules
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cmd =
|
private val cmd =
|
||||||
@@ -55,7 +63,7 @@ class CliCommandTest {
|
|||||||
"scheme+ext=reader5 with args",
|
"scheme+ext=reader5 with args",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val opts = cmd.baseOptions.baseOptions(emptyList(), null, true)
|
val opts = cmd.baseOptions.baseOptions(emptyList(), testMode = true)
|
||||||
val cliTest = CliTest(opts)
|
val cliTest = CliTest(opts)
|
||||||
assertThat(cliTest.myAllowedModules.map { it.pattern() })
|
assertThat(cliTest.myAllowedModules.map { it.pattern() })
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
@@ -68,4 +76,112 @@ class CliCommandTest {
|
|||||||
listOf("\\Qscheme1:\\E", "\\Qscheme2:\\E", "\\Qscheme+ext:\\E")
|
listOf("\\Qscheme1:\\E", "\\Qscheme2:\\E", "\\Qscheme+ext:\\E")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `@-notation package URIs - treated as relative paths when no project present`(
|
||||||
|
@TempDir tempDir: Path
|
||||||
|
) {
|
||||||
|
cmd.parse(arrayOf("--working-dir=$tempDir"))
|
||||||
|
val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true)
|
||||||
|
val cliTest = CliTest(opts)
|
||||||
|
assertThat(cliTest.myResolvedSourceModules)
|
||||||
|
.isEqualTo(listOf(tempDir.toUri().resolve("@foo/bar.pkl")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `@-notation package URIs - empty paths are rejected`(@TempDir tempDir: Path) {
|
||||||
|
tempDir
|
||||||
|
.resolve("PklProject")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
amends "pkl:Project"
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
cmd.parse(arrayOf("--working-dir=$tempDir"))
|
||||||
|
val opts = cmd.baseOptions.baseOptions(listOf(URI("@no.slash")), testMode = true)
|
||||||
|
val exc = assertThrows<CliException> { CliTest(opts) }
|
||||||
|
assertThat(exc.message).isEqualTo("Invalid project dependency URI `@no.slash`.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `@-notation package URIs - missing dependencies are rejected`(@TempDir tempDir: Path) {
|
||||||
|
tempDir
|
||||||
|
.resolve("PklProject")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
amends "pkl:Project"
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
cmd.parse(arrayOf("--working-dir=$tempDir"))
|
||||||
|
val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true)
|
||||||
|
val exc = assertThrows<CliException> { CliTest(opts) }
|
||||||
|
assertThat(exc.message).isEqualTo("Project does not contain dependency `@foo`.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `@-notation package URIs - local dependencies are rejected`(
|
||||||
|
@TempDir tempDir: Path,
|
||||||
|
@TempDir depDir: Path,
|
||||||
|
) {
|
||||||
|
depDir
|
||||||
|
.resolve("PklProject")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
amends "pkl:Project"
|
||||||
|
|
||||||
|
package {
|
||||||
|
name = "foo"
|
||||||
|
baseUri = "package://example.com/foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
packageZipUrl = "https://example.com/foo@\(version).zip"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
tempDir
|
||||||
|
.resolve("PklProject")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
amends "pkl:Project"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
["foo"] = import("$depDir/PklProject")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
cmd.parse(arrayOf("--working-dir=$tempDir"))
|
||||||
|
val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true)
|
||||||
|
val exc = assertThrows<CliException> { CliTest(opts) }
|
||||||
|
assertThat(exc.message)
|
||||||
|
.isEqualTo(
|
||||||
|
"Only remote project dependencies may be referenced using @-notation. Dependency `@foo` is a local dependency."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `@-notation package URIs - remote dependencies are resolved`(@TempDir tempDir: Path) {
|
||||||
|
tempDir
|
||||||
|
.resolve("PklProject")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
amends "pkl:Project"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
["foo"] {
|
||||||
|
uri = "package://example.com/foo@1.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
cmd.parse(arrayOf("--working-dir=$tempDir"))
|
||||||
|
val opts = cmd.baseOptions.baseOptions(listOf(URI("@foo/bar.pkl")), testMode = true)
|
||||||
|
val cliTest = CliTest(opts)
|
||||||
|
assertThat(cliTest.myResolvedSourceModules)
|
||||||
|
.isEqualTo(listOf(tempDir.toUri().resolve("package://example.com/foo@1.2.3#/bar.pkl")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -159,7 +159,7 @@ class CliDocGenerator(
|
|||||||
val regularModuleUris = mutableListOf<URI>()
|
val regularModuleUris = mutableListOf<URI>()
|
||||||
val pklProjectPaths = mutableSetOf<Path>()
|
val pklProjectPaths = mutableSetOf<Path>()
|
||||||
val packageUris = mutableListOf<PackageUri>()
|
val packageUris = mutableListOf<PackageUri>()
|
||||||
for (moduleUri in options.base.normalizedSourceModules) {
|
for (moduleUri in resolvedSourceModules) {
|
||||||
if (moduleUri.scheme == "file") {
|
if (moduleUri.scheme == "file") {
|
||||||
val dir = moduleUri.toPath().parent
|
val dir = moduleUri.toPath().parent
|
||||||
val projectFile = dir.getProjectFile(options.base.normalizedRootDir)
|
val projectFile = dir.getProjectFile(options.base.normalizedRootDir)
|
||||||
|
|||||||
Reference in New Issue
Block a user