Issue #139: portable.txt next to the exe keeps the data directory beside it

This commit is contained in:
MarekSpodBiedry committed 2026-10-08 18:58:41 +02:00
1 parent 5231852443
commit 502c9a33a7
4 files changed
+35 -3

No files matched your search

+14 -1
View File
@@ -470,7 +470,17 @@ impl Preferences {
}
}
/// The per-user data directory (`FILMCRAFT_DATA_DIR` overrides):
/// A file with this name next to the executable makes the install portable: its data directory is
/// `<exe dir>/data` instead of the per-user one (the Windows portable zip ships it).
pub const PORTABLE_MARKER: &str = "portable.txt";
/// `<exe_dir>/data` when `exe_dir` holds [`PORTABLE_MARKER`], else `None`.
pub fn portable_data_dir(exe_dir: &Path) -> Option<PathBuf> {
exe_dir.join(PORTABLE_MARKER).is_file().then(|| exe_dir.join("data"))
}
/// The data directory. `FILMCRAFT_DATA_DIR` overrides; a portable install ([`PORTABLE_MARKER`] next
/// to the executable) uses `<exe dir>/data`; otherwise the per-user directory:
/// macOS `~/Library/Application Support/FilmCraft`, Windows `%APPDATA%\FilmCraft`,
/// elsewhere `$XDG_DATA_HOME/filmcraft` or `~/.local/share/filmcraft`.
pub fn default_data_dir() -> Option<PathBuf> {
@@ -478,6 +488,9 @@ pub fn default_data_dir() -> Option<PathBuf> {
if let Some(d) = env("FILMCRAFT_DATA_DIR") {
return Some(d);
}
if let Some(d) = std::env::current_exe().ok().and_then(|exe| exe.parent().and_then(portable_data_dir)) {
return Some(d);
}
if cfg!(target_os = "macos") {
return env("HOME").map(|h| h.join("Library/Application Support/FilmCraft"));
}
+13
View File
@@ -176,6 +176,19 @@ fn auto_save_ring_rotates_and_can_save_the_project() {
let _ = std::fs::remove_dir_all(&d);
}
#[test]
fn portable_marker_keeps_data_next_to_the_executable() {
// #139: a portable install must not write to %APPDATA%.
let d = temp_dir("portable");
assert_eq!(autosave::portable_data_dir(&d), None);
std::fs::create_dir_all(d.join(autosave::PORTABLE_MARKER)).unwrap();
assert_eq!(autosave::portable_data_dir(&d), None, "a directory is not the marker");
std::fs::remove_dir_all(d.join(autosave::PORTABLE_MARKER)).unwrap();
std::fs::write(d.join(autosave::PORTABLE_MARKER), "").unwrap();
assert_eq!(autosave::portable_data_dir(&d), Some(d.join("data")));
let _ = std::fs::remove_dir_all(&d);
}
#[test]
fn journal_costs_the_ui_thread_only_a_handoff() {
// Per change the UI thread pays an Arc clone and a channel send; encoding happens on the worker.
+2 -1
View File
@@ -159,7 +159,8 @@ Desktop flags: `--recover` recovers the newest candidate without asking. `--no-r
without asking; the changes stay available through File ▸ Recover Unsaved Changes…. `--data-dir
<dir>` (or `FILMCRAFT_DATA_DIR`) moves the data directory. The default is `~/Library/Application
Support/FilmCraft` on macOS, `%APPDATA%\FilmCraft` on Windows, and `$XDG_DATA_HOME/filmcraft` or
`~/.local/share/filmcraft` elsewhere.
`~/.local/share/filmcraft` elsewhere. A `portable.txt` file next to the executable (the Windows
portable zip ships one) makes it `data` next to the executable instead.
### Cost
+6 -1
View File
@@ -5,7 +5,7 @@
.DESCRIPTION
Produces, in $env:DIST (default: dist/release):
filmcraft-<version>-windows-<arch>.msi per-machine installer (WiX v5)
filmcraft-<version>-windows-<arch>-portable.zip filmcraft.exe + filmcraft-cli.exe
filmcraft-<version>-windows-<arch>-portable.zip filmcraft.exe + filmcraft-cli.exe + portable.txt
The binaries link the C runtime statically (+crt-static), so neither the MSI nor the portable
zip needs the Visual C++ redistributable. Signing is delegated to sign.ps1 (skipped with a
@@ -106,6 +106,11 @@ $Portable = Join-Path $TargetDir "windows-package\filmcraft-$Version-windows-$Ar
Remove-Item -Recurse -Force $Portable -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $Portable | Out-Null
Copy-Item (Join-Path $Stage '*.exe') $Portable
# The marker that makes FilmCraft keep its data in .\data next to the exe instead of %APPDATA% (#139).
Set-Content -Encoding ascii -Path (Join-Path $Portable 'portable.txt') -Value @(
'FilmCraft portable: preferences, presets, logs and crash recovery are kept in the data folder',
'next to filmcraft.exe. Delete this file to use %APPDATA%\FilmCraft instead.'
)
foreach ($f in 'README.md', 'LICENSE', 'LICENSE-MIT', 'LICENSE-APACHE') {
$p = Join-Path $Root $f
if (Test-Path $p) { Copy-Item $p $Portable }