Skip to content

Commit 8293383

Browse files
committed
Fix: Ship Complete Native Remotely Releases.
1 parent f337d31 commit 8293383

3 files changed

Lines changed: 236 additions & 57 deletions

File tree

.github/workflows/release.yml

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,20 +141,34 @@ jobs:
141141
token: ${{ secrets.REPROJECTS_PAT }}
142142
path: ReProjects/ReSync
143143
ref: ${{ steps.refs.outputs.resync }}
144-
- name: Build Remotely Jar
144+
- name: Build Remotely Jar And Linux AppImage
145145
working-directory: ReProjects/Remotely
146146
shell: bash
147147
run: |
148148
chmod +x gradlew
149-
flock /var/lock/restudio-release-build.lock ./gradlew -Premotely.version=${{ needs.prepare.outputs.version }} fatJar --no-daemon --stacktrace
149+
flock /var/lock/restudio-release-build.lock ./gradlew -Premotely.version=${{ needs.prepare.outputs.version }} fatJar createLinuxAppImage --no-daemon --stacktrace
150150
install -d release-artifacts
151151
cp build/libs/Remotely-Fat.jar "release-artifacts/Remotely-${{ needs.prepare.outputs.version }}.jar"
152+
app_dir=build/app-image/Remotely
153+
ln -s bin/Remotely "$app_dir/AppRun"
154+
cp packaging/Remotely.png "$app_dir/remotely.png"
155+
printf '[Desktop Entry]\nType=Application\nName=Remotely\nComment=Minecraft Server IDE\nExec=Remotely\nIcon=remotely\nCategories=Development;\nTerminal=false\n' > "$app_dir/remotely.desktop"
156+
curl --fail --location --retry 3 --output appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
157+
printf 'a6d71e2b6cd66f8e8d16c37ad164658985e0cf5fcaa950c90a482890cb9d13e0 appimagetool.AppImage\n' | sha256sum --check
158+
chmod +x appimagetool.AppImage
159+
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 ./appimagetool.AppImage "$app_dir" "release-artifacts/Remotely-${{ needs.prepare.outputs.version }}.AppImage"
152160
- uses: actions/upload-artifact@v7
153161
with:
154162
name: remotely-jar
155163
path: ReProjects/Remotely/release-artifacts/*.jar
156164
if-no-files-found: error
157165
retention-days: 30
166+
- uses: actions/upload-artifact@v7
167+
with:
168+
name: remotely-linux
169+
path: ReProjects/Remotely/release-artifacts/*.AppImage
170+
if-no-files-found: error
171+
retention-days: 30
158172
159173
windows:
160174
needs: prepare
@@ -224,8 +238,89 @@ jobs:
224238
if-no-files-found: error
225239
retention-days: 30
226240
241+
macos:
242+
needs: prepare
243+
strategy:
244+
fail-fast: false
245+
matrix:
246+
include:
247+
- runner: macos-15
248+
platform: macos-arm64
249+
- runner: macos-15-intel
250+
platform: macos-x64
251+
runs-on: ${{ matrix.runner }}
252+
steps:
253+
- uses: actions/checkout@v7
254+
with:
255+
path: ReProjects/Remotely
256+
ref: ${{ needs.prepare.outputs.source_ref }}
257+
- id: refs
258+
shell: bash
259+
run: |
260+
lock=ReProjects/Remotely/.restudio/workspace.lock.json
261+
for project in ReScreen Remodel Rebase Recast ReSync; do
262+
printf '%s=%s\n' "${project,,}" "$(jq -r --arg project "$project" '.[$project]' "$lock")" >> "$GITHUB_OUTPUT"
263+
done
264+
- uses: actions/checkout@v7
265+
with:
266+
repository: RedxAx/ReScreen
267+
token: ${{ secrets.REPROJECTS_PAT }}
268+
path: ReProjects/ReScreen
269+
ref: ${{ steps.refs.outputs.rescreen }}
270+
- uses: actions/checkout@v7
271+
with:
272+
repository: RedxAx/Remodel
273+
token: ${{ secrets.REPROJECTS_PAT }}
274+
path: ReProjects/Remodel
275+
ref: ${{ steps.refs.outputs.remodel }}
276+
- uses: actions/checkout@v7
277+
with:
278+
repository: RedxAx/Rebase
279+
token: ${{ secrets.REPROJECTS_PAT }}
280+
path: ReProjects/Rebase
281+
ref: ${{ steps.refs.outputs.rebase }}
282+
- uses: actions/checkout@v7
283+
with:
284+
repository: RedxAx/Recast
285+
token: ${{ secrets.REPROJECTS_PAT }}
286+
path: ReProjects/Recast
287+
ref: ${{ steps.refs.outputs.recast }}
288+
- uses: actions/checkout@v7
289+
with:
290+
repository: RedxAx/ReSync
291+
token: ${{ secrets.REPROJECTS_PAT }}
292+
path: ReProjects/ReSync
293+
ref: ${{ steps.refs.outputs.resync }}
294+
- uses: actions/setup-java@v5
295+
with:
296+
distribution: temurin
297+
java-version: '21'
298+
cache: gradle
299+
- name: Build Remotely Disk Image
300+
working-directory: ReProjects/Remotely
301+
shell: bash
302+
run: |
303+
iconset=build/Remotely.iconset
304+
mkdir -p "$iconset" release-artifacts
305+
for entry in '16 icon_16x16.png' '32 icon_16x16@2x.png' '32 icon_32x32.png' '64 icon_32x32@2x.png' '128 icon_128x128.png' '256 icon_128x128@2x.png' '256 icon_256x256.png' '512 icon_256x256@2x.png' '512 icon_512x512.png' '1024 icon_512x512@2x.png'; do
306+
read -r size name <<< "$entry"
307+
sips -z "$size" "$size" packaging/Remotely.png --out "$iconset/$name" >/dev/null
308+
done
309+
iconutil --convert icns "$iconset" --output build/Remotely.icns
310+
chmod +x gradlew
311+
./gradlew -Premotely.version=${{ needs.prepare.outputs.version }} -Premotely.packageIcon="$PWD/build/Remotely.icns" createMacDmg --no-daemon --stacktrace
312+
disk_image=$(find build/dist-macos -maxdepth 1 -type f -name '*.dmg' -print -quit)
313+
[[ -n "$disk_image" ]] || { printf 'Remotely Disk Image Was Not Created\n' >&2; exit 1; }
314+
cp "$disk_image" "release-artifacts/Remotely-${{ needs.prepare.outputs.version }}-${{ matrix.platform }}.dmg"
315+
- uses: actions/upload-artifact@v7
316+
with:
317+
name: remotely-${{ matrix.platform }}
318+
path: ReProjects/Remotely/release-artifacts/*.dmg
319+
if-no-files-found: error
320+
retention-days: 30
321+
227322
publish:
228-
needs: [prepare, jar, windows]
323+
needs: [prepare, jar, windows, macos]
229324
runs-on: [self-hosted, node1, linux, release]
230325
environment: production
231326
permissions:
@@ -262,3 +357,6 @@ jobs:
262357
fi
263358
release-tools/scripts/releases/publish-release.sh "artifacts/Remotely-${{ needs.prepare.outputs.version }}.jar" "$project" "${{ needs.prepare.outputs.version }}" "$channel" crossplatform "$visibility"
264359
release-tools/scripts/releases/publish-release.sh "artifacts/Remotely-${{ needs.prepare.outputs.version }}.exe" "$project" "${{ needs.prepare.outputs.version }}" "$channel" windows "$visibility"
360+
release-tools/scripts/releases/publish-release.sh "artifacts/Remotely-${{ needs.prepare.outputs.version }}.AppImage" "$project" "${{ needs.prepare.outputs.version }}" "$channel" linux "$visibility"
361+
release-tools/scripts/releases/publish-release.sh "artifacts/Remotely-${{ needs.prepare.outputs.version }}-macos-arm64.dmg" "$project" "${{ needs.prepare.outputs.version }}" "$channel" macos-arm64 "$visibility"
362+
release-tools/scripts/releases/publish-release.sh "artifacts/Remotely-${{ needs.prepare.outputs.version }}-macos-x64.dmg" "$project" "${{ needs.prepare.outputs.version }}" "$channel" macos-x64 "$visibility"

build.gradle.kts

Lines changed: 135 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import groovy.json.JsonSlurper
22
import org.gradle.api.file.DuplicatesStrategy
33
import org.gradle.api.publish.maven.MavenPublication
4+
import java.util.zip.ZipFile
45

56
plugins {
67
id("java-library")
@@ -16,6 +17,38 @@ application {
1617
mainClass.set("redxax.oxy.remotely.RemotelyInit")
1718
}
1819

20+
val reStudioReleaseJarTasks: List<Any> = if (useReStudioSourceDependencies) {
21+
listOf(
22+
gradle.includedBuild("ReScreen").task(":jar"),
23+
gradle.includedBuild("Remodel").task(":jar"),
24+
gradle.includedBuild("Rebase").task(":jar"),
25+
gradle.includedBuild("Recast").task(":recast-api:jar"),
26+
gradle.includedBuild("Recast").task(":recast-bridge:jar"),
27+
gradle.includedBuild("ReSync").task(":ReSyncCore:jar")
28+
)
29+
} else {
30+
emptyList()
31+
}
32+
33+
val releaseRequiredClasses = listOf(
34+
"restudio/rescreen/config/UiConfigStore.class",
35+
"restudio/rebase/Rebase.class",
36+
"redxax/restudio/Remodel/Main.class",
37+
"dev/restudio/recast/api/FeatureDescriptor.class",
38+
"dev/restudio/recast/bridge/BridgeEntry.class",
39+
"restudio/resync/network/NetworkFrame.class"
40+
)
41+
42+
fun requireReleaseClasses(archives: Collection<File>, artifactName: String) {
43+
val jarFiles = archives.filter { it.isFile && it.extension.equals("jar", ignoreCase = true) }
44+
val missingClasses = releaseRequiredClasses.filter { className ->
45+
jarFiles.none { archive -> ZipFile(archive).use { it.getEntry(className) != null } }
46+
}
47+
require(missingClasses.isEmpty()) {
48+
"$artifactName Is Missing Required Classes: ${missingClasses.joinToString()}"
49+
}
50+
}
51+
1952
tasks.named<JavaExec>("run") {
2053
if (useReStudioSourceDependencies) {
2154
dependsOn(
@@ -424,81 +457,129 @@ tasks.jar {
424457
archiveFileName.set("Remotely-App.jar")
425458
}
426459

427-
tasks.register<Exec>("createInstaller") {
428-
dependsOn("clean", "jar")
460+
tasks.register<Jar>("fatJar") {
461+
group = "build"
462+
dependsOn(reStudioReleaseJarTasks)
463+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
464+
archiveFileName.set("Remotely-Fat.jar")
465+
from(sourceSets.main.get().output)
466+
from({
467+
configurations.runtimeClasspath.get().filter { it.exists() }.map {
468+
if (it.isDirectory) it else zipTree(it)
469+
}
470+
})
471+
exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA")
472+
manifest {
473+
attributes["Main-Class"] = "redxax.oxy.remotely.RemotelyInit"
474+
attributes["Implementation-Version"] = project.version.toString()
475+
attributes["Automatic-Module-Name"] = "dev.restudio.remotely.app"
476+
}
477+
doLast {
478+
requireReleaseClasses(listOf(archiveFile.get().asFile), "Remotely Fat Jar")
479+
}
480+
}
429481

430-
val javaToolchains = project.extensions.getByType<JavaToolchainService>()
431-
val javaLauncher = javaToolchains.launcherFor {
432-
languageVersion.set(JavaLanguageVersion.of(21))
433-
}.get()
482+
val packageJarName = "Remotely-App.jar"
483+
val cleanVersion = version.toString().substringBefore('-').replace(Regex("[^0-9.]"), "")
484+
val javaLauncher = extensions.getByType<JavaToolchainService>().launcherFor {
485+
languageVersion.set(JavaLanguageVersion.of(21))
486+
}
487+
val jpackageExecutable = javaLauncher.map {
488+
val executable = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) "jpackage.exe" else "jpackage"
489+
File(it.metadata.installationPath.asFile, "bin/$executable").absolutePath
490+
}
434491

435-
val jdkHome = javaLauncher.metadata.installationPath.asFile
436-
val jpackagePath = File(jdkHome, "bin/jpackage.exe").absolutePath
492+
fun preparePackageInput(stagingDir: File, outputDir: File, artifactName: String) {
493+
outputDir.deleteRecursively()
494+
stagingDir.deleteRecursively()
495+
stagingDir.mkdirs()
496+
copy {
497+
from(tasks.named<Jar>("fatJar").flatMap { it.archiveFile })
498+
into(stagingDir)
499+
rename { packageJarName }
500+
}
501+
requireReleaseClasses(fileTree(stagingDir) { include("*.jar") }.files, artifactName)
502+
}
437503

438-
val stagingDir = layout.buildDirectory.dir("staging").get().asFile.absolutePath
439-
val inputDir = layout.buildDirectory.dir("libs").get().asFile.absolutePath
440-
val outputDir = layout.buildDirectory.dir("dist").get().asFile.absolutePath
441-
val jarName = "Remotely-App.jar"
504+
tasks.register<Exec>("createInstaller") {
505+
dependsOn("fatJar")
506+
507+
val stagingDir = layout.buildDirectory.dir("package-input/windows").get().asFile
508+
val outputDir = layout.buildDirectory.dir("dist").get().asFile
442509
val iconPath = layout.projectDirectory.file("packaging/Remotely.ico").asFile.absolutePath
443-
val cleanVersion = version.toString().split("-")[0].replace(Regex("[^0-9.]"), "")
444510

445511
doFirst {
446-
println("--------------------------------------------------")
447-
println("Using jpackage: $jpackagePath")
448-
println("--------------------------------------------------")
449-
450-
file(outputDir).deleteRecursively()
451-
file(stagingDir).deleteRecursively()
452-
file(stagingDir).mkdirs()
453-
454-
copy {
455-
from(inputDir)
456-
into(stagingDir)
457-
include(jarName)
458-
}
459-
460-
copy {
461-
from(configurations.runtimeClasspath)
462-
into(stagingDir)
463-
}
464-
465-
println("Staging directory contents:")
466-
file(stagingDir).listFiles()?.forEach { println(it.name) }
512+
preparePackageInput(stagingDir, outputDir, "Remotely Windows Installer")
467513
}
468514

469515
commandLine(
470-
jpackagePath,
516+
jpackageExecutable.get(),
471517
"--type", "exe",
472-
"--dest", outputDir,
473-
"--input", stagingDir,
518+
"--dest", outputDir.absolutePath,
519+
"--input", stagingDir.absolutePath,
474520
"--name", "Remotely",
475-
"--main-jar", jarName,
521+
"--main-jar", packageJarName,
476522
"--main-class", application.mainClass.get(),
477523
"--app-version", cleanVersion,
478524
"--icon", iconPath,
479525
"--win-shortcut",
480526
"--win-menu",
481527
"--win-menu-group", "ReStudio",
482528
"--win-dir-chooser",
483-
// "--win-console",
484529
"--java-options", "-Dfile.encoding=UTF-8 -Xmx4G"
485530
)
486531
}
487532

488-
tasks.register<Jar>("fatJar") {
489-
group = "build"
490-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
491-
archiveFileName.set("Remotely-Fat.jar")
492-
from(sourceSets.main.get().output)
493-
from({
494-
configurations.runtimeClasspath.get().filter { it.exists() }.map {
495-
if (it.isDirectory) it else zipTree(it)
496-
}
497-
})
498-
exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA")
499-
manifest {
500-
attributes["Main-Class"] = "redxax.oxy.remotely.RemotelyInit"
501-
attributes["Implementation-Version"] = project.version.toString()
502-
attributes["Automatic-Module-Name"] = "dev.restudio.remotely.app"
533+
tasks.register<Exec>("createLinuxAppImage") {
534+
dependsOn("fatJar")
535+
536+
val stagingDir = layout.buildDirectory.dir("package-input/linux").get().asFile
537+
val outputDir = layout.buildDirectory.dir("app-image").get().asFile
538+
val iconPath = layout.projectDirectory.file("packaging/Remotely.png").asFile.absolutePath
539+
540+
doFirst {
541+
preparePackageInput(stagingDir, outputDir, "Remotely Linux AppImage")
542+
}
543+
544+
commandLine(
545+
jpackageExecutable.get(),
546+
"--type", "app-image",
547+
"--dest", outputDir.absolutePath,
548+
"--input", stagingDir.absolutePath,
549+
"--name", "Remotely",
550+
"--main-jar", packageJarName,
551+
"--main-class", application.mainClass.get(),
552+
"--app-version", cleanVersion,
553+
"--icon", iconPath,
554+
"--java-options", "-Dfile.encoding=UTF-8 -Xmx4G"
555+
)
556+
}
557+
558+
tasks.register<Exec>("createMacDmg") {
559+
dependsOn("fatJar")
560+
561+
val stagingDir = layout.buildDirectory.dir("package-input/macos").get().asFile
562+
val outputDir = layout.buildDirectory.dir("dist-macos").get().asFile
563+
val iconPath = providers.gradleProperty("remotely.packageIcon").orElse(
564+
layout.projectDirectory.file("packaging/Remotely.icns").asFile.absolutePath
565+
)
566+
val macVersion = cleanVersion.split('.').take(3).joinToString(".")
567+
568+
doFirst {
569+
preparePackageInput(stagingDir, outputDir, "Remotely macOS Disk Image")
503570
}
571+
572+
commandLine(
573+
jpackageExecutable.get(),
574+
"--type", "dmg",
575+
"--dest", outputDir.absolutePath,
576+
"--input", stagingDir.absolutePath,
577+
"--name", "Remotely",
578+
"--main-jar", packageJarName,
579+
"--main-class", application.mainClass.get(),
580+
"--app-version", macVersion,
581+
"--icon", iconPath.get(),
582+
"--mac-package-identifier", "net.restudiomc.remotely",
583+
"--java-options", "-Dfile.encoding=UTF-8 -Xmx4G"
584+
)
504585
}

packaging/Remotely.png

1.88 KB
Loading

0 commit comments

Comments
 (0)