Do not throw if output dir is symlink (#1389)

This commit is contained in:
Stefan M.
2026-01-05 19:16:32 +01:00
committed by GitHub
parent 35861240a0
commit 0a4281366f
3 changed files with 97 additions and 6 deletions

View File

@@ -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");
* you may not use this file except in compliance with the License.
@@ -263,13 +263,15 @@ constructor(
}
val moduleSource = toModuleSource(moduleUri, inputStream)
val output = evaluator.evaluateOutputFiles(moduleSource)
val realOutputDir = if (outputDir.exists()) outputDir.toRealPath() else outputDir
for ((pathSpec, fileOutput) in output) {
checkPathSpec(pathSpec)
val resolvedPath = outputDir.resolve(pathSpec).normalize()
val resolvedPath = realOutputDir.resolve(pathSpec).normalize()
val realPath = if (resolvedPath.exists()) resolvedPath.toRealPath() else resolvedPath
if (!realPath.startsWith(outputDir)) {
if (!realPath.startsWith(realOutputDir)) {
throw CliException(
"Output file conflict: `output.files` entry `\"$pathSpec\"` in module `$moduleUri` resolves to file path `$realPath`, which is outside output directory `$outputDir`."
"Output file conflict: `output.files` entry `\"$pathSpec\"` in module `$moduleUri` resolves to file path `$realPath`, which is outside output directory `$realOutputDir`."
)
}
val previousOutput = writtenFiles[realPath]

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2025-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.

View File

@@ -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");
* you may not use this file except in compliance with the License.
@@ -1634,6 +1634,95 @@ result = someLib.x
)
}
@Test
@DisabledOnOs(OS.WINDOWS)
fun `multiple file output works with symlinked output directory`() {
val realOutputDir = tempDir.resolve("real-output").createDirectories()
val symlinkOutputDir =
Files.createSymbolicLink(tempDir.resolve("symlink-output"), realOutputDir)
val sourceFile =
writePklFile(
"test.pkl",
"""
pigeon {
name = "Pigeon"
diet = "Seeds"
}
parrot {
name = "Parrot"
diet = "Seeds"
}
output {
files {
["pigeon.json"] {
value = pigeon
renderer = new JsonRenderer {}
}
["birds/parrot.yaml"] {
value = parrot
renderer = new YamlRenderer {}
}
}
}
"""
.trimIndent(),
)
val options =
CliEvaluatorOptions(
CliBaseOptions(sourceModules = listOf(sourceFile), workingDir = tempDir),
multipleFileOutputPath = symlinkOutputDir.toString(),
)
CliEvaluator(options).run()
checkOutputFile(
realOutputDir.resolve("pigeon.json"),
"pigeon.json",
"""
{
"name": "Pigeon",
"diet": "Seeds"
}
"""
.trimIndent(),
)
checkOutputFile(
realOutputDir.resolve("birds/parrot.yaml"),
"parrot.yaml",
"""
name: Parrot
diet: Seeds
"""
.trimIndent(),
)
checkOutputFile(
symlinkOutputDir.resolve("pigeon.json"),
"pigeon.json",
"""
{
"name": "Pigeon",
"diet": "Seeds"
}
"""
.trimIndent(),
)
checkOutputFile(
symlinkOutputDir.resolve("birds/parrot.yaml"),
"parrot.yaml",
"""
name: Parrot
diet: Seeds
"""
.trimIndent(),
)
}
private fun evalModuleThatImportsPackage(certsFile: Path?, testPort: Int = -1) {
val moduleUri =
writePklFile(