Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,42 @@ internal class EmbedCodePluginSpec {
result.output shouldContain "Downloading Embed Code $TEST_RELEASE_TAG"
}

@Test
fun `install the Linux ZIP release asset`() {
val releaseTag = "v1.2.5"
val asset = releaseDirectory.resolve(
"download/$releaseTag/embed-code-linux.zip",
)
Files.createDirectories(asset.parent)
ZipOutputStream(Files.newOutputStream(asset)).use { zip ->
zip.putNextEntry(ZipEntry("embed-code-linux"))
zip.write("Linux executable".toByteArray())
zip.closeEntry()
}
writeBuildFile(version = releaseTag, sha256 = sha256(asset))
selectLinuxReleaseAsset()

val result = runner(":installEmbedCode").build()

result.task(":installEmbedCode")?.outcome shouldBe TaskOutcome.SUCCESS
Files.readString(installedExecutable(releaseTag)) shouldBe "Linux executable"
}

@Test
fun `install a bare Linux asset from a release published before ZIP packaging`() {
val asset = releaseDirectory.resolve(
"download/$TEST_RELEASE_TAG/embed-code-linux",
)
Files.writeString(asset, "Legacy Linux executable")
writeBuildFile(sha256 = sha256(asset))
selectLinuxReleaseAsset()

val result = runner(":installEmbedCode").build()

result.task(":installEmbedCode")?.outcome shouldBe TaskOutcome.SUCCESS
Files.readString(installedExecutable()) shouldBe "Legacy Linux executable"
}

@Test
fun `reuse the verified default executable without another download`() {
val downloads = AtomicInteger()
Expand Down Expand Up @@ -340,6 +376,7 @@ internal class EmbedCodePluginSpec {
val platform = EmbedCodePlatform.detect(
System.getProperty("os.name"),
System.getProperty("os.arch"),
TEST_RELEASE_TAG,
)
val taskTemporaryDirectory = projectDirectory.resolve("build/tmp/installEmbedCode")
Files.createDirectories(taskTemporaryDirectory)
Expand Down Expand Up @@ -757,6 +794,7 @@ internal class EmbedCodePluginSpec {
val platform = EmbedCodePlatform.detect(
System.getProperty("os.name"),
System.getProperty("os.arch"),
TEST_RELEASE_TAG,
)
val source = "$baseUrl/download/$TEST_RELEASE_TAG/${platform.assetName}"
result.task(":installEmbedCode")?.outcome shouldBe TaskOutcome.FAILED
Expand Down Expand Up @@ -1002,6 +1040,23 @@ internal class EmbedCodePluginSpec {
)
}

/**
* Overrides the installation task to select the Linux AMD64 release asset.
*/
private fun selectLinuxReleaseAsset() {
Files.writeString(
projectDirectory.resolve("build.gradle.kts"),
"""

tasks.named<io.spine.embedcode.gradle.InstallEmbedCodeTask>("installEmbedCode") {
operatingSystem.set("Linux")
architecture.set("amd64")
}
""".trimIndent(),
StandardOpenOption.APPEND,
)
}

/**
* Returns the cache directory for [releaseTag].
*/
Expand Down Expand Up @@ -1070,6 +1125,7 @@ internal class EmbedCodePluginSpec {
val platform = EmbedCodePlatform.detect(
System.getProperty("os.name"),
System.getProperty("os.arch"),
tag,
)
val versionDirectory = root.resolve("download/$tag")
Files.createDirectories(versionDirectory)
Expand Down Expand Up @@ -1117,6 +1173,7 @@ internal class EmbedCodePluginSpec {
val platform = EmbedCodePlatform.detect(
System.getProperty("os.name"),
System.getProperty("os.arch"),
tag.trim(),
)
val asset = root.resolve("download/${tag.trim()}/${platform.assetName}")
return sha256(asset)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,13 @@ internal data class EmbedCodePlatform(
}

/**
* Selects the release asset for [osName] and [architecture].
* Selects the release asset for [osName], [architecture], and [releaseTag].
*/
fun detect(osName: String, architecture: String): EmbedCodePlatform {
fun detect(
osName: String,
architecture: String,
releaseTag: String,
): EmbedCodePlatform {
val os = osName.lowercase(Locale.ROOT)
val arch = architecture.lowercase(Locale.ROOT)
val isAmd64 = arch == "amd64" || arch == "x86_64"
Expand All @@ -70,7 +74,11 @@ internal data class EmbedCodePlatform(
)

os.contains("linux") && isAmd64 -> EmbedCodePlatform(
"embed-code-linux",
if (releaseTag in bareLinuxAssetReleases) {
"embed-code-linux"
} else {
"embed-code-linux.zip"
},
"embed-code-linux",
)

Expand All @@ -85,5 +93,13 @@ internal data class EmbedCodePlatform(
)
}
}

/**
* All releases published before Linux ZIP packaging was introduced.
*
* The upstream release workflow defines ZIP packaging for subsequent releases.
* Update this mapping if that workflow changes.
*/
private val bareLinuxAssetReleases = setOf("v1.2.3", "v1.2.4")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix (design / diagnostics): this allowlist inverts the trust boundary — every release not listed, including any future embed-code-go release, is assumed to ship embed-code-linux.zip. That is correct today (v1.2.3/v1.2.4 are the only releases and both ship the bare asset), but it hard-codes the assumption that every release after v1.2.4 packages Linux as a ZIP.

If a future release ships the bare asset instead, or the ZIP switch slips past v1.2.5, Linux installs fail with a plain HTTP 404 ... embed-code-linux.zip. addReleaseTagMigrationHint only covers a missing v prefix, so nothing points the user at the packaging change.

Consider (a) recording the cross-repo constraint — new embed-code-go releases must package Linux as .zip — in PROJECT.md's compatibility/trust notes or a comment here, and/or (b) extending the download-failure hint to mention the bare↔ZIP fallback.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ public abstract class InstallEmbedCodeTask : DefaultTask() {
hostOperatingSystem,
hostArchitecture,
)
val platform = EmbedCodePlatform.detect(hostOperatingSystem, hostArchitecture)
val platform = EmbedCodePlatform.detect(
hostOperatingSystem,
hostArchitecture,
releaseTag,
)
val asset = platform.assetName
val baseUrl = trimTrailingSlashes(downloadBaseUrl.get())
val installationRoot = installationDirectory.get().asFile.toPath()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,42 @@ internal class EmbedCodePlatformSpec {
fun `select Apple silicon asset`() {
assertEquals(
EmbedCodePlatform("embed-code-macos-arm64.zip", "embed-code-macos-arm64"),
EmbedCodePlatform.detect("Mac OS X", "aarch64"),
EmbedCodePlatform.detect("Mac OS X", "aarch64", "v1.2.4"),
)
}

@Test
fun `select Intel macOS asset`() {
assertEquals(
EmbedCodePlatform("embed-code-macos-x64.zip", "embed-code-macos-x64"),
EmbedCodePlatform.detect("Mac OS X", "x86_64"),
EmbedCodePlatform.detect("Mac OS X", "x86_64", "v1.2.4"),
)
}

@Test
fun `select Linux asset`() {
fun `select Linux ZIP asset for a new release`() {
assertEquals(
EmbedCodePlatform("embed-code-linux", "embed-code-linux"),
EmbedCodePlatform.detect("Linux", "amd64"),
EmbedCodePlatform("embed-code-linux.zip", "embed-code-linux"),
EmbedCodePlatform.detect("Linux", "amd64", "v1.2.5"),
)
}

@Test
fun `select bare Linux asset for releases published before ZIP packaging`() {
listOf("v1.2.3", "v1.2.4").forEach { releaseTag ->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the forEach asserts without identifying the tag, so a regression for one release reports a bare equality failure with no indication of which tag failed. Passing the tag as the assertion message (assertEquals(expected, actual, releaseTag)) makes the failure self-describing. Also note this list literally duplicates the production bareLinuxAssetReleases set — acceptable, just kept in sync manually.

assertEquals(
EmbedCodePlatform("embed-code-linux", "embed-code-linux"),
EmbedCodePlatform.detect("Linux", "amd64", releaseTag),
releaseTag,
)
}
}

@Test
fun `select Windows asset`() {
assertEquals(
EmbedCodePlatform("embed-code-windows.exe", "embed-code-windows.exe"),
EmbedCodePlatform.detect("Windows 11", "amd64"),
EmbedCodePlatform.detect("Windows 11", "amd64", "v1.2.4"),
)
}

Expand All @@ -80,7 +91,7 @@ internal class EmbedCodePlatformSpec {
@Test
fun `reject platform without release binary`() {
val error = assertThrows(GradleException::class.java) {
EmbedCodePlatform.detect("Linux", "aarch64")
EmbedCodePlatform.detect("Linux", "aarch64", "v1.2.4")
}

assertEquals(
Expand Down