mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2026-10-16 21:21:33 +08:00
构建时创建 deb 包 (#5981)
This commit is contained in:
@@ -48,3 +48,9 @@ jobs:
|
||||
name: HMCL-${{ env.SHORT_SHA }}-sh
|
||||
path: HMCL/build/libs/HMCL-*.sh
|
||||
archive: false
|
||||
- name: Upload DEB
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: HMCL-${{ env.SHORT_SHA }}-deb
|
||||
path: HMCL/build/libs/HMCL-*.deb
|
||||
archive: false
|
||||
|
||||
@@ -46,16 +46,20 @@ jobs:
|
||||
wget "$HMCL_CI_DOWNLOAD_BASE_URI/HMCL-$HMCL_VERSION.jar.sha256"
|
||||
wget "$HMCL_CI_DOWNLOAD_BASE_URI/HMCL-$HMCL_VERSION.sh"
|
||||
wget "$HMCL_CI_DOWNLOAD_BASE_URI/HMCL-$HMCL_VERSION.sh.sha256"
|
||||
wget "$HMCL_CI_DOWNLOAD_BASE_URI/HMCL-$HMCL_VERSION.deb"
|
||||
wget "$HMCL_CI_DOWNLOAD_BASE_URI/HMCL-$HMCL_VERSION.deb.sha256"
|
||||
- name: Verify artifacts
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
export JAR_SHA256=$(cat HMCL-$HMCL_VERSION.jar.sha256 | tr -d '\n')
|
||||
export EXE_SHA256=$(cat HMCL-$HMCL_VERSION.exe.sha256 | tr -d '\n')
|
||||
export SH_SHA256=$(cat HMCL-$HMCL_VERSION.sh.sha256 | tr -d '\n')
|
||||
export DEB_SHA256=$(cat HMCL-$HMCL_VERSION.deb.sha256 | tr -d '\n')
|
||||
|
||||
echo "$JAR_SHA256 HMCL-$HMCL_VERSION.jar" | sha256sum -c
|
||||
echo "$EXE_SHA256 HMCL-$HMCL_VERSION.exe" | sha256sum -c
|
||||
echo "$SH_SHA256 HMCL-$HMCL_VERSION.sh" | sha256sum -c
|
||||
echo "$DEB_SHA256 HMCL-$HMCL_VERSION.deb" | sha256sum -c
|
||||
- name: Generate release note
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
@@ -67,6 +71,7 @@ jobs:
|
||||
echo "| [HMCL-$HMCL_VERSION.exe]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.exe) | \`$(cat HMCL-$HMCL_VERSION.exe.sha256)\` |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.jar]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.jar) | \`$(cat HMCL-$HMCL_VERSION.jar.sha256)\` |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.sh]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.sh) | \`$(cat HMCL-$HMCL_VERSION.sh.sha256)\` |" >> RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.deb]($GH_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.deb) | \`$(cat HMCL-$HMCL_VERSION.deb.sha256)\` |" >> RELEASE_NOTE
|
||||
|
||||
# CNB Release Note
|
||||
echo "[更新日志](https://docs.hmcl.net/changelog/${{ matrix.channel }}.html#HMCL-$HMCL_VERSION)" >> CNB_RELEASE_NOTE
|
||||
@@ -76,6 +81,8 @@ jobs:
|
||||
echo "| [HMCL-$HMCL_VERSION.exe]($CNB_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.exe) | \`$(cat HMCL-$HMCL_VERSION.exe.sha256)\` |" >> CNB_RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.jar]($CNB_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.jar) | \`$(cat HMCL-$HMCL_VERSION.jar.sha256)\` |" >> CNB_RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.sh]($CNB_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.sh) | \`$(cat HMCL-$HMCL_VERSION.sh.sha256)\` |" >> CNB_RELEASE_NOTE
|
||||
echo "| [HMCL-$HMCL_VERSION.deb]($CNB_DOWNLOAD_BASE_URL/v$HMCL_VERSION/HMCL-$HMCL_VERSION.deb) | \`$(cat HMCL-$HMCL_VERSION.deb.sha256)\` |" >> CNB_RELEASE_NOTE
|
||||
|
||||
env:
|
||||
GH_DOWNLOAD_BASE_URL: https://github.com/${{ github.repository }}/releases/download
|
||||
CNB_DOWNLOAD_BASE_URL: https://cnb.cool/HMCL-dev/HMCL/-/releases/download
|
||||
@@ -86,6 +93,7 @@ jobs:
|
||||
"HMCL-${{ env.HMCL_VERSION }}.exe" \
|
||||
"HMCL-${{ env.HMCL_VERSION }}.jar" \
|
||||
"HMCL-${{ env.HMCL_VERSION }}.sh" \
|
||||
"HMCL-${{ env.HMCL_VERSION }}.deb" \
|
||||
--target "${{ env.HMCL_COMMIT_SHA }}" \
|
||||
--title "${{ env.HMCL_TAG_NAME }}" \
|
||||
--notes-file RELEASE_NOTE \
|
||||
@@ -120,6 +128,9 @@ jobs:
|
||||
|
||||
echo "Uploading HMCL-$HMCL_VERSION.sh"
|
||||
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "$HMCL_TAG_NAME" --file-name "HMCL-$HMCL_VERSION.sh"
|
||||
|
||||
echo "Uploading HMCL-$HMCL_VERSION.deb"
|
||||
~/go/bin/git-cnb release asset-upload --repo="$CNB_REPO" --tag-name "$HMCL_TAG_NAME" --file-name "HMCL-$HMCL_VERSION.deb"
|
||||
env:
|
||||
CNB_TOKEN: ${{ secrets.CNB_SYNC_TOKEN }}
|
||||
CNB_REPO: HMCL-dev/HMCL
|
||||
|
||||
+37
-8
@@ -6,6 +6,8 @@ import org.jackhuang.hmcl.gradle.l10n.CreateLanguageList
|
||||
import org.jackhuang.hmcl.gradle.l10n.CreateLocaleNamesResourceBundle
|
||||
import org.jackhuang.hmcl.gradle.l10n.UpsideDownTranslate
|
||||
import org.jackhuang.hmcl.gradle.mod.ParseModDataTask
|
||||
import org.jackhuang.hmcl.gradle.pack.CreateDeb
|
||||
import org.jackhuang.hmcl.gradle.pack.ReleaseType
|
||||
import org.jackhuang.hmcl.gradle.utils.PropertiesUtils
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystems
|
||||
@@ -251,20 +253,22 @@ tasks.processResources {
|
||||
}
|
||||
}
|
||||
|
||||
fun artifactFile(ext: String) = jarPath.resolveSibling(jarPath.nameWithoutExtension + '.' + ext)
|
||||
|
||||
val makeExecutables by tasks.registering {
|
||||
val extensions = listOf("exe", "sh")
|
||||
|
||||
dependsOn(tasks.jar)
|
||||
|
||||
inputs.file(jarPath)
|
||||
outputs.files(extensions.map { File(jarPath.parentFile, jarPath.nameWithoutExtension + '.' + it) })
|
||||
outputs.files(extensions.map { artifactFile(it) })
|
||||
|
||||
doLast {
|
||||
val jarContent = jarPath.readBytes()
|
||||
|
||||
ZipFile(jarPath).use { zipFile ->
|
||||
for (extension in extensions) {
|
||||
val output = File(jarPath.parentFile, jarPath.nameWithoutExtension + '.' + extension)
|
||||
val output = artifactFile(extension)
|
||||
val entry = zipFile.getEntry("assets/HMCLauncher.$extension")
|
||||
?: throw GradleException("HMCLauncher.$extension not found")
|
||||
|
||||
@@ -279,8 +283,31 @@ val makeExecutables by tasks.registering {
|
||||
}
|
||||
}
|
||||
|
||||
val makeDeb by tasks.registering(CreateDeb::class) {
|
||||
dependsOn(makeExecutables)
|
||||
|
||||
val debFile = layout.file(provider { artifactFile("deb") })
|
||||
|
||||
val debChannel = when (versionType) {
|
||||
"stable" -> ReleaseType.STABLE
|
||||
"dev" -> ReleaseType.DEVELOPMENT
|
||||
else -> ReleaseType.NIGHTLY
|
||||
}
|
||||
|
||||
version.set(project.version.toString())
|
||||
releaseType.set(debChannel)
|
||||
appShFile.set(layout.file(provider { artifactFile("sh") }))
|
||||
iconFile.set(layout.projectDirectory.file("image/hmcl.png"))
|
||||
outputFile.set(debFile)
|
||||
|
||||
doLast {
|
||||
createChecksum(debFile.get().asFile)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
dependsOn(makeExecutables)
|
||||
dependsOn(makeDeb)
|
||||
}
|
||||
|
||||
fun parseToolOptions(options: String?): MutableList<String> {
|
||||
@@ -377,12 +404,14 @@ val upgradeTerracottaConfig = tasks.register<TerracottaConfigUpgradeTask>("upgra
|
||||
val destination = layout.projectDirectory.file("src/main/resources/assets/terracotta.json")
|
||||
val source = layout.projectDirectory.file("terracotta-template.json");
|
||||
|
||||
classifiers.set(listOf(
|
||||
"windows-x86_64", "windows-arm64",
|
||||
"macos-x86_64", "macos-arm64",
|
||||
"linux-x86_64", "linux-arm64", "linux-loongarch64", "linux-riscv64",
|
||||
"freebsd-x86_64"
|
||||
))
|
||||
classifiers.set(
|
||||
listOf(
|
||||
"windows-x86_64", "windows-arm64",
|
||||
"macos-x86_64", "macos-arm64",
|
||||
"linux-x86_64", "linux-arm64", "linux-loongarch64", "linux-riscv64",
|
||||
"freebsd-x86_64"
|
||||
)
|
||||
)
|
||||
|
||||
version.set(libs.versions.terracotta)
|
||||
downloadURL.set($$"https://github.com/burningtnt/Terracotta/releases/download/v${version}/terracotta-${version}-${classifier}-pkg.tar.gz")
|
||||
|
||||
@@ -11,6 +11,7 @@ dependencies {
|
||||
implementation(libs.gson)
|
||||
implementation(libs.jna)
|
||||
implementation(libs.kala.compress.tar)
|
||||
implementation(libs.kala.compress.ar)
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.gradle.pack;
|
||||
|
||||
import kala.compress.archivers.ar.ArArchiveEntry;
|
||||
import kala.compress.archivers.ar.ArArchiveOutputStream;
|
||||
import kala.compress.archivers.tar.TarArchiveEntry;
|
||||
import kala.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/// Creates a Debian package for the current HMCL channel.
|
||||
///
|
||||
/// ## Package layout
|
||||
///
|
||||
/// The generated `data.tar.gz` contains four installed artifacts:
|
||||
///
|
||||
/// - the bundled HMCL shell launcher under `/usr/share/java/hmcl/`
|
||||
/// - a channel-specific command under `/usr/bin/`
|
||||
/// - a desktop entry under `/usr/share/applications/`
|
||||
/// - the HMCL icon under `/usr/share/icons/hicolor/256x256/apps/`
|
||||
///
|
||||
/// ## Channel commands and aliases
|
||||
///
|
||||
/// Every package installs a channel-specific executable such as `hmcl-stable`
|
||||
/// or `hmcl-beta`. The generic `hmcl` command is intentionally not shipped as a
|
||||
/// plain file. Instead, maintainer scripts register the channel command into
|
||||
/// the shared `hmcl` alternatives group so multiple channel packages can
|
||||
/// coexist without file conflicts.
|
||||
///
|
||||
/// @author Glavo
|
||||
public abstract class CreateDeb extends DefaultTask {
|
||||
public static final Logger LOGGER = Logging.getLogger(CreateDeb.class);
|
||||
|
||||
private static final int DIRECTORY_MODE = 0755;
|
||||
private static final int EXECUTABLE_MODE = 0755;
|
||||
private static final int REGULAR_FILE_MODE = 0644;
|
||||
|
||||
/// Debian version written into the `control` file and output filename.
|
||||
@Input
|
||||
public abstract Property<String> getVersion();
|
||||
|
||||
/// Release type metadata that controls package name, launcher name, and alias priority.
|
||||
@Input
|
||||
public abstract Property<ReleaseType> getReleaseType();
|
||||
|
||||
/// Executable `.sh` artifact produced by `makeExecutables`.
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getAppShFile();
|
||||
|
||||
/// Desktop icon installed into the hicolor icon theme.
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getIconFile();
|
||||
|
||||
/// Final `.deb` archive written by this task.
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
private ReleaseType getCurrentType() {
|
||||
return getReleaseType().get();
|
||||
}
|
||||
|
||||
private String getCurrentTypeName() {
|
||||
return getCurrentType().getName();
|
||||
}
|
||||
|
||||
private String getLauncherPath() {
|
||||
return "/usr/bin/hmcl-" + getCurrentTypeName();
|
||||
}
|
||||
|
||||
private String getTargetPath() {
|
||||
return "/usr/share/java/hmcl/" + getAppShFile().getAsFile().get().getName();
|
||||
}
|
||||
|
||||
private String getDesktopFilePath() {
|
||||
return "/usr/share/applications/hmcl-%s.desktop".formatted(getCurrentTypeName());
|
||||
}
|
||||
|
||||
private String getIconTargetPath() {
|
||||
return "/usr/share/icons/hicolor/256x256/apps/hmcl-%s.png".formatted(getCurrentTypeName());
|
||||
}
|
||||
|
||||
/// Ensures parent directories exist in the tar stream before child entries are written.
|
||||
private static void makeDirectories(Set<String> directories, TarArchiveOutputStream output, String dirName) throws IOException {
|
||||
if (dirName.isEmpty() || ".".equals(dirName) || directories.contains(dirName))
|
||||
return;
|
||||
|
||||
int idx = dirName.lastIndexOf('/');
|
||||
if (idx > 0) {
|
||||
makeDirectories(directories, output, dirName.substring(0, idx));
|
||||
}
|
||||
|
||||
TarArchiveEntry entry = new TarArchiveEntry(dirName + "/", true);
|
||||
entry.setMode(DIRECTORY_MODE);
|
||||
output.putArchiveEntry(entry);
|
||||
output.closeArchiveEntry();
|
||||
directories.add(dirName);
|
||||
}
|
||||
|
||||
/// Writes UTF-8 text content as a tar entry with the requested file mode.
|
||||
private static void putEntry(Set<String> directories, TarArchiveOutputStream output, String name, String content, int mode) throws IOException {
|
||||
putEntry(directories, output, name, content.getBytes(StandardCharsets.UTF_8), mode);
|
||||
}
|
||||
|
||||
/// Writes binary content as a tar entry and creates parent directories on demand.
|
||||
private static void putEntry(Set<String> directories, TarArchiveOutputStream output, String name, byte[] content, int mode) throws IOException {
|
||||
int idx = name.lastIndexOf('/');
|
||||
if (idx > 0) {
|
||||
makeDirectories(directories, output, name.substring(0, idx));
|
||||
}
|
||||
|
||||
TarArchiveEntry entry = new TarArchiveEntry(name);
|
||||
entry.setMode(mode);
|
||||
entry.setSize(content.length);
|
||||
output.putArchiveEntry(entry);
|
||||
output.write(content);
|
||||
output.closeArchiveEntry();
|
||||
}
|
||||
|
||||
/// Builds a valid `.deb` archive with `debian-binary`, `control.tar.gz`, and `data.tar.gz`.
|
||||
@TaskAction
|
||||
public void run() throws IOException {
|
||||
Path appShFile = getAppShFile().getAsFile().get().toPath();
|
||||
if (!Files.isRegularFile(appShFile))
|
||||
throw new IOException("Invalid app script file: " + appShFile);
|
||||
|
||||
Path iconFile = getIconFile().getAsFile().get().toPath();
|
||||
if (!Files.isRegularFile(iconFile))
|
||||
throw new IOException("Invalid icon file: " + iconFile);
|
||||
|
||||
byte[] appShBytes = Files.readAllBytes(appShFile);
|
||||
if (appShBytes.length == 0)
|
||||
throw new IOException("Empty app script file: " + appShFile);
|
||||
|
||||
byte[] iconBytes = Files.readAllBytes(iconFile);
|
||||
if (iconBytes.length == 0)
|
||||
throw new IOException("Empty icon file: " + iconFile);
|
||||
|
||||
byte[] launcherScriptBytes = getLauncherScript().getBytes(StandardCharsets.UTF_8);
|
||||
byte[] desktopInfoBytes = getDesktopInfo().getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
LOGGER.lifecycle("Creating control.tar.gz");
|
||||
var controlData = new ByteArrayOutputStream();
|
||||
try (var output = new TarArchiveOutputStream(new GZIPOutputStream(controlData))) {
|
||||
output.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
|
||||
|
||||
Set<String> directories = new HashSet<>();
|
||||
putEntry(directories, output, "./control", getControl(appShBytes.length, launcherScriptBytes.length, desktopInfoBytes.length, iconBytes.length), REGULAR_FILE_MODE);
|
||||
putEntry(directories, output, "./postinst", getPostinst(), EXECUTABLE_MODE);
|
||||
putEntry(directories, output, "./prerm", getPrerm(), EXECUTABLE_MODE);
|
||||
}
|
||||
|
||||
Path outputFile = getOutputFile().get().getAsFile().toPath();
|
||||
Files.createDirectories(outputFile.getParent());
|
||||
|
||||
LOGGER.lifecycle("Creating data.tar.gz");
|
||||
var dataTarBuffer = new ByteArrayOutputStream(12 * 1024 * 1024);
|
||||
try (var output = new TarArchiveOutputStream(new GZIPOutputStream(dataTarBuffer))) {
|
||||
output.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
|
||||
|
||||
Set<String> directories = new HashSet<>();
|
||||
putEntry(directories, output, "." + getTargetPath(), appShBytes, EXECUTABLE_MODE);
|
||||
putEntry(directories, output, "." + getLauncherPath(), launcherScriptBytes, EXECUTABLE_MODE);
|
||||
putEntry(directories, output, "." + getDesktopFilePath(), desktopInfoBytes, REGULAR_FILE_MODE);
|
||||
putEntry(directories, output, "." + getIconTargetPath(), iconBytes, REGULAR_FILE_MODE);
|
||||
}
|
||||
|
||||
LOGGER.lifecycle("Creating deb file");
|
||||
try (var output = new ArArchiveOutputStream(Files.newOutputStream(outputFile,
|
||||
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE))) {
|
||||
putArEntry(output, "debian-binary", "2.0\n".getBytes(StandardCharsets.UTF_8));
|
||||
putArEntry(output, "control.tar.gz", controlData.toByteArray());
|
||||
putArEntry(output, "data.tar.gz", dataTarBuffer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds one member to the outer ar container used by Debian packages.
|
||||
private static void putArEntry(ArArchiveOutputStream output, String name, byte[] content) throws IOException {
|
||||
ArArchiveEntry entry = new ArArchiveEntry(name, content.length);
|
||||
output.putArchiveEntry(entry);
|
||||
output.write(content);
|
||||
output.closeArchiveEntry();
|
||||
}
|
||||
|
||||
/// Generates the package metadata file.
|
||||
///
|
||||
/// `Depends` is deliberately omitted so the package stays policy-light and
|
||||
/// leaves runtime dependency resolution to the target environment.
|
||||
private String getControl(long appSize, long launcherScriptSize, long desktopInfoSize, long iconSize) {
|
||||
long installedSize = (appSize + launcherScriptSize + desktopInfoSize + iconSize + 1023) / 1024;
|
||||
|
||||
return """
|
||||
Package: %s
|
||||
Version: %s
|
||||
Section: games
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Installed-Size: %d
|
||||
Maintainer: Glavo <zjx001202@gmail.com>
|
||||
Description: Hello Minecraft! Launcher
|
||||
Homepage: https://github.com/HMCL-dev/HMCL
|
||||
""".formatted(getCurrentType().getPackageName(), getVersion().get(), Math.max(installedSize, 1)) + "\n";
|
||||
}
|
||||
|
||||
private static final String COMMON_LAUNCHER_PATH = "/usr/bin/hmcl";
|
||||
|
||||
/// Registers the channel command into the shared `hmcl` alternatives group.
|
||||
private String getPostinst() {
|
||||
return """
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ "$1" = configure ]; then
|
||||
update-alternatives --install %s hmcl %s %d
|
||||
fi
|
||||
""".formatted(COMMON_LAUNCHER_PATH, getLauncherPath(), getCurrentType().getAlternativesPriority());
|
||||
}
|
||||
|
||||
/// Removes the channel command from the shared `hmcl` alternatives group.
|
||||
private String getPrerm() {
|
||||
return """
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ "$1" = remove ] || [ "$1" = deconfigure ]; then
|
||||
update-alternatives --remove hmcl %s
|
||||
fi
|
||||
""".formatted(getLauncherPath());
|
||||
}
|
||||
|
||||
/// Creates a tiny wrapper that launches the bundled shell script from the user's home directory.
|
||||
private String getLauncherScript() {
|
||||
return """
|
||||
#!/usr/bin/env bash
|
||||
cd "$HOME"
|
||||
if [ -z "${HMCL_USER_HOME:-}" ]; then
|
||||
if [ -z "${XDG_DATA_HOME:-}" ]; then
|
||||
export HMCL_USER_HOME="$HOME/.local/share/hmcl"
|
||||
else
|
||||
export HMCL_USER_HOME="$XDG_DATA_HOME/hmcl"
|
||||
fi
|
||||
fi
|
||||
if [ -z "${HMCL_LOCAL_HOME:-}" ]; then
|
||||
export HMCL_LOCAL_HOME="$HMCL_USER_HOME/local-%s"
|
||||
fi
|
||||
if [ -z "${HMCL_DEPENDENCIES_DIR:-}" ]; then
|
||||
export HMCL_DEPENDENCIES_DIR="$HMCL_USER_HOME/dependencies"
|
||||
fi
|
||||
exec %s "$@"
|
||||
""".formatted(getCurrentTypeName(), getTargetPath());
|
||||
}
|
||||
|
||||
/// Generates the desktop entry that points to the channel-specific launcher command.
|
||||
private String getDesktopInfo() {
|
||||
return """
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=%s
|
||||
Comment=Hello Minecraft! Launcher
|
||||
Exec=%s
|
||||
Icon=%s
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
Categories=Game;
|
||||
Keywords=mc;minecraft;
|
||||
""".formatted(getCurrentType().getDisplayName(), getLauncherPath(), getIconTargetPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.gradle.pack;
|
||||
|
||||
/// Debian packaging metadata for one HMCL release type.
|
||||
///
|
||||
/// The package name, installed command, desktop file, and alternatives
|
||||
/// priority are intentionally centralized here so `CreateDeb` can stay focused
|
||||
/// on archive layout instead of duplicating channel-specific branching.
|
||||
public enum ReleaseType {
|
||||
STABLE("stable", "hmcl", "HMCL", 100),
|
||||
DEVELOPMENT("beta", "hmcl-beta", "HMCL (Beta)", 200),
|
||||
NIGHTLY("nightly", "hmcl-nightly", "HMCL (Nightly)", 300);
|
||||
|
||||
private final String name;
|
||||
private final String packageName;
|
||||
private final String displayName;
|
||||
private final int alternativesPriority;
|
||||
|
||||
ReleaseType(String name, String packageName, String displayName, int alternativesPriority) {
|
||||
this.name = name;
|
||||
this.packageName = packageName;
|
||||
this.displayName = displayName;
|
||||
this.alternativesPriority = alternativesPriority;
|
||||
}
|
||||
|
||||
///
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Debian package name written into `control` and used in the output filename.
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/// Priority used when registering the generic `hmcl` alias.
|
||||
public int getAlternativesPriority() {
|
||||
return alternativesPriority;
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ pipeline {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'bash ./config/jenkins/config-jenkins.sh'
|
||||
sh './gradlew clean makeExecutables --stacktrace --no-daemon'
|
||||
sh './gradlew clean makeExecutables makeDeb --stacktrace --no-daemon'
|
||||
archiveArtifacts artifacts: 'HMCL/build/libs/*'
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ pipeline {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'bash ./config/jenkins/config-jenkins.sh'
|
||||
sh './gradlew clean makeExecutables --stacktrace --no-daemon'
|
||||
sh './gradlew clean makeExecutables makeDeb --stacktrace --no-daemon'
|
||||
archiveArtifacts artifacts: 'HMCL/build/libs/*'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ hmclauncher = { module = "org.glavo.hmcl:HMCLauncher", version.ref = "hmclaunche
|
||||
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
|
||||
kala-compress-zip = { module = "org.glavo.kala:kala-compress-archivers-zip", version.ref = "kala-compress" }
|
||||
kala-compress-tar = { module = "org.glavo.kala:kala-compress-archivers-tar", version.ref = "kala-compress" }
|
||||
kala-compress-ar = { module = "org.glavo.kala:kala-compress-archivers-ar", version.ref = "kala-compress" }
|
||||
simple-png-javafx = { module = "org.glavo:simple-png-javafx", version.ref = "simple-png-javafx" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
tomlj = { module = "org.tomlj:tomlj", version.ref = "tomlj" }
|
||||
|
||||
Reference in New Issue
Block a user