Skip to content

Commit

Permalink
No longer unzip remote templates
Browse files Browse the repository at this point in the history
Instead read directly inside them, and allow to configure a different
  repo root in cases like GitHub's
  zips, that have a root directory
  named after the repo and branch name
  • Loading branch information
RedNesto committed Jul 2, 2024
1 parent ce11b6b commit ac7dcfa
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package com.demonwav.mcdev.creator.custom.providers

import com.demonwav.mcdev.MinecraftSettings
import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.creator.custom.TemplateDescriptor
import com.demonwav.mcdev.creator.modalityState
import com.demonwav.mcdev.update.PluginUtil
import com.demonwav.mcdev.util.refreshSync
Expand All @@ -37,6 +38,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() {

private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/v\$version.zip"
private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates")
private val builtinTemplatesInnerPath = "templates-${TemplateDescriptor.FORMAT_VERSION}"
private var repoUpdated: Boolean = false

override val label: String = MCDevBundle("template.provider.builtin.label")
Expand All @@ -49,7 +51,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() {
return
}

if (doUpdateRepo(indicator, label, builtinRepoUrl, builtinTemplatesPath)) {
if (doUpdateRepo(indicator, label, builtinRepoUrl)) {
repoUpdated = true
}
}
Expand All @@ -58,6 +60,11 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() {
context: WizardContext,
repo: MinecraftSettings.TemplateRepo
): Collection<LoadedTemplate> {
val remoteTemplates = doLoadTemplates(context, repo, builtinTemplatesInnerPath)
if (remoteTemplates.isNotEmpty()) {
return remoteTemplates
}

val repoRoot = builtinTemplatesPath.virtualFile
?: return emptyList()
repoRoot.refreshSync(context.modalityState)
Expand Down
117 changes: 71 additions & 46 deletions src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import com.demonwav.mcdev.creator.modalityState
import com.demonwav.mcdev.creator.selectProxy
import com.demonwav.mcdev.update.PluginUtil
import com.demonwav.mcdev.util.refreshSync
import com.demonwav.mcdev.util.virtualFile
import com.github.kittinunf.fuel.core.FuelManager
import com.github.kittinunf.result.getOrNull
import com.github.kittinunf.result.onError
Expand All @@ -40,20 +39,19 @@ import com.intellij.openapi.observable.properties.PropertyGraph
import com.intellij.openapi.observable.util.trim
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.COLUMNS_LARGE
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.columns
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.textValidation
import com.intellij.util.io.ZipUtil
import com.intellij.util.io.createDirectories
import java.nio.file.Path
import javax.swing.JComponent
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.moveTo
import kotlin.io.path.absolutePathString
import kotlin.io.path.exists
import kotlin.io.path.writeBytes

open class RemoteTemplateProvider : TemplateProvider {
Expand All @@ -73,7 +71,7 @@ open class RemoteTemplateProvider : TemplateProvider {
continue
}

if (doUpdateRepo(indicator, repo.name, remote.url, remote.getDestination(repo.name))) {
if (doUpdateRepo(indicator, repo.name, remote.url)) {
updatedTemplates.add(remote.url)
}
}
Expand All @@ -82,12 +80,11 @@ open class RemoteTemplateProvider : TemplateProvider {
protected fun doUpdateRepo(
indicator: ProgressIndicator,
repoName: String,
originalRepoUrl: String,
destination: Path
originalRepoUrl: String
): Boolean {
indicator.text2 = "Updating remote repository $repoName"

val repoUrl = originalRepoUrl.replace("\$version", TemplateDescriptor.FORMAT_VERSION.toString())
val repoUrl = replaceVariables(originalRepoUrl)

val manager = FuelManager()
manager.proxy = selectProxy(repoUrl)
Expand All @@ -102,26 +99,9 @@ open class RemoteTemplateProvider : TemplateProvider {
}.getOrNull() ?: return false

try {
val remoteTemplatesDir = destination
remoteTemplatesDir.createDirectories()
val zipPath = remoteTemplatesDir.resolveSibling("$repoName.zip")
val zipPath = RemoteTemplateRepo.getDestinationZip(repoName)
zipPath.parent.createDirectories()
zipPath.writeBytes(data)
FileUtil.deleteRecursively(remoteTemplatesDir)
ZipUtil.extract(zipPath, remoteTemplatesDir, null)

// Loose way to find out if the url is a github repo archive
// In such cases there is a single directory in the root directory of the zip
// We simply move all its children to the base directory so the rest of the system uses the correct
// root directory for this repository
val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/[vV]?(.*?).zip".toRegex()
val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(repoUrl)
if (githubRepoArchiveMatcher != null) {
val githubRepoName = githubRepoArchiveMatcher.groupValues[2]
val branchName = githubRepoArchiveMatcher.groupValues[3]
for (child in remoteTemplatesDir.resolve("$githubRepoName-$branchName").listDirectoryEntries()) {
child.moveTo(remoteTemplatesDir.resolve(child.fileName))
}
}

thisLogger().info("Remote templates repository update applied successfully")
return true
Expand All @@ -138,15 +118,46 @@ open class RemoteTemplateProvider : TemplateProvider {
context: WizardContext,
repo: MinecraftSettings.TemplateRepo
): Collection<LoadedTemplate> {
val remote = RemoteTemplateRepo.deserialize(repo.data)
val remoteRepo = RemoteTemplateRepo.deserialize(repo.data)
?: return emptyList()
val repoRoot = remote.getDestination(repo.name).virtualFile
return doLoadTemplates(context, repo, remoteRepo.innerPath)
}

protected fun doLoadTemplates(
context: WizardContext,
repo: MinecraftSettings.TemplateRepo,
rawInnerPath: String
): List<LoadedTemplate> {
val remoteRootPath = RemoteTemplateRepo.getDestinationZip(repo.name)
if (!remoteRootPath.exists()) {
return emptyList()
}

val archiveRoot = remoteRootPath.absolutePathString() + JarFileSystem.JAR_SEPARATOR

val fs = JarFileSystem.getInstance()
val rootFile = fs.refreshAndFindFileByPath(archiveRoot)
?: return emptyList()
val modalityState = context.modalityState
repoRoot.refreshSync(modalityState)
rootFile.refreshSync(modalityState)

val innerPath = replaceVariables(rawInnerPath)
val repoRoot = if (innerPath.isNotBlank()) {
rootFile.findFileByRelativePath(innerPath)
} else {
rootFile
}

if (repoRoot == null) {
return emptyList()
}

return TemplateProvider.findTemplates(modalityState, repoRoot)
}

private fun replaceVariables(originalRepoUrl: String): String =
originalRepoUrl.replace("\$version", TemplateDescriptor.FORMAT_VERSION.toString())

override fun setupConfigUi(
data: String,
dataSetter: (String) -> Unit
Expand All @@ -155,6 +166,7 @@ open class RemoteTemplateProvider : TemplateProvider {
val defaultRepo = RemoteTemplateRepo.deserialize(data)
val urlProperty = propertyGraph.property(defaultRepo?.url ?: "").trim()
val autoUpdateProperty = propertyGraph.property(defaultRepo?.autoUpdate != false)
val innerPathProperty = propertyGraph.property(defaultRepo?.innerPath ?: "").trim()

return panel {
row(MCDevBundle("creator.ui.custom.remote.url.label")) {
Expand All @@ -166,37 +178,50 @@ open class RemoteTemplateProvider : TemplateProvider {
.textValidation(BuiltinValidations.nonBlank)
}

row(MCDevBundle("creator.ui.custom.remote.inner_path.label")) {
textField()
.comment(MCDevBundle("creator.ui.custom.remote.inner_path.comment"))
.align(AlignX.FILL)
.columns(COLUMNS_LARGE)
.bindText(innerPathProperty)
}

row {
checkBox(MCDevBundle("creator.ui.custom.remote.auto_update.label"))
.bindSelected(autoUpdateProperty)
}

onApply {
val repo = RemoteTemplateRepo(urlProperty.get(), autoUpdateProperty.get())
val repo = RemoteTemplateRepo(urlProperty.get(), autoUpdateProperty.get(), innerPathProperty.get())
dataSetter(repo.serialize())
}
}
}

data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) {

fun getDestination(repoName: String): Path {
return PathManager.getSystemDir().resolve("mcdev-templates").resolve(repoName)
}
data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean, val innerPath: String) {

fun serialize(): String = "$url\n$autoUpdate"
fun serialize(): String = "$url\n$autoUpdate\n$innerPath"

companion object {

val templatesBaseDir: Path
get() = PathManager.getSystemDir().resolve("mcdev-templates")

fun getDestinationZip(repoName: String): Path {
return templatesBaseDir.resolve("$repoName.zip")
}

fun deserialize(data: String): RemoteTemplateRepo? {
val lines = data.lines()
return when (lines.size) {
0 -> null
1 -> RemoteTemplateRepo(lines[0], true)
else -> {
val (url, autoUpdate) = lines
RemoteTemplateRepo(url, autoUpdate.toBoolean())
}
if (data.isBlank()) {
return null
}

val lines = data.lines()
return RemoteTemplateRepo(
lines[0],
lines.getOrNull(1).toBoolean(),
lines.getOrNull(2) ?: "",
)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages/MinecraftDevelopment.properties
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ creator.ui.custom.archive.dialog.title=Template Archive
creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template
creator.ui.custom.remote.url.label=Download URL:
creator.ui.custom.remote.url.comment='$version' will be replaced by the template descriptor version currently in use
creator.ui.custom.remote.inner_path.label=Inner Path:
creator.ui.custom.remote.inner_path.comment='$version' will be replaced by the template descriptor version currently in use
creator.ui.custom.remote.auto_update.label=Auto update

creator.ui.warn.no_properties=This template has no properties
Expand Down

0 comments on commit ac7dcfa

Please sign in to comment.