diff --git a/ReleaseBuilder/Build/Command.Upload.cs b/ReleaseBuilder/Build/Command.Upload.cs
index d75cc2af8..c311f5bbb 100644
--- a/ReleaseBuilder/Build/Command.Upload.cs
+++ b/ReleaseBuilder/Build/Command.Upload.cs
@@ -106,8 +106,9 @@ public static partial class Command
///
/// The files to upload
/// The runtime configuration
+ /// The release channels to propagate to
/// An awaitable task
- public static async Task UploadToS3(IEnumerable files, RuntimeConfig rtcfg)
+ public static async Task UploadToS3(IEnumerable files, RuntimeConfig rtcfg, IEnumerable propagateTo)
{
var totalSize = files.Sum(f => new FileInfo(f.Path).Length);
Console.WriteLine($"Uploading {files.Count()} files ({Duplicati.Library.Utility.Utility.FormatSizeString(totalSize)}) to S3...");
@@ -141,6 +142,22 @@ public static partial class Command
size += filesize;
Console.WriteLine($"{size / (double)totalSize * 100:F1}% - Uploaded {file.Name} ({Duplicati.Library.Utility.Utility.FormatSizeString(filesize)})");
}
+
+ foreach (var channel in propagateTo)
+ {
+ if (channel == rtcfg.ReleaseInfo.Channel)
+ continue;
+
+ var target = $"{channel.ToString().ToLowerInvariant()}/latest-v2.manifest";
+ var source = $"{rtcfg.ReleaseInfo.Channel.ToString().ToLowerInvariant()}/latest-v2.manifest";
+ await Duplicati.Library.Utility.RetryHelper.Retry(() =>
+ client.CopyObjectAsync(
+ Program.Configuration.ConfigFiles.AwsUploadBucket,
+ source,
+ Program.Configuration.ConfigFiles.AwsUploadBucket,
+ target
+ ), 3, TimeSpan.FromSeconds(1), CancellationToken.None);
+ }
}
///
@@ -214,7 +231,7 @@ public static partial class Command
///
/// The runtime configuration
/// An awaitable task
- public static async Task ReloadUpdateServer(RuntimeConfig rtcfg)
+ public static async Task ReloadUpdateServer(RuntimeConfig rtcfg, IEnumerable propagateTo)
{
using var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, "https://updates.duplicati.com/reload");
@@ -225,7 +242,7 @@ public static partial class Command
$"{rtcfg.ReleaseInfo.Channel.ToString().ToLowerInvariant()}/latest-v2.json",
$"{rtcfg.ReleaseInfo.Channel.ToString().ToLowerInvariant()}/latest-v2.js",
$"{rtcfg.ReleaseInfo.Channel.ToString().ToLowerInvariant()}/latest-v2.manifest",
- });
+ }.Concat(propagateTo.Select(c => $"{c.ToString().ToLowerInvariant()}/latest-v2.manifest")));
var response = await client.SendAsync(req);
response.EnsureSuccessStatusCode();
diff --git a/ReleaseBuilder/Build/Command.cs b/ReleaseBuilder/Build/Command.cs
index 84c25fb60..d0de3a4b9 100644
--- a/ReleaseBuilder/Build/Command.cs
+++ b/ReleaseBuilder/Build/Command.cs
@@ -299,6 +299,12 @@ public static partial class Command
getDefaultValue: () => false
);
+ var propagateReleaseOption = new Option(
+ name: "--propagate-release",
+ description: "Propagate the release to the next channel",
+ getDefaultValue: () => false
+ );
+
var command = new System.CommandLine.Command("build", "Builds the packages for a release") {
gitStashPushOption,
releaseChannelArgument,
@@ -322,7 +328,8 @@ public static partial class Command
disableUpdateServerReloadOption,
disableDiscordAnnounceOption,
useHostedBuildsOption,
- resumeFromUploadOption
+ resumeFromUploadOption,
+ propagateReleaseOption
};
command.Handler = CommandHandler.Create(DoBuild);
@@ -355,6 +362,7 @@ public static partial class Command
/// If forum posting should be disabled
/// If hosted builds should be used
/// If the process should resume from the upload step
+ /// If the release should be propagated to the next channel
record CommandInput(
PackageTarget[] Targets,
DirectoryInfo BuildPath,
@@ -378,7 +386,8 @@ public static partial class Command
bool DisableUpdateServerReload,
bool DisableDiscordAnnounce,
bool UseHostedBuilds,
- bool ResumeFromUpload
+ bool ResumeFromUpload,
+ bool PropagateRelease
);
static async Task DoBuild(CommandInput input)
@@ -619,6 +628,13 @@ public static partial class Command
if (input.GitStashPush && !input.ResumeFromUpload)
await GitPush.TagAndPush(baseDir, releaseInfo);
+ // Propagate the release to the next channel, if selected
+ var nextChannels = new[] { ReleaseChannel.Experimental, ReleaseChannel.Beta, ReleaseChannel.Stable }
+ .Where(x => x > releaseInfo.Channel)
+ .Where(x => rtcfg.ReleaseInfo.Channel != ReleaseChannel.Canary)
+ .Where(x => input.PropagateRelease)
+ .ToArray();
+
if (rtcfg.UseS3Upload || rtcfg.UseGithubUpload)
{
Console.WriteLine("Build completed, uploading packages ...");
@@ -636,7 +652,7 @@ public static partial class Command
.Append(new Upload.UploadFile(packageJson, Path.GetFileName(packageJson)))
.Append(new Upload.UploadFile(packageJson, $"latest-v2-{releaseInfo.Version}.json"));
- await Upload.UploadToS3(uploads, rtcfg);
+ await Upload.UploadToS3(uploads, rtcfg, nextChannels);
}
if (rtcfg.UseGithubUpload)
@@ -657,7 +673,7 @@ public static partial class Command
if (rtcfg.UseUpdateServerReload)
{
Console.WriteLine("Release completed, reloading update server ...");
- await Upload.ReloadUpdateServer(rtcfg);
+ await Upload.ReloadUpdateServer(rtcfg, nextChannels);
}
if (rtcfg.UseForumPosting)