Fix actions

This commit is contained in:
ZekerZhayard committed 2026-05-21 13:04:43 +08:00
1 parent 8b5d440ed7
commit 756336e533
22 files changed
+334 -829

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
# Composite action: upload jars to R2/COS, generate and upload JSON metadata
name: Publish
description: Upload built artifacts to object storage and publish JSON metadata
inputs:
artifact-dir:
description: Directory containing built artifacts (relative to workspace)
required: true
type:
description: Release type - 'beta' or 'release'
required: true
runs:
using: composite
steps:
- uses: actions/checkout@v4
with:
clean: false # preserve artifact files already downloaded before this action
# Upload jars to R2 (S3-compatible, mods/ prefix)
- uses: michaelliao/sync-s3-compatible-action@v1
with:
SYNC_DIR: ${{ inputs.artifact-dir }}
BUCKET_URL: ${{ env.R2_BASE_URL }}
BUCKET_NAME: ${{ env.R2_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ env.R2_SECRET_ID }}
AWS_SECRET_ACCESS_KEY: ${{ env.R2_SECRET_KEY }}
TARGET_PATH: mods/
# Upload jars to COS (qcloud mode, mods/ prefix)
- uses: michaelliao/sync-s3-compatible-action@v1
with:
SYNC_TYPE: qcloud
SYNC_DIR: ${{ inputs.artifact-dir }}
BUCKET_NAME: ${{ env.COS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ env.COS_SECRET_ID }}
AWS_SECRET_ACCESS_KEY: ${{ env.COS_SECRET_KEY }}
BUCKET_REGION: ap-shanghai
TARGET_PATH: mods/
# Generate JSON metadata, upload to R2/COS root, and output CDN URLs
- name: Generate and upload JSON metadata
id: publish-json
shell: pwsh
run: .github/scripts/Publish-Json.ps1 -ArtifactDir '${{ inputs.artifact-dir }}' -Type '${{ inputs.type }}'
# Refresh Tencent Cloud CDN cache for the JSON files
- name: Refresh CDN
uses: tencentcloud/cdn-refresh-action@v1
with:
urls: ${{ steps.publish-json.outputs.cdn-urls }}
secret-id: ${{ env.COS_SECRET_ID }}
secret-key: ${{ env.COS_SECRET_KEY }}
continue-on-error: true
+29
View File
@@ -0,0 +1,29 @@
param()
$ErrorActionPreference = "Stop"
$props = Get-Content "gradle.properties"
function Get-Prop($key) {
($props | Select-String "^$key=") -replace "^.*=", "" | ForEach-Object { $_.Trim() }
}
function Set-MultilineOutput($name, $value) {
"$name<<EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
$value | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
$loaders = Get-Prop "loaders"
$gameVersions = Get-Prop "game_versions"
$java = Get-Prop "java"
$version = Get-Prop "mod_version"
$loadersList = $loaders -split "," | ForEach-Object { $_.Trim() }
$gameVersionsList = $gameVersions -split "," | ForEach-Object { $_.Trim() }
$javaList = $java -split "," | ForEach-Object { $_.Trim() }
Set-MultilineOutput "loaders" $loadersList
Set-MultilineOutput "game-versions" $gameVersionsList
Set-MultilineOutput "java" $javaList
"short-version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
+82
View File
@@ -0,0 +1,82 @@
param(
[string]$ArtifactDir,
[string]$Type # "beta" or "release"
)
$ErrorActionPreference = "Stop"
# ---- Validate required env vars ----
foreach ($var in @('R2_SECRET_ID', 'R2_SECRET_KEY', 'R2_BUCKET', 'R2_BASE_URL')) {
if (-not $env:$var) { throw "Missing required env: $var" }
}
$suffix = if ($Type -eq "beta") { "-beta" } else { "" }
# ---- Find the built jar ----
$jar = Get-ChildItem -Path $ArtifactDir -Filter "*.jar" | Where-Object { $_.Name -notlike "*-sources.jar" } | Select-Object -First 1
if (-not $jar) { throw "No jar found in $ArtifactDir" }
$jarFilename = $jar.Name
$jarKey = "mods/$jarFilename"
# ---- Derive short version from gradle.properties + build number ----
# mod_version=15.0, IS_SNAPSHOT=true, GITHUB_RUN_NUMBER=33 -> 15.0-s33
# mod_version=15.0, IS_SNAPSHOT=false -> 15.0
$version = ((Get-Content "gradle.properties" | Select-String "^mod_version=") -replace "^mod_version=", "").Trim()
$isSnapshot = if ($env:IS_SNAPSHOT) { $env:IS_SNAPSHOT -ne "false" } else { $true }
if ($isSnapshot) {
$shortVersion = "$version-s$env:GITHUB_RUN_NUMBER"
} else {
$shortVersion = "$version"
}
# ---- Read MC major versions from gradle.properties ----
$mcVersions = ((Get-Content "gradle.properties" | Select-String "^minecraft_major_versions=") -replace "^.*=", "").Trim()
$mcVersionList = $mcVersions -split "," | ForEach-Object { $_.Trim() }
$baseUrl = $env:R2_BASE_URL.TrimEnd('/')
# ---- Generate latest(-beta).json ----
$latestJson = @{
version = $shortVersion
downloads = @{ Universal = "$baseUrl/$jarKey" }
launchermeta = @{}
} | ConvertTo-Json -Depth 10
Set-Content -Path "$ArtifactDir/latest$suffix.json" -Value $latestJson -NoNewline
# ---- Generate detail(-beta).json ----
$details = [ordered]@{}
foreach ($mcVer in $mcVersionList) {
$details[$mcVer] = @{ Universal = "$baseUrl/$jarKey" }
}
$detailJson = @{
version = $shortVersion
timestamp = [System.DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
details = $details
} | ConvertTo-Json -Depth 10
Set-Content -Path "$ArtifactDir/detail$suffix.json" -Value $detailJson -NoNewline
# ---- Upload JSON files to R2 ----
$env:AWS_ACCESS_KEY_ID = $env:R2_SECRET_ID
$env:AWS_SECRET_ACCESS_KEY = $env:R2_SECRET_KEY
$env:AWS_DEFAULT_REGION = "auto"
aws s3 cp "$ArtifactDir/latest$suffix.json" "s3://$env:R2_BUCKET/latest$suffix.json" --endpoint-url $($env:R2_BASE_URL.TrimEnd('/'))
aws s3 cp "$ArtifactDir/detail$suffix.json" "s3://$env:R2_BUCKET/detail$suffix.json" --endpoint-url $($env:R2_BASE_URL.TrimEnd('/'))
# ---- Upload JSON files to COS ----
try {
pip install coscmd -q --disable-pip-version-check
coscmd config -a $env:COS_SECRET_ID -s $env:COS_SECRET_KEY -b $env:COS_BUCKET -r ap-shanghai
coscmd upload "$ArtifactDir/latest$suffix.json" "latest$suffix.json"
coscmd upload "$ArtifactDir/detail$suffix.json" "detail$suffix.json"
} catch {
Write-Warning "COS JSON upload failed: $_"
}
# ---- Output CDN URLs for subsequent refresh step ----
$cdnBase = "https://csl.littleservice.cn"
"cdn-urls<<EOF" | Out-File $env:GITHUB_OUTPUT -Append
"${cdnBase}/latest${suffix}.json" | Out-File $env:GITHUB_OUTPUT -Append
"${cdnBase}/detail${suffix}.json" | Out-File $env:GITHUB_OUTPUT -Append
"EOF" | Out-File $env:GITHUB_OUTPUT -Append
-15
View File
@@ -1,15 +0,0 @@
param(
[string] $GradleCommand = "./gradlew.bat"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path
Set-Location -LiteralPath $RepositoryRoot
& $GradleCommand clean build --stacktrace
if ($LASTEXITCODE -ne 0) {
throw "Gradle build failed with exit code $LASTEXITCODE."
}
-51
View File
@@ -1,51 +0,0 @@
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$RunnerTemp = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) {
[System.IO.Path]::GetTempPath()
} else {
$env:RUNNER_TEMP
}
function Add-ActionPath {
param([Parameter(Mandatory = $true)][string] $Path)
$env:Path = "$Path;$env:Path"
if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) {
Add-Content -LiteralPath $env:GITHUB_PATH -Value $Path
}
}
if (-not (Get-Command aws -ErrorAction SilentlyContinue)) {
$installer = Join-Path $RunnerTemp "AWSCLIV2.msi"
Invoke-WebRequest -Uri "https://awscli.amazonaws.com/AWSCLIV2.msi" -OutFile $installer
$install = Start-Process msiexec.exe -Wait -PassThru -ArgumentList "/i `"$installer`" /qn"
if ($install.ExitCode -ne 0) {
throw "AWS CLI installer failed with exit code $($install.ExitCode)."
}
$awsCliDir = Join-Path $env:ProgramFiles "Amazon/AWSCLIV2"
$machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
$userPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
$env:Path = "$awsCliDir;$machinePath;$userPath;$env:Path"
if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) {
Add-Content -LiteralPath $env:GITHUB_PATH -Value $awsCliDir
}
}
if (-not (Get-Command coscli -ErrorAction SilentlyContinue)) {
$coscliDir = Join-Path $RunnerTemp "coscli"
New-Item -ItemType Directory -Force -Path $coscliDir | Out-Null
Invoke-WebRequest -Uri "https://github.com/tencentyun/coscli/releases/download/v1.0.8/coscli-v1.0.8-windows-amd64.exe" -OutFile (Join-Path $coscliDir "coscli.exe")
Add-ActionPath $coscliDir
}
if (-not (Get-Command tccli -ErrorAction SilentlyContinue)) {
python -m pip install --user tccli
if ($LASTEXITCODE -ne 0) {
throw "tccli install failed with exit code $LASTEXITCODE."
}
$pythonUserScripts = Join-Path (python -m site --user-base) "Scripts"
Add-ActionPath $pythonUserScripts
}
@@ -1,35 +0,0 @@
param(
[string] $SourcePath = "Bootstrap/build/libs",
[string] $DestinationPath = "build/publish"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path
Set-Location -LiteralPath $RepositoryRoot
function Resolve-RepoPath {
param([Parameter(Mandatory = $true)][string] $Path)
if ([System.IO.Path]::IsPathRooted($Path)) {
return [System.IO.Path]::GetFullPath($Path)
}
return [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $Path))
}
$source = Resolve-RepoPath $SourcePath
$destination = Resolve-RepoPath $DestinationPath
New-Item -ItemType Directory -Force -Path $destination | Out-Null
$jars = Get-ChildItem -LiteralPath $source -Filter "CustomSkinLoader_Universal-*.jar" -File |
Where-Object { $_.Name -notlike "*-sources.jar" }
if (-not $jars) {
throw "No Universal jar found under $SourcePath."
}
$jars | Copy-Item -Destination $destination -Force
@@ -1,25 +0,0 @@
param(
[string] $Channel = $env:PUBLISH_CHANNEL,
[string] $LatestJsonName = $env:LATEST_JSON_NAME,
[string] $DetailJsonName = $env:DETAIL_JSON_NAME
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
if ([string]::IsNullOrWhiteSpace($Channel)) {
$Channel = "Beta"
}
$publishScript = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "publish-artifacts.ps1"
$arguments = @("-Channel", $Channel)
if (-not [string]::IsNullOrWhiteSpace($LatestJsonName)) {
$arguments += @("-LatestJsonName", $LatestJsonName)
}
if (-not [string]::IsNullOrWhiteSpace($DetailJsonName)) {
$arguments += @("-DetailJsonName", $DetailJsonName)
}
& $publishScript @arguments
-427
View File
@@ -1,427 +0,0 @@
param(
[ValidateSet("Beta", "Release")]
[string] $Channel = "Beta",
[string] $LatestJsonName,
[string] $DetailJsonName,
[string] $ArtifactsRoot = "build/publish",
[switch] $SkipObjectStorage
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "..\..")).Path
Set-Location -LiteralPath $RepositoryRoot
function Resolve-RepoPath {
param([Parameter(Mandatory = $true)][string] $Path)
if ([System.IO.Path]::IsPathRooted($Path)) {
return [System.IO.Path]::GetFullPath($Path)
}
return [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $Path))
}
function ConvertTo-ShortVersion {
param([Parameter(Mandatory = $true)][string] $Version)
return $Version.Replace("SNAPSHOT-", "s")
}
function Read-BuildProperties {
$properties = [ordered] @{}
foreach ($line in Get-Content -LiteralPath (Resolve-RepoPath "build.properties")) {
$trimmedLine = $line.Trim()
if ([string]::IsNullOrWhiteSpace($trimmedLine) -or $trimmedLine.StartsWith("#")) {
continue
}
$separatorIndex = $trimmedLine.IndexOf("=")
if ($separatorIndex -lt 1) {
continue
}
$name = $trimmedLine.Substring(0, $separatorIndex).Trim()
$value = $trimmedLine.Substring($separatorIndex + 1).Trim()
$properties[$name] = $value
}
return $properties
}
function Get-RequiredProperty {
param(
[Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties,
[Parameter(Mandatory = $true)][string] $Name
)
$value = $Properties[$Name]
if ([string]::IsNullOrWhiteSpace($value)) {
throw "Missing $Name in build.properties"
}
return $value
}
function Get-PropertyList {
param(
[Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties,
[Parameter(Mandatory = $true)][string] $Name
)
return (Get-RequiredProperty $Properties $Name) -split "," |
ForEach-Object { $_.Trim() } |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
}
function Get-PropertyListByNames {
param(
[Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties,
[Parameter(Mandatory = $true)][string[]] $Names
)
foreach ($name in $Names) {
if (-not [string]::IsNullOrWhiteSpace($Properties[$name])) {
return Get-PropertyList $Properties $name
}
}
throw "Missing one of these properties in build.properties: $($Names -join ', ')"
}
function Test-VersionAtLeast {
param(
[Parameter(Mandatory = $true)][string] $Version,
[Parameter(Mandatory = $true)][string] $MinimumVersion
)
return [version] $Version -ge [version] $MinimumVersion
}
function Get-VersionFromJarName {
param([Parameter(Mandatory = $true)][string] $FileName)
$prefix = "CustomSkinLoader_Universal-"
$suffix = ".jar"
if (-not $FileName.StartsWith($prefix) -or -not $FileName.EndsWith($suffix)) {
throw "Unexpected Universal jar name: $FileName"
}
return $FileName.Substring($prefix.Length, $FileName.Length - $prefix.Length - $suffix.Length)
}
function New-Directory {
param([Parameter(Mandatory = $true)][string] $Path)
[System.IO.Directory]::CreateDirectory((Resolve-RepoPath $Path)) | Out-Null
}
function Copy-PublishArtifacts {
param([Parameter(Mandatory = $true)][string] $Destination)
New-Directory $Destination
$destinationPath = Resolve-RepoPath $Destination
Get-ChildItem -LiteralPath $destinationPath -File -ErrorAction SilentlyContinue | Remove-Item -Force
$jar = Get-ChildItem -LiteralPath (Resolve-RepoPath "Bootstrap/build/libs") -Filter "CustomSkinLoader_Universal-*.jar" -File -ErrorAction Stop |
Where-Object { $_.Name -notlike "*-sources.jar" } |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $jar) {
throw "No Universal jar found under Bootstrap/build/libs."
}
Copy-Item -LiteralPath $jar.FullName -Destination (Join-Path $destinationPath $jar.Name) -Force
return Get-Item -LiteralPath (Join-Path $destinationPath $jar.Name)
}
function ConvertTo-JsonFile {
param(
[Parameter(Mandatory = $true)] $InputObject,
[Parameter(Mandatory = $true)][string] $Path
)
$json = $InputObject | ConvertTo-Json -Depth 10
$absolutePath = Resolve-RepoPath $Path
[System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($absolutePath)) | Out-Null
[System.IO.File]::WriteAllText($absolutePath, $json + [System.Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
}
function New-Metadata {
param(
[Parameter(Mandatory = $true)][string] $Version,
[Parameter(Mandatory = $true)][string] $OutputDirectory,
[Parameter(Mandatory = $true)][string] $LatestName,
[Parameter(Mandatory = $true)][string] $DetailName,
[Parameter(Mandatory = $true)][System.Collections.IDictionary] $BuildProperties
)
$publicBaseUrl = if ([string]::IsNullOrWhiteSpace($env:CLOUDFLARE_CDN_ROOT)) { "https://csl.3-3.dev/" } else { $env:CLOUDFLARE_CDN_ROOT.Trim() }
if (-not $publicBaseUrl.EndsWith("/")) {
$publicBaseUrl += "/"
}
$outputPath = Resolve-RepoPath $OutputDirectory
$jars = Get-ChildItem -LiteralPath $outputPath -Filter "CustomSkinLoader_Universal-*.jar" -File |
Sort-Object Name
if (-not $jars) {
throw "No publish jars found in $OutputDirectory."
}
$latest = [ordered] @{
version = ConvertTo-ShortVersion $Version
downloads = [ordered] @{}
launchermeta = [ordered] @{}
}
foreach ($jar in $jars) {
$latest.downloads["Universal"] = "$publicBaseUrl`mods/$($jar.Name)"
}
$minecraftMajorVersions = Get-PropertyList $BuildProperties "minecraft_major_versions"
$detailMap = [ordered] @{}
foreach ($minecraftVersion in $minecraftMajorVersions) {
$loaderMap = [ordered] @{}
$loaderMap["Forge"] = $latest.downloads["Universal"]
if (Test-VersionAtLeast $minecraftVersion "1.14") {
$loaderMap["Fabric"] = $latest.downloads["Universal"]
$loaderMap["Quilt"] = $latest.downloads["Universal"]
}
if (Test-VersionAtLeast $minecraftVersion "1.20") {
$loaderMap["NeoForge"] = $latest.downloads["Universal"]
}
$detailMap[$minecraftVersion] = $loaderMap
}
$detail = [ordered] @{
version = $latest.version
timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
details = $detailMap
}
ConvertTo-JsonFile $latest (Join-Path $outputPath $LatestName)
ConvertTo-JsonFile $detail (Join-Path $outputPath $DetailName)
}
function Write-GitHubOutputValue {
param(
[Parameter(Mandatory = $true)][string] $Name,
[Parameter(Mandatory = $true)][string] $Value
)
if ([string]::IsNullOrWhiteSpace($env:GITHUB_OUTPUT)) {
return
}
$delimiter = "EOF_$([guid]::NewGuid().ToString('N'))"
$content = "$Name<<$delimiter" + [System.Environment]::NewLine +
$Value + [System.Environment]::NewLine +
$delimiter + [System.Environment]::NewLine
[System.IO.File]::AppendAllText($env:GITHUB_OUTPUT, $content, [System.Text.UTF8Encoding]::new($false))
}
function Write-PublishOutputs {
param(
[Parameter(Mandatory = $true)][string] $Version,
[Parameter(Mandatory = $true)][System.Collections.IDictionary] $BuildProperties
)
$shortVersion = ConvertTo-ShortVersion $Version
$edition = Get-RequiredProperty $BuildProperties "edition"
Write-GitHubOutputValue "edition" $edition
Write-GitHubOutputValue "version" $shortVersion
Write-GitHubOutputValue "mc_publish_name" "CustomSkinLoader_$edition`_$shortVersion"
Write-GitHubOutputValue "mc_publish_version" "$shortVersion-$edition"
Write-GitHubOutputValue "loaders" ((Get-PropertyListByNames $BuildProperties @("loaders", "mod_loaders")) -join [System.Environment]::NewLine)
Write-GitHubOutputValue "game_versions" ((Get-PropertyListByNames $BuildProperties @("game-versions", "minecraft_major_versions")) -join [System.Environment]::NewLine)
Write-GitHubOutputValue "java_versions" ((Get-PropertyListByNames $BuildProperties @("java", "java_full_versions")) -join [System.Environment]::NewLine)
}
function Invoke-AwsCliUpload {
param(
[Parameter(Mandatory = $true)][string] $EndpointUrl,
[Parameter(Mandatory = $true)][string] $Bucket,
[Parameter(Mandatory = $true)][string] $AccessKeyId,
[Parameter(Mandatory = $true)][string] $SecretAccessKey,
[Parameter(Mandatory = $true)][string] $Directory
)
$aws = Get-Command aws -ErrorAction SilentlyContinue
if (-not $aws) {
Write-Warning "AWS CLI was not found. Skip S3-compatible upload."
return
}
$oldAccessKey = $env:AWS_ACCESS_KEY_ID
$oldSecretKey = $env:AWS_SECRET_ACCESS_KEY
$oldRegion = $env:AWS_DEFAULT_REGION
try {
$env:AWS_ACCESS_KEY_ID = $AccessKeyId
$env:AWS_SECRET_ACCESS_KEY = $SecretAccessKey
$env:AWS_DEFAULT_REGION = "auto"
foreach ($file in Get-ChildItem -LiteralPath $Directory -File) {
$key = if ($file.Extension -eq ".jar") { "mods/$($file.Name)" } else { $file.Name }
& aws --endpoint-url $EndpointUrl s3 cp $file.FullName "s3://$Bucket/$key" --only-show-errors
}
} finally {
$env:AWS_ACCESS_KEY_ID = $oldAccessKey
$env:AWS_SECRET_ACCESS_KEY = $oldSecretKey
$env:AWS_DEFAULT_REGION = $oldRegion
}
}
function Invoke-CosCliUpload {
param(
[Parameter(Mandatory = $true)][string] $Bucket,
[Parameter(Mandatory = $true)][string] $SecretId,
[Parameter(Mandatory = $true)][string] $SecretKey,
[Parameter(Mandatory = $true)][string] $Directory
)
$coscli = Get-Command coscli -ErrorAction SilentlyContinue
if (-not $coscli) {
Write-Warning "coscli was not found. Skip Tencent COS upload."
return
}
$configPath = Join-Path $env:RUNNER_TEMP "coscli-customskinloader.yaml"
$cosRegion = if ([string]::IsNullOrWhiteSpace($env:COS_REGION)) { "ap-shanghai" } else { $env:COS_REGION.Trim() }
@"
cos:
base:
secretid: $SecretId
secretkey: $SecretKey
sessiontoken: ""
protocol: https
buckets:
- name: $Bucket
alias: $Bucket
region: $cosRegion
endpoint: cos.$cosRegion.myqcloud.com
ofs: false
"@ | Set-Content -LiteralPath $configPath -Encoding UTF8
foreach ($file in Get-ChildItem -LiteralPath $Directory -File) {
$key = if ($file.Extension -eq ".jar") { "mods/$($file.Name)" } else { $file.Name }
& coscli -c $configPath cp $file.FullName "cos://$Bucket/$key"
}
}
function Get-PublishObjectKeys {
param([Parameter(Mandatory = $true)][string] $Directory)
return Get-ChildItem -LiteralPath $Directory -File |
ForEach-Object {
if ($_.Extension -eq ".jar") {
"mods/$($_.Name)"
} else {
$_.Name
}
}
}
function Invoke-TencentCdnRefresh {
param([Parameter(Mandatory = $true)][string[]] $Keys)
$cdnRoot = if ([string]::IsNullOrWhiteSpace($env:TENCENT_CDN_ROOT)) { "https://csl.littleservice.cn/" } else { $env:TENCENT_CDN_ROOT.Trim() }
if (-not $cdnRoot.EndsWith("/")) {
$cdnRoot += "/"
}
if ([string]::IsNullOrWhiteSpace($env:COS_SECRET_ID) -or [string]::IsNullOrWhiteSpace($env:COS_SECRET_KEY)) {
Write-Host "Tencent CDN credentials are incomplete. Skip CDN refresh."
return
}
$tccli = Get-Command tccli -ErrorAction SilentlyContinue
if (-not $tccli) {
Write-Warning "tccli was not found. Skip Tencent CDN refresh."
return
}
$oldSecretId = $env:TENCENTCLOUD_SECRET_ID
$oldSecretKey = $env:TENCENTCLOUD_SECRET_KEY
try {
$env:TENCENTCLOUD_SECRET_ID = $env:COS_SECRET_ID
$env:TENCENTCLOUD_SECRET_KEY = $env:COS_SECRET_KEY
$urlsJson = ConvertTo-Json -Compress -InputObject @($Keys | ForEach-Object { "$cdnRoot$_" })
& tccli cdn PurgeUrlsCache --Urls $urlsJson
if ($LASTEXITCODE -ne 0) {
throw "Tencent CDN purge failed."
}
& tccli cdn PushUrlsCache --Urls $urlsJson
if ($LASTEXITCODE -ne 0) {
throw "Tencent CDN push failed."
}
} finally {
$env:TENCENTCLOUD_SECRET_ID = $oldSecretId
$env:TENCENTCLOUD_SECRET_KEY = $oldSecretKey
}
}
if ([string]::IsNullOrWhiteSpace($LatestJsonName)) {
$LatestJsonName = switch ($Channel) {
"Beta" { "latest-beta.json" }
"Canary" { "latest-canary.json" }
"Release" { "latest.json" }
}
}
if ([string]::IsNullOrWhiteSpace($DetailJsonName)) {
$DetailJsonName = switch ($Channel) {
"Beta" { "detail-beta.json" }
"Canary" { "detail-canary.json" }
"Release" { "detail.json" }
}
}
New-Directory $ArtifactsRoot
$buildProperties = Read-BuildProperties
$artifacts = Copy-PublishArtifacts $ArtifactsRoot
$ArtifactsRoot = Resolve-RepoPath $ArtifactsRoot
$fullVersion = Get-VersionFromJarName $artifacts[0].Name
New-Metadata -Version $fullVersion -OutputDirectory $ArtifactsRoot -LatestName $LatestJsonName -DetailName $DetailJsonName -BuildProperties $buildProperties
Write-PublishOutputs -Version $fullVersion -BuildProperties $buildProperties
Write-Host "Prepared publish artifacts:"
Get-ChildItem -LiteralPath $ArtifactsRoot -File | ForEach-Object { Write-Host " - $($_.Name)" }
if ($SkipObjectStorage) {
Write-Host "SkipObjectStorage was set. Metadata generated locally only."
exit 0
}
if (-not [string]::IsNullOrWhiteSpace($env:R2_BASE_URL) -and
-not [string]::IsNullOrWhiteSpace($env:R2_BUCKET) -and
-not [string]::IsNullOrWhiteSpace($env:R2_SECRET_ID) -and
-not [string]::IsNullOrWhiteSpace($env:R2_SECRET_KEY)) {
Invoke-AwsCliUpload -EndpointUrl $env:R2_BASE_URL -Bucket $env:R2_BUCKET -AccessKeyId $env:R2_SECRET_ID -SecretAccessKey $env:R2_SECRET_KEY -Directory $ArtifactsRoot
} else {
Write-Host "R2 secrets are incomplete. Skip R2 upload."
}
if (-not [string]::IsNullOrWhiteSpace($env:COS_BUCKET) -and
-not [string]::IsNullOrWhiteSpace($env:COS_SECRET_ID) -and
-not [string]::IsNullOrWhiteSpace($env:COS_SECRET_KEY)) {
Invoke-CosCliUpload -Bucket $env:COS_BUCKET -SecretId $env:COS_SECRET_ID -SecretKey $env:COS_SECRET_KEY -Directory $ArtifactsRoot
} else {
Write-Host "COS secrets are incomplete. Skip COS upload."
}
Invoke-TencentCdnRefresh -Keys @(Get-PublishObjectKeys $ArtifactsRoot)
-37
View File
@@ -1,37 +0,0 @@
param(
[string] $BranchName = $env:GITHUB_REF_NAME,
[string] $BuildType = $env:BUILD_TYPE,
[string] $OutputPath = "build/publish/release-notes.md",
[string] $TimeZoneId = "China Standard Time"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path
Set-Location -LiteralPath $RepositoryRoot
if ([string]::IsNullOrWhiteSpace($BranchName)) {
throw "BranchName is required."
}
if ([string]::IsNullOrWhiteSpace($BuildType)) {
throw "BuildType is required."
}
$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) {
[System.IO.Path]::GetFullPath($OutputPath)
} else {
[System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $OutputPath))
}
[System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($absoluteOutputPath)) | Out-Null
$buildTime = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TimeZoneId).ToString("yyyy.MM.dd HH:mm")
@"
Build Time / 构建时间: $buildTime
Synchronize $BranchName branch code updates, keeping only the latest $BuildType build.
自动同步 $BranchName 分支构建产物,仅保留最新 $BuildType 版本。
"@ | Set-Content -LiteralPath $absoluteOutputPath -Encoding UTF8
+64 -16
View File
@@ -1,21 +1,69 @@
name: Beta
on:
push:
branches:
- 15-develop
workflow_dispatch:
branches: [15-develop]
permissions:
contents: write
jobs:
build-beta:
name: Build Beta
uses: ./.github/workflows/common.yml
with:
type: Beta
publish: true
prerelease-tag: Beta-Build
prerelease-name: CustomSkinLoader Beta Build
latest-json: latest-beta.json
detail-json: detail-beta.json
is-snapshot: true
secrets: inherit
build:
name: Build
runs-on: windows-latest
steps:
- uses: ./.github/actions/build
with:
artifact-name: CustomSkinLoader-Beta-${{ github.run_number }}
publish:
name: Publish
needs: build
runs-on: windows-latest
environment: Build
env:
IS_SNAPSHOT: 'true'
R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }}
R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_BASE_URL: ${{ secrets.R2_BASE_URL }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
steps:
- uses: actions/download-artifact@v4
with:
name: CustomSkinLoader-Beta-${{ github.run_number }}
path: artifact
- uses: ./.github/actions/publish
with:
artifact-dir: artifact
type: beta
- name: Generate CI-Build body
shell: pwsh
run: |
@"
Build Time / 构建时间 : $((Get-Date).ToUniversalTime().AddHours(8).ToString("yyyy.MM.dd HH:mm"))
Auto build from branch $env:GITHUB_REF_NAME
[How to use?](https://github.com/xfl03/MCCustomSkinLoader/wiki/How-to-Use)
"@ | Set-Content -Path ci-build-body.md -NoNewline
- name: Move CI-Build tag to current commit
uses: richardsimko/update-tag@v1
with:
tag_name: CI-Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: GitHub PreRelease
uses: softprops/action-gh-release@v2
with:
tag_name: CI-Build
name: CustomSkinLoader CI-Build
prerelease: true
body_path: ci-build-body.md
files: artifact/*.jar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-173
View File
@@ -1,173 +0,0 @@
name: Common
on:
workflow_call:
inputs:
type:
required: true
type: string
publish:
required: false
type: boolean
default: false
release:
required: false
type: boolean
default: false
prerelease-tag:
required: false
type: string
default: ""
prerelease-name:
required: false
type: string
default: ""
latest-json:
required: false
type: string
default: latest-beta.json
detail-json:
required: false
type: string
default: detail-beta.json
is-snapshot:
required: false
type: boolean
default: true
permissions:
contents: write
jobs:
build:
name: Build
runs-on: windows-latest
defaults:
run:
shell: powershell
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: liberica
java-version: "25"
cache: gradle
- name: Build
env:
IS_SNAPSHOT: ${{ inputs['is-snapshot'] }}
run: ./.github/scripts/build.ps1
- name: Install publish CLIs
if: ${{ inputs.publish || inputs.release }}
run: ./.github/scripts/install-publish-clis.ps1
- name: Prepare publish artifacts
id: publish-artifacts
if: ${{ inputs.publish || inputs.release }}
env:
IS_SNAPSHOT: ${{ inputs['is-snapshot'] }}
PUBLISH_CHANNEL: ${{ inputs.type }}
LATEST_JSON_NAME: ${{ inputs['latest-json'] }}
DETAIL_JSON_NAME: ${{ inputs['detail-json'] }}
R2_BASE_URL: ${{ secrets.R2_BASE_URL }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }}
R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
COS_REGION: ${{ secrets.COS_REGION }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
TENCENT_CDN_ROOT: ${{ secrets.TENCENT_CDN_ROOT }}
CLOUDFLARE_CDN_ROOT: ${{ secrets.CLOUDFLARE_CDN_ROOT }}
run: ./.github/scripts/prepare-publish-artifacts.ps1
- name: Prepare build artifacts
if: ${{ !(inputs.publish || inputs.release) }}
run: ./.github/scripts/prepare-build-artifacts.ps1
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: CustomSkinLoader-${{ inputs.type }}-${{ github.run_number }}
path: build/publish/*
- name: Write pre-release body
if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }}
env:
BUILD_TYPE: ${{ inputs.type }}
run: ./.github/scripts/write-prerelease-body.ps1
- name: Delete previous pre-release assets
if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }}
uses: 8Mi-Tech/delete-release-assets-action@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ inputs['prerelease-tag'] }}
deleteOnlyFromDrafts: false
- name: Move pre-release tag
if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }}
uses: richardsimko/update-tag@v1
with:
tag_name: ${{ inputs['prerelease-tag'] }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish GitHub pre-release
if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }}
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ inputs['prerelease-name'] }}
tag_name: ${{ inputs['prerelease-tag'] }}
prerelease: true
make_latest: false
body_path: build/publish/release-notes.md
overwrite_files: true
files: |
build/publish/*.jar
build/publish/*.json
- name: Publish GitHub release assets
if: ${{ inputs.release }}
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
overwrite_files: true
files: |
build/publish/*.jar
build/publish/*.json
- name: Publish to CurseForge and Modrinth
if: ${{ inputs.release }}
uses: Kir-Antipov/[email protected]
with:
modrinth-id: idMHQ4n2
modrinth-featured: true
modrinth-unfeature-mode: subset
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
curseforge-id: 286924
curseforge-token: ${{ secrets.CURSEFORGE_API_KEY }}
name: ${{ steps.publish-artifacts.outputs.mc_publish_name }}
version: ${{ steps.publish-artifacts.outputs.mc_publish_version }}
version-type: release
loaders: |
${{ steps.publish-artifacts.outputs.loaders }}
game-versions: |
${{ steps.publish-artifacts.outputs.game_versions }}
game-version-filter: releases
java: |
${{ steps.publish-artifacts.outputs.java_versions }}
retry-attempts: 2
retry-delay: 10000
fail-mode: fail
files: |
build/publish/*.jar
+9 -13
View File
@@ -1,18 +1,14 @@
name: Pull Request
on:
pull_request:
types:
- opened
- synchronize
- reopened
types: [opened, synchronize, reopened]
branches: [15-develop]
jobs:
build-pull-request:
name: Build Pull Request
uses: ./.github/workflows/common.yml
with:
type: PullRequest
publish: false
release: false
is-snapshot: true
build:
name: Build
runs-on: windows-latest
steps:
- uses: ./.github/actions/build
with:
artifact-name: CustomSkinLoader-PR-${{ github.run_number }}
+72 -11
View File
@@ -1,17 +1,78 @@
name: Release
on:
release:
types: [released]
permissions:
contents: write
jobs:
build-release:
name: Build Release
uses: ./.github/workflows/common.yml
with:
type: Release
release: true
latest-json: latest.json
detail-json: detail.json
is-snapshot: false
secrets: inherit
build:
name: Build
runs-on: windows-latest
steps:
- uses: ./.github/actions/build
with:
is-snapshot: 'false'
artifact-name: CustomSkinLoader-Release-${{ github.run_number }}
publish:
name: Publish
needs: build
runs-on: windows-latest
environment: Build
env:
IS_SNAPSHOT: 'false'
R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }}
R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
R2_BASE_URL: ${{ secrets.R2_BASE_URL }}
COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }}
COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }}
COS_BUCKET: ${{ secrets.COS_BUCKET }}
steps:
- uses: actions/download-artifact@v4
with:
name: CustomSkinLoader-Release-${{ github.run_number }}
path: artifact
- uses: ./.github/actions/publish
with:
artifact-dir: artifact
type: release
- name: Get publish info from gradle.properties
id: info
shell: pwsh
run: .github/scripts/Get-PublishInfo.ps1
- uses: Kir-Antipov/[email protected]
with:
modrinth-id: idMHQ4n2
modrinth-featured: true
modrinth-unfeature-mode: subset
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
curseforge-id: 286924
curseforge-token: ${{ secrets.CURSEFORGE_API_KEY }}
name: CustomSkinLoader_Universal_${{ steps.info.outputs.short-version }}
version: ${{ steps.info.outputs.short-version }}-Universal
version-type: release
loaders: |
${{ steps.info.outputs.loaders }}
game-versions: |
${{ steps.info.outputs.game-versions }}
game-version-filter: releases
java: |
${{ steps.info.outputs.java }}
retry-attempts: 2
retry-delay: 10000
fail-mode: fail
files: |
artifact/*.jar
- name: GitHub Release
uses: softprops/action-gh-release@v2
with:
files: artifact/*.jar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,4 +1,4 @@
manifestLibraries {
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.fabricmc.fabric-loader/0.19.2.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.fabricmc.fabric-loader/${fabric_v1_version}.json"
}
+1 -1
View File
@@ -1,4 +1,4 @@
manifestLibraries {
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/14.23.5.2864.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/${forge_v1_version}.json"
}
+1 -1
View File
@@ -1,4 +1,4 @@
manifestLibraries {
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/36.2.42.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/${forge_v2_version}.json"
}
+1 -1
View File
@@ -1,4 +1,4 @@
manifestLibraries {
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/20.5.21-beta.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/${neoforge_v1_version}.json"
}
+1 -1
View File
@@ -1,4 +1,4 @@
manifestLibraries {
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/26.1.2.59-beta.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/${neoforge_v2_version}.json"
}
+1 -1
View File
@@ -90,7 +90,7 @@ Keep pull requests focused. If a cleanup is unrelated to the bug or feature, sen
Pull requests run the shared GitHub Actions build on `windows-latest` with PowerShell and Java 25. This is a CI choice; local builds should remain portable across Windows, Linux, and macOS.
Publishing jobs generate the Universal jar plus metadata files from `build.properties`. Beta builds publish moving beta metadata, release builds publish release metadata and upload to distribution platforms. Object-storage upload is optional and is skipped when the required secrets are not configured.
Publishing jobs generate the Universal jar plus metadata files from `gradle.properties`. Beta builds publish moving beta metadata, release builds publish release metadata and upload to distribution platforms. Object-storage upload is optional and is skipped when the required secrets are not configured.
To generate publish metadata locally without uploading, first build the project, then run:
+6 -15
View File
@@ -1,19 +1,10 @@
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.attributes.java.TargetJvmVersion
def buildPropertiesFile = rootProject.file('build.properties')
if (!buildPropertiesFile) {
throw new GradleException('Missing build.properties')
}
def buildProperties = new Properties()
buildPropertiesFile.withInputStream { buildProperties.load(it) }
def requireBuildProperty = { String propertyName ->
def propertyValue = buildProperties.getProperty(propertyName)?.trim()
def propertyValue = rootProject.findProperty(propertyName)?.toString()?.trim()
if (!propertyValue) {
throw new GradleException("Missing ${propertyName} in ${buildPropertiesFile}")
throw new GradleException("Missing ${propertyName} in gradle.properties")
}
propertyValue
}
@@ -26,9 +17,9 @@ def isTruthy = { String value ->
value.trim().toLowerCase(Locale.ROOT) == 'true'
}
def releaseName = requireBuildProperty('name')
def releaseGroup = requireBuildProperty('group')
def releaseVersion = requireBuildProperty('version')
def releaseName = requireBuildProperty('mod_name')
def releaseGroup = requireBuildProperty('mod_group')
def releaseVersion = requireBuildProperty('mod_version')
def releaseBuildNumber = System.getenv('GITHUB_RUN_NUMBER')?.trim() ?: '0'
def releaseIsSnapshot = isTruthy(System.getenv('IS_SNAPSHOT'))
def releaseFullVersion = (releaseIsSnapshot ? "${releaseVersion}-SNAPSHOT-${releaseBuildNumber}" : releaseVersion).toString()
@@ -71,7 +62,7 @@ subprojects {
manifestLibraries {
// Share Bootstrap's dependency set with all packaged child modules.
add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraft/26.1.2.json'
add "https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraft/${minecraft_version}.json"
}
repositories {
+11 -4
View File
@@ -1,9 +1,16 @@
name=CustomSkinLoader
group=customskinloader
version=15.0
mod_name=CustomSkinLoader
mod_group=customskinloader
mod_version=15.0
edition=Universal
minecraft_major_versions=1.8,1.9,1.10,1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19,1.20,1.21,26.1
minecraft_version=26.1.2
fabric_v1_version=0.19.2
forge_v1_version=14.23.5.2864
forge_v2_version=36.2.42
neoforge_v1_version=20.6.13-beta
neoforge_v2_version=26.1.2.59-beta
loaders=fabric,forge,neoforge,quilt
game-versions=1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1,26.1.1,26.1.2
game_versions=1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1,26.1.1,26.1.2
java=8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,25
+1 -1
View File
@@ -1,4 +1,4 @@
rootProject.name = 'CustomSkinLoader-Universal'
rootProject.name = 'MCCustomSkinLoader'
include(
'Bootstrap',