fix: keep per-user data out of the Velopack install root #94
No files matched your search
@@ -110,7 +110,7 @@ On launch, gclo shows a brief branded splash, then the **Quick Sync** connect ca
|
||||
|
||||
**Settings** (File > Settings…) let you set a default GitHub token (stored in the Windows Credential Manager), the default target folder (with a folder picker), the default parallelism, and the theme (System, Light, or Dark); an Advanced section toggles the startup splash and its duration.
|
||||
|
||||
**Activity log:** View > Activity log… shows the tail of today's log and links to the logs folder. Logs live under `%LOCALAPPDATA%\gclo\logs`, one file per day; they record run parameters and per-repo failures and never contain your token (see [SECURITY.md](SECURITY.md)).
|
||||
**Activity log:** View > Activity log… shows the tail of today's log and links to the logs folder. Logs live under `%LOCALAPPDATA%\KofTwentyTwo\gclo\logs`, one file per day; they record run parameters and per-repo failures and never contain your token (see [SECURITY.md](SECURITY.md)).
|
||||
|
||||
**Updates:** Help > Check for updates… downloads and applies the latest release for your channel. Self-update is only available in installed builds (not the portable zip or a local debug build).
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ GitHub.
|
||||
| GitHub REST API | Lists organizations and repositories | Untrusted input (responses are parsed), authenticated by the PAT |
|
||||
| Git remotes (`github.com`) | Clone and fetch over HTTPS via libgit2 | Untrusted input (repository content, including file paths) |
|
||||
| Windows Credential Manager | Stores saved-account and default tokens | Trusted store; boundary is the Windows user account |
|
||||
| Local data directory | `%LOCALAPPDATA%\gclo` (or `GCLO_DATA_DIR`): settings, accounts, logs | Trusted; same user |
|
||||
| Local data directory | `%LOCALAPPDATA%\KofTwentyTwo\gclo` (or `GCLO_DATA_DIR`): settings, accounts, logs. Deliberately outside the Velopack install root `%LOCALAPPDATA%\gclo`, which Setup.exe deletes on reinstall (#91) | Trusted; same user |
|
||||
| Target folder | Where repositories are written; may already contain repositories | Partly untrusted (pre-existing content, `.git` config) |
|
||||
| WSL default distribution | Runs `git clone` for the "Clone in WSL" recovery | Semi-trusted (user-installed Linux environment) |
|
||||
| Update feed | GitHub Releases of this repository, `releases.<channel>.json` | Untrusted until verified (HTTPS, package hashes, attestations) |
|
||||
@@ -112,3 +112,4 @@ flowchart LR
|
||||
| 2026-10-09 | v1.0.1 | James Maes (@KofTwentyTwo) | Initial model, covering the path-recovery, WSL clone, vault scoping, package-manager, and SBOM changes of 1.0.1 |
|
||||
| 2026-10-09 | v1.0.1 | James Maes (@KofTwentyTwo) | T11 updated: releases build in the shared standards workflow; publishing tokens moved to the `release` environment (#76) |
|
||||
| 2026-10-10 | v1.0.2 | James Maes (@KofTwentyTwo) | T11: the packaging PR is opened with a short-lived release GitHub App token (contents + pull requests on this repository, no bypass) instead of a long-lived PAT; the commit is GitHub-signed (#85) |
|
||||
| 2026-10-10 | v1.0.2 | James Maes (@KofTwentyTwo) | Local data directory moved to `%LOCALAPPDATA%\KofTwentyTwo\gclo`: the Velopack install root is deleted by Setup.exe, which destroyed accounts.json and settings.json (#91) |
|
||||
@@ -128,18 +128,31 @@ public sealed class ActivityLogTests : IDisposable
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultDirectory_IsLocalAppDataGcloLogs()
|
||||
public void DefaultDirectory_IsLocalAppDataKofTwentyTwoGcloLogs()
|
||||
{
|
||||
var log = new FileActivityLog();
|
||||
|
||||
string expected = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"gclo", "logs");
|
||||
"KofTwentyTwo", "gclo", "logs");
|
||||
Assert.Equal(expected, log.LogDirectory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultDirectory_IsNotInsideTheVelopackInstallRoot()
|
||||
{
|
||||
// Setup.exe deletes %LOCALAPPDATA%\gclo (the install root) on reinstall (#91).
|
||||
string installRoot = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "gclo");
|
||||
|
||||
Assert.False(
|
||||
new FileActivityLog().LogDirectory.StartsWith(installRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Directory_IsCreatedLazily_OnFirstWriteNotInConstructor()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2026 James Maes (KofTwentyTwo)
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
using gclo.ViewModels;
|
||||
|
||||
|
||||
namespace gclo.Engine.Tests;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Where gclo keeps its per-user data. The default must stay outside the Velopack
|
||||
/// install root (%LOCALAPPDATA%\gclo): Setup.exe uninstalls an existing install
|
||||
/// first and deletes that folder, which destroyed accounts.json and settings.json
|
||||
/// in 1.0.2-beta.1 (#91).
|
||||
/// </summary>
|
||||
public sealed class GcloPathsTests
|
||||
{
|
||||
/// <summary>The default root is %LOCALAPPDATA%\KofTwentyTwo\gclo.</summary>
|
||||
[Fact]
|
||||
public void DefaultDataRoot_IsLocalAppDataKofTwentyTwoGclo()
|
||||
{
|
||||
string expected = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"KofTwentyTwo", "gclo");
|
||||
|
||||
Assert.Equal(expected, GcloPaths.DefaultDataRoot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>The default root is never the install root nor anything under it.</summary>
|
||||
[Fact]
|
||||
public void DefaultDataRoot_IsNotTheVelopackInstallRoot()
|
||||
{
|
||||
string installRoot = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "gclo");
|
||||
|
||||
Assert.NotEqual(installRoot, GcloPaths.DefaultDataRoot, StringComparer.OrdinalIgnoreCase);
|
||||
Assert.False(
|
||||
GcloPaths.DefaultDataRoot.StartsWith(installRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,16 @@ public sealed class FileActivityLog : IActivityLog
|
||||
|
||||
/// <summary>Creates a log that writes under <paramref name="directory"/>.</summary>
|
||||
/// <param name="directory">
|
||||
/// Folder for the log files; defaults to %LOCALAPPDATA%\gclo\logs when null or blank.
|
||||
/// Folder for the log files; defaults to %LOCALAPPDATA%\KofTwentyTwo\gclo\logs
|
||||
/// when null or blank (the same data root as the app; never the Velopack
|
||||
/// install root %LOCALAPPDATA%\gclo, which Setup.exe deletes, #91).
|
||||
/// </param>
|
||||
public FileActivityLog(string? directory = null)
|
||||
{
|
||||
LogDirectory = string.IsNullOrWhiteSpace(directory)
|
||||
? Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"gclo", "logs")
|
||||
"KofTwentyTwo", "gclo", "logs")
|
||||
: directory;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace gclo.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Persists account profiles as JSON at accounts.json under
|
||||
/// <see cref="GcloPaths.DataRoot"/> (default %LOCALAPPDATA%\gclo) and their
|
||||
/// <see cref="GcloPaths.DataRoot"/> (default %LOCALAPPDATA%\KofTwentyTwo\gclo) and their
|
||||
/// tokens in an <see cref="ITokenVault"/>; the file holds metadata only and never a
|
||||
/// token. Unlike <see cref="AppSettings"/>, accounts are primary user data, so write
|
||||
/// failures in <see cref="Save"/>, <see cref="Delete"/>, and
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace gclo.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// User preferences persisted as JSON at settings.json under
|
||||
/// <see cref="GcloPaths.DataRoot"/> (default %LOCALAPPDATA%\gclo, overridable via
|
||||
/// <see cref="GcloPaths.DataRoot"/> (default %LOCALAPPDATA%\KofTwentyTwo\gclo, overridable via
|
||||
/// GCLO_DATA_DIR). Uses Environment.GetFolderPath (not Windows.Storage.ApplicationData)
|
||||
/// so the same path works whether the app runs packaged or unpackaged.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,17 +8,23 @@ namespace gclo.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// The single seam for where gclo keeps its per-user data (settings, accounts,
|
||||
/// logs). Defaults to %LOCALAPPDATA%\gclo; the GCLO_DATA_DIR environment variable
|
||||
/// overrides it so UI tests (and portable setups) can point the whole app at an
|
||||
/// isolated directory without touching the real profile. Components that accept
|
||||
/// an explicit directory (test seams) are unaffected — this only feeds defaults.
|
||||
/// logs). Defaults to %LOCALAPPDATA%\KofTwentyTwo\gclo; the GCLO_DATA_DIR
|
||||
/// environment variable overrides it so UI tests (and portable setups) can point
|
||||
/// the whole app at an isolated directory without touching the real profile.
|
||||
/// Components that accept an explicit directory (test seams) are unaffected —
|
||||
/// this only feeds defaults.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The data root must never be the Velopack install root (%LOCALAPPDATA%\gclo):
|
||||
/// Setup.exe uninstalls an existing install first and deletes that whole folder,
|
||||
/// which destroyed the accounts and settings stored there (#91).
|
||||
/// </remarks>
|
||||
public static class GcloPaths
|
||||
{
|
||||
/// <summary>
|
||||
/// Root directory for gclo's per-user data: GCLO_DATA_DIR when set and
|
||||
/// non-empty, otherwise %LOCALAPPDATA%\gclo. Read on every access so a
|
||||
/// value set before launch always wins over any cached default.
|
||||
/// non-empty, otherwise <see cref="DefaultDataRoot"/>. Read on every access so
|
||||
/// a value set before launch always wins over any cached default.
|
||||
/// </summary>
|
||||
public static string DataRoot =>
|
||||
Environment.GetEnvironmentVariable("GCLO_DATA_DIR") is { Length: > 0 } dir
|
||||
@@ -27,7 +33,12 @@ public static class GcloPaths
|
||||
|
||||
|
||||
|
||||
/// <summary>%LOCALAPPDATA%\gclo: the root used when GCLO_DATA_DIR is not set.</summary>
|
||||
/// <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).
|
||||
/// </summary>
|
||||
public static string DefaultDataRoot =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "gclo");
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"KofTwentyTwo", "gclo");
|
||||
}
|
||||
Reference in new issue
Block a user