Skip to content

Commit

Permalink
Merge pull request #23 from juraj-hrivnak/github
Browse files Browse the repository at this point in the history
GitHub support
  • Loading branch information
juraj-hrivnak authored Sep 24, 2024
2 parents 12c35be + 60978c9 commit 3515411
Show file tree
Hide file tree
Showing 45 changed files with 1,582 additions and 374 deletions.
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/teksturepako/pakku/Version.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

package teksturepako.pakku

const val VERSION = "0.15.1"
const val VERSION = "0.17.1"
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package teksturepako.pakku.api.actions

import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.api.platforms.IProjectProvider
import teksturepako.pakku.api.projects.Project
import teksturepako.pakku.api.projects.ProjectFile
import teksturepako.pakku.cli.ui.dim
Expand Down Expand Up @@ -39,7 +39,7 @@ open class ActionError(
ActionError("Failed to download '$path'.")

class NoHashes(val projectFile: ProjectFile) :
ActionError("File '${projectFile.getPath()}' has no hashes.")
ActionError("File '${projectFile.getPath()}' has no hashes.", isWarning = true)

class HashFailed(val projectFile: ProjectFile, val originalHash: String, val newHash: String) :
ActionError("""Failed to math hash for file '${projectFile.getPath()}'.
Expand Down Expand Up @@ -99,18 +99,18 @@ open class ActionError(
"Could not add ${dim(project.type)} ${project.getFlavoredSlug()}. It is already added."
}

class NotFoundOnPlatform(val project: Project, val platform: Platform) :
ActionError("${project.type} ${project.slug} was not found on ${platform.name}")
class NotFoundOn(val project: Project, val provider: IProjectProvider) :
ActionError("${project.type} ${project.slug} was not found on ${provider.name}")
{
override fun message(arg: String): String =
"${dim(project.type)} ${project.getFlavoredSlug()} was not found on ${platform.name}."
"${dim(project.type)} ${project.getFlavoredSlug()} was not found on ${provider.name}."
}

class NoFilesOnPlatform(val project: Project, val platform: Platform) :
ActionError("No files for ${project.type} ${project.slug} found on ${platform.name}.")
class NoFilesOn(val project: Project, val provider: IProjectProvider) :
ActionError("No files for ${project.type} ${project.slug} found on ${provider.name}.")
{
override fun message(arg: String) =
"No files for ${dim(project.type)} ${project.getFlavoredSlug()} found on ${platform.name}."
"No files for ${dim(project.type)} ${project.getFlavoredSlug()} found on ${provider.name}."
}

class NoFiles(val project: Project, val lockFile: LockFile) :
Expand Down
32 changes: 19 additions & 13 deletions src/commonMain/kotlin/teksturepako/pakku/api/actions/Addition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package teksturepako.pakku.api.actions

import teksturepako.pakku.api.actions.ActionError.*
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.GitHub
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.api.projects.Project

Expand All @@ -28,27 +29,32 @@ suspend fun Project?.createAdditionRequest(
return onError(AlreadyAdded(project))
}

for (platform in platforms)
// We do not have to check platform for GitHub only project
if (project.slug.keys.size > 1 || project.slug.keys.firstOrNull() != GitHub.serialName)
{
// Check if project is on each platform
if (project.isNotOnPlatform(platform))
for (platform in platforms)
{
if (!strict) continue
else
// Check if project is on each platform
if (project.isNotOnPlatform(platform))
{
onError(NotFoundOnPlatform(project, platform))
return
if (!strict) continue
else
{
onError(NotFoundOn(project, platform))
return
}
}
}

// Check if project has files on each platform
if (project.hasNoFilesOnPlatform(platform))
{
onError(NoFilesOnPlatform(project, platform))
isRecommended = false
// Check if project has files on each platform
if (project.hasNoFilesOnPlatform(platform))
{
onError(NoFilesOn(project, platform))
isRecommended = false
}
}
}


// Check if project has any files at all
if (project.hasNoFiles())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,33 @@ suspend fun ExportProfile.export(
.onFailure { onError(this, CouldNotSave(null, it.message)) }
.get() ?: return

// -- FILE CACHE --

// Create parent directory
cacheDir.tryOrNull {
it.createDirectory()
it.setAttribute("dos:hidden", true)
}

val inputDirectory = Path(cacheDir.pathString, this.name)

val results = this.rules.filterNotNull().produceRuleResults(lockFile, configFile, this.name)
val results: List<RuleResult> = this.rules.filterNotNull().produceRuleResults(lockFile, configFile, this.name)

val cachedPaths = results.resolveResults { onError(this, it) }.awaitAll().filterNotNull() +
// Run export rules
val cachedPaths: List<Path> = results.resolveResults { onError(this, it) }.awaitAll().filterNotNull() +
results.finishResults { onError(this, it) }.awaitAll().filterNotNull()

val fileHashes = cachedPaths.filterNot { it.isDirectory() }
// -- FILE CACHE --

/** Map of _absolute paths_ to their _hashes_ for every path not in a directory */
val fileHashes: Map<Path, String> = cachedPaths.filterNot { it.isDirectory() }
.mapNotNull { file ->
file.tryToResult { it.readBytes() }
.onFailure { onError(this, it) }
.get()?.let { file to it }
}
.associate { it.first.absolute() to createHash("sha1", it.second) }

val dirContentHashes = cachedPaths.filter { it.isDirectory() }
/** Map of _absolute paths_ to their _hashes_ for every path in a directory */
val dirContentHashes: Map<Path, String> = cachedPaths.filter { it.isDirectory() }
.mapNotNull { directory ->
directory.tryToResult { it.toFile().walkTopDown() }
.onFailure { onError(this, it) }.get()
Expand Down Expand Up @@ -259,6 +263,12 @@ suspend fun List<RuleResult>.finishResults(
}
}

/**
* Runs through a list of [ExportRules][ExportRule],
* applies data to their [RuleContexts][RuleContext] and transforms them into [RuleResults][RuleResult].
*
* [RuleContext.MissingProject] and [RuleContext.Finished] are applied last.
*/
suspend fun List<ExportRule>.produceRuleResults(
lockFile: LockFile, configFile: ConfigFile, workingSubDir: String
): List<RuleResult>
Expand All @@ -275,8 +285,8 @@ suspend fun List<ExportRule>.produceRuleResults(
} + readProjectOverrides().map {
rule to RuleContext.ExportingProjectOverride(it, lockFile, configFile, workingSubDir)
}
}.map { (rule, ruleEntry) ->
rule.getResult(ruleEntry)
}.map { (exportRule, ruleContext) ->
exportRule.getResult(ruleContext)
}

val missing = results.filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import teksturepako.pakku.api.data.json
import teksturepako.pakku.api.http.Http
import teksturepako.pakku.api.overrides.OverrideType
import teksturepako.pakku.api.overrides.ProjectOverride
import teksturepako.pakku.api.platforms.Multiplatform
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.api.platforms.IProjectProvider
import teksturepako.pakku.api.projects.Project
import teksturepako.pakku.io.tryToResult
import kotlin.io.path.*
Expand Down Expand Up @@ -113,8 +112,8 @@ sealed class RuleContext(open val workingSubDir: String)
{
if (!project.redistributable && !force) return error(NotRedistributable(project))

val projectFile = Multiplatform.platforms.firstNotNullOfOrNull { platform ->
project.getFilesForPlatform(platform).firstOrNull()
val projectFile = IProjectProvider.providers.firstNotNullOfOrNull { provider ->
project.getFilesForProvider(provider).firstOrNull()
} ?: return error(NoFiles(project, lockFile))

val result = onExport(
Expand Down Expand Up @@ -211,7 +210,7 @@ sealed class RuleContext(open val workingSubDir: String)
) : RuleContext(workingSubDir)
{
suspend fun exportAsOverrideFrom(
platform: Platform,
provider: IProjectProvider,
onExport: suspend (
bytesCallback: suspend () -> ByteArray?,
fileName: String,
Expand All @@ -221,8 +220,8 @@ sealed class RuleContext(open val workingSubDir: String)
{
if (!project.redistributable) return error(NotRedistributable(project))

val projectFile = project.getFilesForPlatform(platform).firstOrNull()
?: return error(NoFilesOnPlatform(project, platform))
val projectFile = project.getFilesForProvider(provider).firstOrNull()
?: return error(NoFilesOn(project, provider))

val result = onExport(
// Creates a callback to download the file lazily.
Expand All @@ -232,10 +231,36 @@ sealed class RuleContext(open val workingSubDir: String)
)

return ruleResult(
"exportAsOverrideFrom ${platform.name} ${result.message}",
"exportAsOverrideFrom ${provider.name} ${result.message}",
result.packaging
)
}

suspend fun exportAsOverride(
force: Boolean = false,
excludedProviders: Set<IProjectProvider> = setOf(),
onExport: suspend (
bytesCallback: suspend () -> ByteArray?,
fileName: String,
overridesFolder: String
) -> RuleResult
): RuleResult
{
if (!project.redistributable && !force) return error(NotRedistributable(project))

val projectFile = (IProjectProvider.providers - excludedProviders).firstNotNullOfOrNull { provider ->
project.getFilesForProvider(provider).firstOrNull()
} ?: return error(NoFiles(project, lockFile))

val result = onExport(
// Creates a callback to download the file lazily.
{ projectFile.url?.let { url -> Http().requestByteArray(url) } },
projectFile.fileName,
OverrideType.fromProject(project).folderName
)

return ruleResult("exportAsOverride ${result.message}", result.packaging)
}
}

/** Rule context indicating that all other actions have been finished. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import teksturepako.pakku.api.actions.export.rules.ruleOfCfModpack
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.CurseForge
import teksturepako.pakku.api.platforms.Modrinth
import teksturepako.pakku.compat.exportFileDirector

class CurseForgeProfile(lockFile: LockFile, configFile: ConfigFile) : ExportProfile(
Expand All @@ -18,8 +17,8 @@ class CurseForgeProfile(lockFile: LockFile, configFile: ConfigFile) : ExportProf
val modpackModel = createCfModpackModel(it, lockFile, configFile)
ruleOfCfModpack(modpackModel)
},
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(Modrinth)
else ruleOfCfMissingProjects(Modrinth),
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(excludedProviders = setOf(CurseForge))
else ruleOfCfMissingProjects(),
replacementRule()
),
dependsOn = CurseForge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import teksturepako.pakku.api.actions.export.rules.ruleOfMrMissingProjects
import teksturepako.pakku.api.actions.export.rules.ruleOfMrModpack
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.CurseForge
import teksturepako.pakku.api.platforms.Modrinth
import teksturepako.pakku.compat.exportFileDirector

Expand All @@ -19,8 +18,8 @@ class ModrinthProfile(lockFile: LockFile, configFile: ConfigFile) : ExportProfil
val modpackModel = createMrModpackModel(it, lockFile, configFile)
ruleOfMrModpack(modpackModel)
},
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(CurseForge)
else ruleOfMrMissingProjects(CurseForge),
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(excludedProviders = setOf(Modrinth))
else ruleOfMrMissingProjects(),
replacementRule()
),
dependsOn = Modrinth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import teksturepako.pakku.api.models.cf.CfModpackModel
import teksturepako.pakku.api.models.cf.CfModpackModel.*
import teksturepako.pakku.api.overrides.OverrideType
import teksturepako.pakku.api.platforms.CurseForge
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.api.projects.Project
import teksturepako.pakku.api.projects.ProjectFile

Expand All @@ -35,12 +34,12 @@ fun ruleOfCfModpack(modpackModel: CfModpackModel) = ExportRule {
}
}

fun ruleOfCfMissingProjects(platform: Platform) = ExportRule {
fun ruleOfCfMissingProjects() = ExportRule {
when (it)
{
is MissingProject ->
{
it.exportAsOverrideFrom(platform) { bytesCallback, fileName, _ ->
it.exportAsOverride(excludedProviders = setOf(CurseForge)) { bytesCallback, fileName, _ ->
it.createFile(bytesCallback, OverrideType.OVERRIDE.folderName, it.project.type.folderName, fileName)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ import teksturepako.pakku.api.actions.export.ruleResult
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.data.jsonEncodeDefaults
import teksturepako.pakku.api.data.workingPath
import teksturepako.pakku.api.models.mr.MrModpackModel
import teksturepako.pakku.api.models.mr.MrModpackModel.MrFile
import teksturepako.pakku.api.platforms.GitHub
import teksturepako.pakku.api.platforms.Modrinth
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.api.projects.Project
import teksturepako.pakku.api.projects.ProjectFile
import teksturepako.pakku.api.projects.ProjectSide
import teksturepako.pakku.io.createHash
import teksturepako.pakku.io.readPathBytesOrNull
import kotlin.io.path.Path

fun ruleOfMrModpack(modpackModel: MrModpackModel) = ExportRule {
when (it)
{
is ExportingProject ->
{
val projectFile = it.project.getFilesForPlatform(Modrinth).firstOrNull()
val projectFile = it.project.getFilesForProviders(Modrinth, GitHub).firstOrNull()
?: return@ExportRule it.setMissing()

it.addToMrModpackModel(projectFile, modpackModel)
Expand All @@ -35,12 +39,12 @@ fun ruleOfMrModpack(modpackModel: MrModpackModel) = ExportRule {
}
}

fun ruleOfMrMissingProjects(platform: Platform) = ExportRule {
fun ruleOfMrMissingProjects() = ExportRule {
when (it)
{
is MissingProject ->
{
it.exportAsOverrideFrom(platform) { bytesCallback, fileName, overridesDir ->
it.exportAsOverride(excludedProviders = setOf(Modrinth, GitHub)) { bytesCallback, fileName, overridesDir ->
it.createFile(bytesCallback, overridesDir, it.project.type.folderName, fileName)
}
}
Expand Down Expand Up @@ -82,11 +86,11 @@ fun createMrModpackModel(
)
}

fun ProjectFile.toMrFile(parentProject: Project): MrFile?
suspend fun ProjectFile.toMrFile(parentProject: Project): MrFile?
{
if (this.type != Modrinth.serialName) return null
if (this.type !in listOf(Modrinth.serialName, GitHub.serialName)) return null

val serverRequired = if (parentProject.side in listOf(ProjectSide.SERVER, ProjectSide.BOTH))
val serverSide = if (parentProject.side in listOf(ProjectSide.SERVER, ProjectSide.BOTH) || parentProject.side == null)
{
"required"
}
Expand All @@ -95,12 +99,18 @@ fun ProjectFile.toMrFile(parentProject: Project): MrFile?
return MrFile(
path = "${parentProject.type.folderName}/${this.fileName}",
hashes = MrFile.Hashes(
sha512 = this.hashes?.get("sha512")!!,
sha1 = this.hashes["sha1"]!!
sha512 = this.hashes?.get("sha512")
?: readPathBytesOrNull(Path(workingPath, parentProject.type.folderName, fileName))?.let { bytes ->
createHash("sha512", bytes)
} ?: return null,
sha1 = this.hashes?.get("sha1")
?: readPathBytesOrNull(Path(workingPath, parentProject.type.folderName, fileName))?.let { bytes ->
createHash("sha1", bytes)
} ?: return null,
),
env = MrFile.Env(
client = "required",
server = serverRequired,
server = serverSide,
),
// Replace ' ' in URL with '+'
downloads = setOf(this.url!!.replace(" ", "+")),
Expand Down
Loading

0 comments on commit 3515411

Please sign in to comment.