feat: Allow checking out repo PRs
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package moe.nea.firmament.commands
|
package moe.nea.firmament.commands
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher
|
import com.mojang.brigadier.CommandDispatcher
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType.string
|
import com.mojang.brigadier.arguments.StringArgumentType.string
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||||
@@ -130,6 +131,15 @@ fun firmamentCommand() = literal("firmament") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
thenLiteral("repo") {
|
thenLiteral("repo") {
|
||||||
|
thenLiteral("checkpr") {
|
||||||
|
thenArgument("prnum", IntegerArgumentType.integer(1)) { prnum ->
|
||||||
|
thenExecute {
|
||||||
|
val prnum = this[prnum]
|
||||||
|
source.sendFeedback(tr("firmament.repo.reload.pr", "Temporarily reloading repo from PR #${prnum}."))
|
||||||
|
RepoManager.downloadOverridenBranch("refs/pull/$prnum/head")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
thenLiteral("reload") {
|
thenLiteral("reload") {
|
||||||
thenLiteral("fetch") {
|
thenLiteral("fetch") {
|
||||||
thenExecute {
|
thenExecute {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
package moe.nea.firmament.repo
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
@@ -28,101 +26,102 @@ import moe.nea.firmament.util.iterate
|
|||||||
|
|
||||||
object RepoDownloadManager {
|
object RepoDownloadManager {
|
||||||
|
|
||||||
val repoSavedLocation = Firmament.DATA_DIR.resolve("repo-extracted")
|
val repoSavedLocation = Firmament.DATA_DIR.resolve("repo-extracted")
|
||||||
val repoMetadataLocation = Firmament.DATA_DIR.resolve("loaded-repo-sha.txt")
|
val repoMetadataLocation = Firmament.DATA_DIR.resolve("loaded-repo-sha.txt")
|
||||||
|
|
||||||
private fun loadSavedVersionHash(): String? =
|
private fun loadSavedVersionHash(): String? =
|
||||||
if (repoSavedLocation.exists()) {
|
if (repoSavedLocation.exists()) {
|
||||||
if (repoMetadataLocation.exists()) {
|
if (repoMetadataLocation.exists()) {
|
||||||
try {
|
try {
|
||||||
repoMetadataLocation.readText().trim()
|
repoMetadataLocation.readText().trim()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
private fun saveVersionHash(versionHash: String) {
|
private fun saveVersionHash(versionHash: String) {
|
||||||
latestSavedVersionHash = versionHash
|
latestSavedVersionHash = versionHash
|
||||||
repoMetadataLocation.writeText(versionHash)
|
repoMetadataLocation.writeText(versionHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestSavedVersionHash: String? = loadSavedVersionHash()
|
var latestSavedVersionHash: String? = loadSavedVersionHash()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private class GithubCommitsResponse(val sha: String)
|
private class GithubCommitsResponse(val sha: String)
|
||||||
|
|
||||||
private suspend fun requestLatestGithubSha(): String? {
|
private suspend fun requestLatestGithubSha(branchOverride: String?): String? {
|
||||||
if (RepoManager.Config.branch == "prerelease") {
|
if (RepoManager.Config.branch == "prerelease") {
|
||||||
RepoManager.Config.branch = "master"
|
RepoManager.Config.branch = "master"
|
||||||
}
|
}
|
||||||
val response =
|
val response =
|
||||||
Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${RepoManager.Config.branch}")
|
Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${branchOverride ?: RepoManager.Config.branch}")
|
||||||
if (response.status.value != 200) {
|
if (response.status.value != 200) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return response.body<GithubCommitsResponse>().sha
|
return response.body<GithubCommitsResponse>().sha
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) {
|
private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) {
|
||||||
val response = Firmament.httpClient.get(url)
|
val response = Firmament.httpClient.get(url)
|
||||||
val targetFile = Files.createTempFile("firmament-repo", ".zip")
|
val targetFile = Files.createTempFile("firmament-repo", ".zip")
|
||||||
val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
|
||||||
response.bodyAsChannel().copyTo(outputChannel)
|
response.bodyAsChannel().copyTo(outputChannel)
|
||||||
targetFile
|
targetFile
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads the latest repository from github, setting [latestSavedVersionHash].
|
* Downloads the latest repository from github, setting [latestSavedVersionHash].
|
||||||
* @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update)
|
* @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update)
|
||||||
*/
|
*/
|
||||||
suspend fun downloadUpdate(force: Boolean): Boolean = withContext(CoroutineName("Repo Update Check")) {
|
suspend fun downloadUpdate(force: Boolean, branch: String? = null): Boolean =
|
||||||
val latestSha = requestLatestGithubSha()
|
withContext(CoroutineName("Repo Update Check")) {
|
||||||
if (latestSha == null) {
|
val latestSha = requestLatestGithubSha(branch)
|
||||||
logger.warn("Could not request github API to retrieve latest REPO sha.")
|
if (latestSha == null) {
|
||||||
return@withContext false
|
logger.warn("Could not request github API to retrieve latest REPO sha.")
|
||||||
}
|
return@withContext false
|
||||||
val currentSha = loadSavedVersionHash()
|
}
|
||||||
if (latestSha != currentSha || force) {
|
val currentSha = loadSavedVersionHash()
|
||||||
val requestUrl =
|
if (latestSha != currentSha || force) {
|
||||||
"https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip"
|
val requestUrl =
|
||||||
logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
|
"https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip"
|
||||||
val zipFile = downloadGithubArchive(requestUrl)
|
logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
|
||||||
logger.info("Download repository zip file to $zipFile. Deleting old repository")
|
val zipFile = downloadGithubArchive(requestUrl)
|
||||||
withContext(IO) { repoSavedLocation.toFile().deleteRecursively() }
|
logger.info("Download repository zip file to $zipFile. Deleting old repository")
|
||||||
logger.info("Extracting new repository")
|
withContext(IO) { repoSavedLocation.toFile().deleteRecursively() }
|
||||||
withContext(IO) { extractNewRepository(zipFile) }
|
logger.info("Extracting new repository")
|
||||||
logger.info("Repository loaded on disk.")
|
withContext(IO) { extractNewRepository(zipFile) }
|
||||||
saveVersionHash(latestSha)
|
logger.info("Repository loaded on disk.")
|
||||||
return@withContext true
|
saveVersionHash(latestSha)
|
||||||
} else {
|
return@withContext true
|
||||||
logger.debug("Repository on latest sha $currentSha. Not performing update")
|
} else {
|
||||||
return@withContext false
|
logger.debug("Repository on latest sha $currentSha. Not performing update")
|
||||||
}
|
return@withContext false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun extractNewRepository(zipFile: Path) {
|
private fun extractNewRepository(zipFile: Path) {
|
||||||
repoSavedLocation.createDirectories()
|
repoSavedLocation.createDirectories()
|
||||||
ZipInputStream(zipFile.inputStream()).use { cis ->
|
ZipInputStream(zipFile.inputStream()).use { cis ->
|
||||||
while (true) {
|
while (true) {
|
||||||
val entry = cis.nextEntry ?: break
|
val entry = cis.nextEntry ?: break
|
||||||
if (entry.isDirectory) continue
|
if (entry.isDirectory) continue
|
||||||
val extractedLocation =
|
val extractedLocation =
|
||||||
repoSavedLocation.resolve(
|
repoSavedLocation.resolve(
|
||||||
entry.name.substringAfter('/', missingDelimiterValue = "")
|
entry.name.substringAfter('/', missingDelimiterValue = "")
|
||||||
)
|
)
|
||||||
if (repoSavedLocation !in extractedLocation.iterate { it.parent }) {
|
if (repoSavedLocation !in extractedLocation.iterate { it.parent }) {
|
||||||
logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
|
logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
|
||||||
throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
|
throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
|
||||||
}
|
}
|
||||||
extractedLocation.parent.createDirectories()
|
extractedLocation.parent.createDirectories()
|
||||||
extractedLocation.outputStream().use { cis.copyTo(it) }
|
extractedLocation.outputStream().use { cis.copyTo(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,13 @@ object RepoManager {
|
|||||||
|
|
||||||
fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem)
|
fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem)
|
||||||
|
|
||||||
|
fun downloadOverridenBranch(branch: String) {
|
||||||
|
Firmament.coroutineScope.launch {
|
||||||
|
RepoDownloadManager.downloadUpdate(true, branch)
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun launchAsyncUpdate(force: Boolean = false) {
|
fun launchAsyncUpdate(force: Boolean = false) {
|
||||||
Firmament.coroutineScope.launch {
|
Firmament.coroutineScope.launch {
|
||||||
RepoDownloadManager.downloadUpdate(force)
|
RepoDownloadManager.downloadUpdate(force)
|
||||||
|
|||||||
Reference in New Issue
Block a user