Merge pull request #6710 from duplicati/feature/add-appimage-builds
Added support for building AppImage
This commit is contained in:
Vendored
+3
@@ -31,6 +31,9 @@
|
||||
"--targets", "win-arm64-agent.msi",
|
||||
"--targets", "linux-x64-gui.zip",
|
||||
"--targets", "linux-arm64-gui.zip",
|
||||
"--targets", "linux-x64-gui.appimage",
|
||||
"--targets", "linux-arm64-gui.appimage",
|
||||
"--targets", "linux-arm7-gui.appimage",
|
||||
"--targets", "osx-x64-gui.dmg",
|
||||
"--targets", "osx-arm64-gui.dmg",
|
||||
"--targets", "osx-x64-gui.pkg",
|
||||
|
||||
@@ -39,6 +39,40 @@ public static partial class Command
|
||||
/// <param name="CreatedFile">The created package file path</param>
|
||||
public record BuiltPackage(PackageTarget Target, string CreatedFile);
|
||||
|
||||
/// <summary>
|
||||
/// Generates the metainfo content with the releases block injected
|
||||
/// </summary>
|
||||
/// <param name="baseDir">The base directory</param>
|
||||
/// <param name="metainfoPath">The path to the source metainfo file</param>
|
||||
/// <returns>The modified metainfo content</returns>
|
||||
public static string GetMetainfoWithReleases(string baseDir, string metainfoPath)
|
||||
{
|
||||
var versionPath = Path.Combine(baseDir, "ReleaseBuilder", "build_version.txt");
|
||||
var changelogPath = Path.Combine(baseDir, "ReleaseBuilder", "changelog-news.txt");
|
||||
|
||||
var version = File.ReadAllText(versionPath).Trim();
|
||||
var changelog = File.ReadAllText(changelogPath).Trim();
|
||||
var metainfo = File.ReadAllText(metainfoPath);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(" <releases>");
|
||||
sb.AppendLine($" <release version=\"{version}\" date=\"{DateTime.UtcNow:yyyy-MM-dd}\">");
|
||||
sb.AppendLine(" <description>");
|
||||
sb.AppendLine($" <p>{System.Security.SecurityElement.Escape(changelog)}</p>");
|
||||
sb.AppendLine(" </description>");
|
||||
sb.AppendLine(" </release>");
|
||||
sb.AppendLine(" </releases>");
|
||||
|
||||
// Insert before </component>
|
||||
var insertIndex = metainfo.LastIndexOf("</component>");
|
||||
if (insertIndex >= 0)
|
||||
{
|
||||
metainfo = metainfo.Insert(insertIndex, sb.ToString());
|
||||
}
|
||||
|
||||
return metainfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the packages for the specified build targets.
|
||||
/// </summary>
|
||||
@@ -140,6 +174,10 @@ public static partial class Command
|
||||
await BuildZipPackage(Path.Combine(buildRoot, target.BuildTargetString), $"duplicati-{rtcfg.ReleaseInfo.ReleaseName}-{target.BuildTargetString}", tempFile, target, rtcfg);
|
||||
break;
|
||||
|
||||
case PackageType.AppImage:
|
||||
await BuildAppImagePackage(baseDir, buildRoot, tempFile, target, rtcfg);
|
||||
break;
|
||||
|
||||
case PackageType.MSI:
|
||||
await BuildMsiPackage(baseDir, buildRoot, tempFile, target, rtcfg);
|
||||
break;
|
||||
@@ -253,6 +291,170 @@ public static partial class Command
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an AppImage package asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="baseDir">The base directory.</param>
|
||||
/// <param name="buildRoot">The build root directory.</param>
|
||||
/// <param name="appImageFile">The AppImage file to generate.</param>
|
||||
/// <param name="target">The package target.</param>
|
||||
/// <param name="rtcfg">The runtime configuration.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
static async Task BuildAppImagePackage(string baseDir, string buildRoot, string appImageFile, PackageTarget target, RuntimeConfig rtcfg)
|
||||
{
|
||||
if (target.OS != OSType.Linux)
|
||||
throw new Exception($"AppImage is only supported for Linux targets, got: {target.OS}");
|
||||
|
||||
if (target.Interface != InterfaceType.GUI)
|
||||
throw new Exception($"AppImage is only supported for GUI interface, got: {target.Interface}");
|
||||
|
||||
var tmpRoot = Path.Combine(buildRoot, "tmp-appimage");
|
||||
if (Directory.Exists(tmpRoot))
|
||||
Directory.Delete(tmpRoot, true);
|
||||
Directory.CreateDirectory(tmpRoot);
|
||||
|
||||
var appDir = Path.Combine(tmpRoot, "AppDir");
|
||||
var appDirUsr = Path.Combine(appDir, "usr");
|
||||
var appDirLib = Path.Combine(appDirUsr, "lib", "duplicati");
|
||||
var appDirBin = Path.Combine(appDirUsr, "bin");
|
||||
Directory.CreateDirectory(appDirLib);
|
||||
Directory.CreateDirectory(appDirBin);
|
||||
|
||||
EnvHelper.CopyDirectory(
|
||||
Path.Combine(buildRoot, target.BuildTargetString),
|
||||
appDirLib,
|
||||
recursive: true);
|
||||
|
||||
await PackageSupport.InstallPackageIdentifier(appDirLib, target);
|
||||
await PackageSupport.RenameExecutables(appDirLib);
|
||||
await PackageSupport.SetPermissionFlags(appDirLib, rtcfg);
|
||||
|
||||
var availableExecutables = ExecutableRenames.Values
|
||||
.Where(x => File.Exists(Path.Combine(appDirLib, x)))
|
||||
.ToList();
|
||||
|
||||
if (!availableExecutables.Any())
|
||||
throw new Exception("No executables found in AppImage payload");
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
throw new PlatformNotSupportedException("AppImage packaging is not supported on Windows, use WSL or Docker");
|
||||
|
||||
foreach (var executable in availableExecutables)
|
||||
{
|
||||
var linkPath = Path.Combine(appDirBin, executable);
|
||||
await ProcessHelper.Execute([
|
||||
"ln", "-s",
|
||||
Path.Combine("..", "lib", "duplicati", executable),
|
||||
linkPath
|
||||
]);
|
||||
}
|
||||
|
||||
var sharedDir = Path.Combine(baseDir, "ReleaseBuilder", "Resources", "shared");
|
||||
var appImageResourcesDir = Path.Combine(baseDir, "ReleaseBuilder", "Resources", "appimage");
|
||||
|
||||
File.Copy(
|
||||
Path.Combine(sharedDir, "desktop", "duplicati.desktop"),
|
||||
Path.Combine(appDir, "com.duplicati.app.desktop"),
|
||||
true
|
||||
);
|
||||
|
||||
var applicationsDir = Path.Combine(appDirUsr, "share", "applications");
|
||||
Directory.CreateDirectory(applicationsDir);
|
||||
File.Copy(
|
||||
Path.Combine(sharedDir, "desktop", "duplicati.desktop"),
|
||||
Path.Combine(applicationsDir, "com.duplicati.app.desktop"),
|
||||
true
|
||||
);
|
||||
|
||||
var metainfoDir = Path.Combine(appDirUsr, "share", "metainfo");
|
||||
Directory.CreateDirectory(metainfoDir);
|
||||
|
||||
// Use the correct reverse-DNS filename for metadata and desktop file to satisfy appstreamcli
|
||||
var metainfoSrc = Path.Combine(sharedDir, "metainfo", "com.duplicati.app.metainfo.xml");
|
||||
var metainfoDest = Path.Combine(metainfoDir, "com.duplicati.app.appdata.xml");
|
||||
var metainfoContent = GetMetainfoWithReleases(baseDir, metainfoSrc);
|
||||
// Update launchable to match new desktop filename
|
||||
metainfoContent = metainfoContent.Replace("duplicati.desktop", "com.duplicati.app.desktop");
|
||||
File.WriteAllText(metainfoDest, metainfoContent);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
throw new PlatformNotSupportedException("Creating unresolved symlinks is not supported on Windows, use WSL or Docker");
|
||||
|
||||
var iconSource = Path.Combine(sharedDir, "pixmaps", "duplicati.png");
|
||||
File.Copy(iconSource, Path.Combine(appDir, "duplicati.png"), true);
|
||||
File.Copy(iconSource, Path.Combine(appDir, ".DirIcon"), true);
|
||||
|
||||
var defaultExecutable = availableExecutables.Contains("duplicati")
|
||||
? "duplicati"
|
||||
: availableExecutables.First();
|
||||
|
||||
var appRunTemplate = File.ReadAllText(Path.Combine(appImageResourcesDir, "AppRun.template"));
|
||||
var appRunPayload = appRunTemplate
|
||||
.Replace("%EXECUTABLE_LIST%", string.Join("\n", availableExecutables.Select(x => $" echo \" - {x}\"")))
|
||||
.Replace("%INVOCATION_CASES%", string.Join("\n", availableExecutables.Select(x => $" {x}) exec \"${{HERE}}/usr/bin/{x}\" \"$@\" ;;")))
|
||||
.Replace("%FLAG_CASES%", string.Join("\n", availableExecutables.Select(x =>
|
||||
$" --{x})\n shift\n exec \"${{HERE}}/usr/bin/{x}\" \"$@\"\n ;;")))
|
||||
.Replace("%DEFAULT_EXECUTABLE%", defaultExecutable);
|
||||
|
||||
File.WriteAllText(Path.Combine(appDir, "AppRun"), appRunPayload);
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
var filemode755 = UnixFileMode.UserRead
|
||||
| UnixFileMode.UserWrite
|
||||
| UnixFileMode.UserExecute
|
||||
| UnixFileMode.GroupRead
|
||||
| UnixFileMode.GroupExecute
|
||||
| UnixFileMode.OtherRead
|
||||
| UnixFileMode.OtherExecute;
|
||||
|
||||
File.SetUnixFileMode(Path.Combine(appDir, "AppRun"), filemode755);
|
||||
}
|
||||
|
||||
File.Copy(
|
||||
Path.Combine(appImageResourcesDir, "Dockerfile.build"),
|
||||
Path.Combine(tmpRoot, "Dockerfile"),
|
||||
true
|
||||
);
|
||||
|
||||
await ProcessHelper.Execute([
|
||||
"docker", "build",
|
||||
"-t", "duplicati/appimage-build:latest",
|
||||
tmpRoot
|
||||
], workingDirectory: tmpRoot);
|
||||
|
||||
var appImageArch = target.Arch switch
|
||||
{
|
||||
ArchType.x64 => "x86_64",
|
||||
ArchType.Arm64 => "aarch64",
|
||||
ArchType.Arm7 => "armhf",
|
||||
_ => throw new Exception($"Unsupported AppImage architecture: {target.Arch}")
|
||||
};
|
||||
|
||||
var appImageName = Path.GetFileName(appImageFile);
|
||||
var appImageTempPath = Path.Combine(tmpRoot, appImageName);
|
||||
if (File.Exists(appImageTempPath))
|
||||
File.Delete(appImageTempPath);
|
||||
|
||||
await ProcessHelper.Execute([
|
||||
"docker", "run",
|
||||
"--workdir", "/build",
|
||||
"--volume", $"{tmpRoot}:/build:rw",
|
||||
"-e", "APPIMAGE_EXTRACT_AND_RUN=1",
|
||||
"-e", $"ARCH={appImageArch}",
|
||||
"duplicati/appimage-build:latest",
|
||||
"/usr/local/bin/appimagetool",
|
||||
"/build/AppDir",
|
||||
$"/build/{appImageName}"
|
||||
]);
|
||||
|
||||
if (File.Exists(appImageFile))
|
||||
File.Delete(appImageFile);
|
||||
|
||||
File.Move(appImageTempPath, appImageFile);
|
||||
Directory.Delete(tmpRoot, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an MSI package asynchronously.
|
||||
/// </summary>
|
||||
@@ -904,6 +1106,11 @@ public static partial class Command
|
||||
Path.Combine(pkgroot, "usr", "share", "applications", "duplicati.desktop")
|
||||
));
|
||||
|
||||
var metainfoDest = Path.Combine(pkgroot, "usr", "share", "metainfo", "com.duplicati.app.metainfo.xml");
|
||||
var metainfoDir = Path.GetDirectoryName(metainfoDest)!;
|
||||
if (!Directory.Exists(metainfoDir)) Directory.CreateDirectory(metainfoDir);
|
||||
File.WriteAllText(metainfoDest, GetMetainfoWithReleases(baseDir, Path.Combine(sharedDir, "metainfo", "com.duplicati.app.metainfo.xml")));
|
||||
|
||||
supportFiles.AddRange(
|
||||
new[] { "duplicati.png", "duplicati.svg", "duplicati.xpm" }
|
||||
.Select(f => (
|
||||
@@ -1211,6 +1418,7 @@ public static partial class Command
|
||||
File.Copy(Path.Combine(sharedDir, "pixmaps", "duplicati.xpm"), Path.Combine(sources, "duplicati.xpm"));
|
||||
File.Copy(Path.Combine(sharedDir, "pixmaps", "duplicati.png"), Path.Combine(sources, "duplicati.png"));
|
||||
File.Copy(Path.Combine(sharedDir, "desktop", "duplicati.desktop"), Path.Combine(sources, "duplicati.desktop"));
|
||||
File.WriteAllText(Path.Combine(sources, "com.duplicati.app.metainfo.xml"), CreatePackage.GetMetainfoWithReleases(baseDir, Path.Combine(sharedDir, "metainfo", "com.duplicati.app.metainfo.xml")));
|
||||
File.Copy(Path.Combine(resourcesDir, "duplicati-install-recursive.sh"), Path.Combine(sources, "duplicati-install-recursive.sh"));
|
||||
if (target.Interface != InterfaceType.Agent)
|
||||
{
|
||||
|
||||
@@ -83,6 +83,16 @@ public static partial class Command
|
||||
"Duplicati.WindowsService.csproj"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The packages that require Docker to build
|
||||
/// </summary>
|
||||
private static readonly IReadOnlySet<PackageType> DockerDependingTargets = new HashSet<PackageType> {
|
||||
PackageType.Docker,
|
||||
PackageType.Deb,
|
||||
PackageType.RPM,
|
||||
PackageType.AppImage
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Some executables have shorter names that follow the Linux convention of all-lowercase
|
||||
/// </summary>
|
||||
@@ -596,7 +606,7 @@ public static partial class Command
|
||||
|
||||
if (!rtcfg.UseDockerBuild)
|
||||
{
|
||||
var unsupportedBuilds = buildTargets.Where(x => x.Package == PackageType.Docker || x.Package == PackageType.Deb || x.Package == PackageType.RPM).ToList();
|
||||
var unsupportedBuilds = buildTargets.Where(x => DockerDependingTargets.Contains(x.Package)).ToList();
|
||||
if (unsupportedBuilds.Any())
|
||||
throw new Exception($"The following packages cannot be built without Docker: {string.Join(", ", unsupportedBuilds.Select(x => x.PackageTargetString))}");
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ public enum PackageType
|
||||
/// </summary>
|
||||
Zip,
|
||||
/// <summary>
|
||||
/// The AppImage package
|
||||
/// </summary>
|
||||
AppImage,
|
||||
/// <summary>
|
||||
/// The Windows installer format
|
||||
/// </summary>
|
||||
MSI,
|
||||
@@ -171,6 +175,7 @@ public record PackageTarget(OSType OS, ArchType Arch, InterfaceType Interface, P
|
||||
=> package switch
|
||||
{
|
||||
PackageType.Zip => "zip",
|
||||
PackageType.AppImage => "appimage",
|
||||
PackageType.MSI => "msi",
|
||||
PackageType.Deb => "deb",
|
||||
PackageType.RPM => "rpm",
|
||||
|
||||
+124
-121
@@ -1,122 +1,125 @@
|
||||
// Copyright (C) 2025, The Duplicati Team
|
||||
// https://duplicati.com, hello@duplicati.com
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.CommandLine;
|
||||
|
||||
namespace ReleaseBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the executable
|
||||
/// </summary>
|
||||
class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The supported build packages
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<PackageTarget> SupportedPackageTargets = new[] {
|
||||
"win-x64-gui.zip",
|
||||
"win-x64-gui.msi",
|
||||
"win-x86-gui.zip",
|
||||
"win-x86-gui.msi",
|
||||
"win-arm64-gui.zip",
|
||||
"win-arm64-gui.msi",
|
||||
"win-x64-agent.msi",
|
||||
"win-arm64-agent.msi",
|
||||
"win-x64-agent.zip",
|
||||
"win-arm64-agent.zip",
|
||||
|
||||
"linux-x64-gui.zip",
|
||||
"linux-x64-gui.deb",
|
||||
"linux-x64-gui.rpm",
|
||||
"linux-x64-cli.zip",
|
||||
"linux-x64-cli.deb",
|
||||
"linux-x64-cli.rpm",
|
||||
"linux-x64-cli.docker",
|
||||
"linux-x64-cli.spk",
|
||||
"linux-x64-agent.zip",
|
||||
"linux-x64-agent.deb",
|
||||
"linux-x64-agent.rpm",
|
||||
"linux-x64-agent.docker",
|
||||
|
||||
"linux-arm7-gui.zip",
|
||||
"linux-arm7-gui.deb",
|
||||
"linux-arm7-cli.zip",
|
||||
"linux-arm7-cli.deb",
|
||||
"linux-arm7-cli.docker",
|
||||
"linux-arm7-cli.spk",
|
||||
"linux-arm7-agent.zip",
|
||||
"linux-arm7-agent.deb",
|
||||
|
||||
"linux-arm64-gui.zip",
|
||||
"linux-arm64-gui.deb",
|
||||
"linux-arm64-gui.rpm",
|
||||
"linux-arm64-cli.zip",
|
||||
"linux-arm64-cli.deb",
|
||||
"linux-arm64-cli.rpm",
|
||||
"linux-arm64-cli.docker",
|
||||
"linux-arm64-cli.spk",
|
||||
"linux-arm64-agent.zip",
|
||||
"linux-arm64-agent.deb",
|
||||
"linux-arm64-agent.rpm",
|
||||
"linux-arm64-agent.docker",
|
||||
|
||||
"osx-x64-gui.dmg",
|
||||
"osx-x64-gui.pkg",
|
||||
"osx-x64-agent.pkg",
|
||||
"osx-x64-cli.pkg",
|
||||
"osx-arm64-gui.dmg",
|
||||
"osx-arm64-gui.pkg",
|
||||
"osx-arm64-agent.pkg",
|
||||
"osx-arm64-cli.pkg",
|
||||
}
|
||||
.Select(x => PackageTarget.ParsePackageId(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// The return code of the application; shared state
|
||||
/// </summary>
|
||||
public static int? ReturnCode = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the builder
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
static async Task<int> Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReturnCode = await new RootCommand("Build tool for Duplicati")
|
||||
{
|
||||
Build.Command.Create(),
|
||||
CreateKey.Command.Create(),
|
||||
}.InvokeAsync(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
ReturnCode = 1;
|
||||
}
|
||||
|
||||
return ReturnCode ?? 2;
|
||||
}
|
||||
// Copyright (C) 2025, The Duplicati Team
|
||||
// https://duplicati.com, hello@duplicati.com
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.CommandLine;
|
||||
|
||||
namespace ReleaseBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the executable
|
||||
/// </summary>
|
||||
class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The supported build packages
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<PackageTarget> SupportedPackageTargets = new[] {
|
||||
"win-x64-gui.zip",
|
||||
"win-x64-gui.msi",
|
||||
"win-x86-gui.zip",
|
||||
"win-x86-gui.msi",
|
||||
"win-arm64-gui.zip",
|
||||
"win-arm64-gui.msi",
|
||||
"win-x64-agent.msi",
|
||||
"win-arm64-agent.msi",
|
||||
"win-x64-agent.zip",
|
||||
"win-arm64-agent.zip",
|
||||
|
||||
"linux-x64-gui.zip",
|
||||
"linux-x64-gui.deb",
|
||||
"linux-x64-gui.rpm",
|
||||
"linux-x64-gui.appimage",
|
||||
"linux-x64-cli.zip",
|
||||
"linux-x64-cli.deb",
|
||||
"linux-x64-cli.rpm",
|
||||
"linux-x64-cli.docker",
|
||||
"linux-x64-cli.spk",
|
||||
"linux-x64-agent.zip",
|
||||
"linux-x64-agent.deb",
|
||||
"linux-x64-agent.rpm",
|
||||
"linux-x64-agent.docker",
|
||||
|
||||
"linux-arm7-gui.zip",
|
||||
"linux-arm7-gui.deb",
|
||||
"linux-arm7-gui.appimage",
|
||||
"linux-arm7-cli.zip",
|
||||
"linux-arm7-cli.deb",
|
||||
"linux-arm7-cli.docker",
|
||||
"linux-arm7-cli.spk",
|
||||
"linux-arm7-agent.zip",
|
||||
"linux-arm7-agent.deb",
|
||||
|
||||
"linux-arm64-gui.zip",
|
||||
"linux-arm64-gui.deb",
|
||||
"linux-arm64-gui.rpm",
|
||||
"linux-arm64-gui.appimage",
|
||||
"linux-arm64-cli.zip",
|
||||
"linux-arm64-cli.deb",
|
||||
"linux-arm64-cli.rpm",
|
||||
"linux-arm64-cli.docker",
|
||||
"linux-arm64-cli.spk",
|
||||
"linux-arm64-agent.zip",
|
||||
"linux-arm64-agent.deb",
|
||||
"linux-arm64-agent.rpm",
|
||||
"linux-arm64-agent.docker",
|
||||
|
||||
"osx-x64-gui.dmg",
|
||||
"osx-x64-gui.pkg",
|
||||
"osx-x64-agent.pkg",
|
||||
"osx-x64-cli.pkg",
|
||||
"osx-arm64-gui.dmg",
|
||||
"osx-arm64-gui.pkg",
|
||||
"osx-arm64-agent.pkg",
|
||||
"osx-arm64-cli.pkg",
|
||||
}
|
||||
.Select(x => PackageTarget.ParsePackageId(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// The return code of the application; shared state
|
||||
/// </summary>
|
||||
public static int? ReturnCode = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the builder
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
static async Task<int> Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReturnCode = await new RootCommand("Build tool for Duplicati")
|
||||
{
|
||||
Build.Command.Create(),
|
||||
CreateKey.Command.Create(),
|
||||
}.InvokeAsync(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
ReturnCode = 1;
|
||||
}
|
||||
|
||||
return ReturnCode ?? 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
||||
export PATH="${HERE}/usr/bin/:${PATH}"
|
||||
|
||||
show_help() {
|
||||
echo "Usage: $(basename "${ARGV0:-$0}") [--<executable>] [args]"
|
||||
echo
|
||||
echo "Available executables:"
|
||||
%EXECUTABLE_LIST%
|
||||
echo
|
||||
echo "You can invoke the AppImage via a symlink name or with --<executable>."
|
||||
}
|
||||
|
||||
if [[ "${1}" == "--help" || "${1}" == "-h" ]]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
INVOCATION_NAME="$(basename "${ARGV0:-$0}")"
|
||||
case "$INVOCATION_NAME" in
|
||||
%INVOCATION_CASES%
|
||||
esac
|
||||
|
||||
case "$1" in
|
||||
%FLAG_CASES%
|
||||
esac
|
||||
|
||||
exec "${HERE}/usr/bin/%DEFAULT_EXECUTABLE%" "$@"
|
||||
@@ -0,0 +1,28 @@
|
||||
# We remove the hardcoded platform here so it builds for the host's native arch by default
|
||||
FROM ubuntu:latest
|
||||
|
||||
# Use the automatic Buildx variable
|
||||
ARG TARGETARCH
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
appstream \
|
||||
ca-certificates \
|
||||
curl \
|
||||
file \
|
||||
gnupg \
|
||||
libglib2.0-0 \
|
||||
squashfs-tools \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN case "${TARGETARCH}" in \
|
||||
"amd64") ARCH="x86_64" ;; \
|
||||
"arm64") ARCH="aarch64" ;; \
|
||||
*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;; \
|
||||
esac \
|
||||
&& curl -L -o /usr/local/bin/appimagetool \
|
||||
"https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${ARCH}.AppImage" \
|
||||
&& chmod +x /usr/local/bin/appimagetool
|
||||
|
||||
# Necessary for AppImages to run in many container environments
|
||||
ENV APPIMAGE_EXTRACT_AND_RUN=1
|
||||
@@ -1,5 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Categories=System;Archiving;FileTools;Filesystem;
|
||||
Categories=System;Archiving;FileTools;
|
||||
Type=Application
|
||||
Name=Duplicati
|
||||
GenericName= Backup tool
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.duplicati.app</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<name>Duplicati</name>
|
||||
<summary>Create and maintain local and remote backup copies of your data</summary>
|
||||
|
||||
<developer id="com.duplicati.app">
|
||||
<name>Duplicati Team</name>
|
||||
</developer>
|
||||
|
||||
<content_rating type="oars-1.1" />
|
||||
|
||||
<description>
|
||||
<p>
|
||||
Duplicati is a free backup client that securely stores encrypted,
|
||||
incremental, compressed backups on cloud storage services and remote file servers.
|
||||
</p>
|
||||
<p>
|
||||
It works with Amazon S3, Windows Live SkyDrive, Google Drive,
|
||||
OneDrive or WebDAV, SSH, FTP (and many more).
|
||||
</p>
|
||||
<p>
|
||||
Duplicati has built-in AES-256 encryption and backups can be signed using GNU Privacy Guard.
|
||||
A built-in scheduler and a Web-based interface makes it easy to maintain a regular backup.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">duplicati.desktop</launchable>
|
||||
|
||||
<url type="homepage">https://www.duplicati.com/</url>
|
||||
<url type="bugtracker">https://github.com/duplicati/duplicati/issues</url>
|
||||
<url type="vcs-browser">https://github.com/duplicati/duplicati</url>
|
||||
|
||||
<provides>
|
||||
<binary>duplicati</binary>
|
||||
</provides>
|
||||
|
||||
</component>
|
||||
Reference in New Issue
Block a user