diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 292e771fc9..fb985ceac9 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a869921e7..5c0715591d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/HMCL/build.gradle.kts b/HMCL/build.gradle.kts index ef99eeb88e..6606ecc755 100644 --- a/HMCL/build.gradle.kts +++ b/HMCL/build.gradle.kts @@ -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 { @@ -377,12 +404,14 @@ val upgradeTerracottaConfig = tasks.register("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") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 789dca8a86..568bad20c9 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { implementation(libs.gson) implementation(libs.jna) implementation(libs.kala.compress.tar) + implementation(libs.kala.compress.ar) } java { diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/CreateDeb.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/CreateDeb.java new file mode 100644 index 0000000000..c2c2e151ba --- /dev/null +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/CreateDeb.java @@ -0,0 +1,300 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ +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 getVersion(); + + /// Release type metadata that controls package name, launcher name, and alias priority. + @Input + public abstract Property 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 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 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 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 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 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 + 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()); + } +} diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/ReleaseType.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/ReleaseType.java new file mode 100644 index 0000000000..ee22a83ebc --- /dev/null +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/pack/ReleaseType.java @@ -0,0 +1,60 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ +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; + } +} diff --git a/config/jenkins/dev/Jenkinsfile b/config/jenkins/dev/Jenkinsfile index c5848e5faa..aae0b520da 100644 --- a/config/jenkins/dev/Jenkinsfile +++ b/config/jenkins/dev/Jenkinsfile @@ -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/*' } } diff --git a/config/jenkins/stable/Jenkinsfile b/config/jenkins/stable/Jenkinsfile index f7c24200d8..2da4bf2af5 100644 --- a/config/jenkins/stable/Jenkinsfile +++ b/config/jenkins/stable/Jenkinsfile @@ -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/*' } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1e4eb85851..e89be83f4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" }