feat(release): pack the dev channel as its own app so it installs beside stable #115
No files matched your search
@@ -153,7 +153,10 @@ jobs:
|
||||
with:
|
||||
solution: gclo.slnx
|
||||
app-project: gclo/gclo.csproj
|
||||
app-id: gclo
|
||||
# Prereleases pack as their own Velopack app (gclo-dev): separate install
|
||||
# folder, Start menu entry, update feed and data root, so a dev build can be
|
||||
# installed beside the stable one (#114). Asset names follow the app id.
|
||||
app-id: ${{ contains(github.ref_name, '-') && 'gclo-dev' || 'gclo' }}
|
||||
main-exe: gclo.exe
|
||||
icon: gclo/Assets/Brand/gclo.ico
|
||||
platform: x64
|
||||
@@ -193,6 +196,7 @@ jobs:
|
||||
TAG: ${{ github.ref_name }}
|
||||
K22_VERSION: ${{ needs.release.outputs.version }}
|
||||
K22_CHANNEL: ${{ needs.release.outputs.prerelease == 'true' && 'dev' || 'stable' }}
|
||||
K22_APP_ID: ${{ needs.release.outputs.prerelease == 'true' && 'gclo-dev' || 'gclo' }}
|
||||
EXPECTED_SUBJECT: ${{ vars.TRUSTED_SIGNING_SUBJECT }}
|
||||
steps:
|
||||
- name: Download the published assets
|
||||
@@ -202,9 +206,9 @@ jobs:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force assets | Out-Null
|
||||
gh release download $env:TAG --dir assets --pattern "gclo-$env:K22_CHANNEL-Setup.exe" --pattern "gclo-$env:K22_CHANNEL-Portable.zip" --pattern "gclo-cli-win-x64-$env:K22_VERSION.zip"
|
||||
gh release download $env:TAG --dir assets --pattern "$env:K22_APP_ID-$env:K22_CHANNEL-Setup.exe" --pattern "$env:K22_APP_ID-$env:K22_CHANNEL-Portable.zip" --pattern "gclo-cli-win-x64-$env:K22_VERSION.zip"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
Expand-Archive assets/gclo-$env:K22_CHANNEL-Portable.zip -DestinationPath assets/portable
|
||||
Expand-Archive assets/$env:K22_APP_ID-$env:K22_CHANNEL-Portable.zip -DestinationPath assets/portable
|
||||
Expand-Archive assets/gclo-cli-win-x64-$env:K22_VERSION.zip -DestinationPath assets/cli
|
||||
|
||||
- name: signtool verify /pa /all on every shipped executable
|
||||
@@ -217,7 +221,7 @@ jobs:
|
||||
}
|
||||
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter signtool.exe |
|
||||
Where-Object { $_.FullName -match '\\x64\\' } | Sort-Object FullName -Descending | Select-Object -First 1
|
||||
$files = @(Get-Item assets/gclo-$env:K22_CHANNEL-Setup.exe) + @(Get-ChildItem assets/portable, assets/cli -Recurse -Include *.exe, *.dll |
|
||||
$files = @(Get-Item assets/$env:K22_APP_ID-$env:K22_CHANNEL-Setup.exe) + @(Get-ChildItem assets/portable, assets/cli -Recurse -Include *.exe, *.dll |
|
||||
Where-Object { $_.Name -like 'gclo*' -or $_.Name -in 'Update.exe' })
|
||||
$failed = 0
|
||||
foreach ($f in $files)
|
||||
|
||||
@@ -23,6 +23,21 @@ Any prerelease identifier works (`-alpha`, `-beta.2`, `-rc.1`, ...) — the rule
|
||||
is simply: **a `-` in the version means prerelease, which means the `dev`
|
||||
channel**.
|
||||
|
||||
### The dev edition is a separate app
|
||||
|
||||
A prerelease is packed as its own Velopack app, id **`gclo-dev`**, while stable
|
||||
releases use **`gclo`**. The two install side by side: separate install folder
|
||||
(`%LOCALAPPDATA%\gclo-dev` vs `%LOCALAPPDATA%\gclo`), Start menu entry, update
|
||||
feed and data root (`%LOCALAPPDATA%\KofTwentyTwo\gclo-dev` vs `…\gclo`), and
|
||||
the dev edition's window title reads "gclo (dev)". A machine can therefore keep
|
||||
the stable app for real work and a dev build for trying the next release; each
|
||||
only ever updates within its own channel. The dev edition's assets follow its
|
||||
app id: `gclo-dev-dev-Setup.exe`, `gclo-dev-dev-Portable.zip`,
|
||||
`gclo-dev-1.2.3-beta.1-dev-full.nupkg`, `releases.dev.json`. The CLI zip is the
|
||||
same for both (`gclo-cli-win-x64-<version>.zip`). Dev installs made before this
|
||||
split (app id `gclo` on the dev channel, up to 1.0.2-beta.3) cannot update into
|
||||
`gclo-dev`: uninstall them, or install the stable app over them.
|
||||
|
||||
### How self-update channels map
|
||||
|
||||
The desktop app updates itself with [Velopack](https://velopack.io). Each
|
||||
|
||||
@@ -30,6 +30,21 @@ public sealed class GcloPathsTests
|
||||
|
||||
|
||||
|
||||
/// <summary>The dev edition keeps its own data root beside the stable one (#114).</summary>
|
||||
[Fact]
|
||||
public void DataFolderNameFor_DevEdition_UsesItsOwnFolder()
|
||||
{
|
||||
// Edition itself is process-wide state set once by the app's entry point; the
|
||||
// tests exercise the pure mapping so they cannot race the vault's scoping.
|
||||
Assert.Equal("gclo", GcloPaths.DataFolderNameFor(""));
|
||||
Assert.Equal("gclo-dev", GcloPaths.DataFolderNameFor("dev"));
|
||||
Assert.Throws<ArgumentNullException>(() => GcloPaths.DataFolderNameFor(null!));
|
||||
Assert.Equal("", GcloPaths.Edition);
|
||||
Assert.Equal("gclo", GcloPaths.DataFolderName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>The default root is never the install root nor anything under it.</summary>
|
||||
[Fact]
|
||||
public void DefaultDataRoot_IsNotTheVelopackInstallRoot()
|
||||
|
||||
@@ -34,11 +34,36 @@ public static class GcloPaths
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// %LOCALAPPDATA%\KofTwentyTwo\gclo: the root used when GCLO_DATA_DIR is not
|
||||
/// set. Outside the Velopack install root on purpose (see the type remarks).
|
||||
/// The installed edition: "" for the stable app (Velopack app id <c>gclo</c>) and
|
||||
/// "dev" for the dev-channel app (<c>gclo-dev</c>), which installs beside the
|
||||
/// stable one and keeps its own data root so both can run on one machine (#114).
|
||||
/// Set once by the app's entry point before anything reads <see cref="DataRoot"/>;
|
||||
/// local and CLI runs leave it empty.
|
||||
/// </summary>
|
||||
public static string Edition { get; set; } = "";
|
||||
|
||||
|
||||
|
||||
/// <summary>The folder name under %LOCALAPPDATA%\KofTwentyTwo: "gclo", or "gclo-dev" for the dev edition.</summary>
|
||||
public static string DataFolderName => DataFolderNameFor(Edition);
|
||||
|
||||
|
||||
|
||||
/// <summary>"gclo" for the stable edition (empty <paramref name="edition"/>), "gclo-<edition>" otherwise.</summary>
|
||||
public static string DataFolderNameFor(string edition)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(edition);
|
||||
return edition.Length == 0 ? "gclo" : $"gclo-{edition}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// %LOCALAPPDATA%\KofTwentyTwo\gclo (or gclo-dev): the root used when GCLO_DATA_DIR
|
||||
/// is not set. Outside the Velopack install root on purpose (see the type remarks).
|
||||
/// </summary>
|
||||
public static string DefaultDataRoot =>
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"KofTwentyTwo", "gclo");
|
||||
"KofTwentyTwo", DataFolderName);
|
||||
}
|
||||
@@ -121,7 +121,10 @@ public sealed partial class MainWindow : Window
|
||||
InitializeComponent();
|
||||
ApplyBackdrop();
|
||||
|
||||
Title = "gclo — Git Clone Large Organizations";
|
||||
// The dev edition installs beside the stable app, so the title says which one this is.
|
||||
Title = GcloPaths.Edition.Length == 0
|
||||
? "gclo — Git Clone Large Organizations"
|
||||
: $"gclo ({GcloPaths.Edition}) — Git Clone Large Organizations";
|
||||
|
||||
// Quick Sync is declared in XAML; its Guid tag and the account items are
|
||||
// runtime data, so they are filled in here.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using gclo.ViewModels;
|
||||
using Velopack;
|
||||
|
||||
|
||||
@@ -25,6 +26,14 @@ public static class Program
|
||||
// Must be first: handles Velopack install/update/uninstall hooks and may exit.
|
||||
VelopackApp.Build().Run();
|
||||
|
||||
// The dev channel is packed as its own app (id gclo-dev) so it installs beside
|
||||
// the stable one; it keeps its own data root and says so in the title (#114).
|
||||
// Local and CLI runs have no Velopack app id and stay on the stable paths.
|
||||
if(string.Equals(Velopack.Locators.VelopackLocator.Current?.AppId, "gclo-dev", StringComparison.Ordinal))
|
||||
{
|
||||
GcloPaths.Edition = "dev";
|
||||
}
|
||||
|
||||
// The remainder mirrors the XAML-generated Main (obj\...\App.g.i.cs) verbatim.
|
||||
global::WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||
global::Microsoft.UI.Xaml.Application.Start((p) =>
|
||||
|
||||
Reference in new issue
Block a user