Branch: safe-export → main
PR 3 of the review fixes plan (docs/review-fixes-plan.md). Part of 0.5.2; the version does not change in this PR.
Export refuses links (S1, export side).
export.Usable. The app uses the same check for the missing-file badges and missing count, so a pad with a linked source shows as missing and the export buttons are disabled, matching what export would do.Export checks names again (S1). Folder and the zip writer refuse a sample file name that is not a plain file name, using PR 1's rule (project.PlainFileName). This is a second line of defence: load and assign already ensure it.
New sample names are cleaned at assign (S1, assign).
poolFileName in project before the 001, 002 suffixes are added. A comment there gives the reason./, \, control characters, and the characters the device's FAT card forbids (: * ? " < > |) with _. A name that comes out empty, . or .. becomes sample.wav. The sample name follows from the cleaned file name.kick\one.wav, which Linux and macOS allow, becomes kick_one.wav, and its export loads again.Links you pick yourself still work. When you assign a WAV that is a link, MPCkit follows it. The pool file is named after the link, and the source is the file it points to. Picking the target itself later reuses the same pool entry.
Overwrite cannot lose the old export (M1, R1). With overwrite set, Folder used to delete the old .xpj and data folder before moving the new ones in, so a failed rename lost both. Now:
If any move fails, the moves already made are undone, newest first, and the old export is back as it was. If even putting it back fails, the temporary folder is kept, and the error says where the old export is. The Folder comment now says exactly this.
Missing destination. Exporting into a folder that no longer exists now says export folder does not exist: <dest> instead of a bare stat error.
Tests (M5). Coverage of internal/export is up from 74.2% to 87.8%. New tests:
TestSymlinkedSource: a kit whose data folder holds a link to a file outside. Folder and Zip export both fail, naming the file, and write nothing.TestLinkedDataFolder: a kit whose data folder is itself a link. Same result.TestBadPoolName: a pool name ../evil.wav is refused by Folder and Zip export, and nothing is written.TestOverwriteFailureKeepsOldExport: makes each of the 4 renames of an overwrite fail in turn. Every time, the previous export is byte-for-byte as it was.TestOverwriteUndoFails: the new .xpj cannot be moved in, and putting things back fails too. The error names the folder that holds the old export, and the export is complete there.TestOverwriteHalfPair: an overwrite where only the old .xpj exists.TestMissingDestination.TestExportErrors: a bad project name, no recorded source, a destination that is a file, a zip into a missing folder, and the "already exists" message.TestBackslashNameExportsAndLoads: assigns a WAV named kick\one.wav, gets kick_one.wav, exports, and loads the export.project and app: TestPoolFileName, TestAssignFollowsLink and TestLinkedFileIsMissing.Against the old code, every new export test fails.
Lstat checks only the last part of a path. A kit whose whole _[ProjectData] folder is a link to, say, ~/.ssh, with a pool entry named id_rsa, would still have exported that file. Usable now also requires the file's own folder to be a real folder.
.xpj should go. With the new overwrite, that directory is simply moved aside and replaced, so it no longer fails. The tests instead swap the package's rename (normally os.Rename) for one that fails on chosen calls. This reaches every step on every system.CON.wav, aux.wav) is still taken as it is. It can be written on Linux and macOS, but not on Windows. Say if you want it renamed too.TestBackslashNameExportsAndLoads also skips there, because Windows file names cannot hold \.On the server:
go test ./... passes, including the round-trip tests, unchanged.go test -cover ./internal/export shows 87.8%.go vet ./... (also with GOOS=windows) and staticcheck ./... are clean.govulncheck ./... reports only GO-2026-6859, which waits for Go 1.27.4 in PR 4.npm install, npm run check (0 errors, 0 warnings) and npm run build pass. The lockfile npm install wrote was not committed.wails3 build builds, and timeout 15 xvfb-run -a bin/mpckit starts and runs. The log holds only the usual headless GTK warnings.On the laptop (PowerShell, in the repo):
git fetch; git checkout safe-exportgo test -cover ./internal/export: ok, coverage 87.8%. That is the same as on the server: the tests that skip on Windows add no coverage of their own.go test ./...: all ok.wails3 dev: load testdata\device\DemoFile.xpj, choose Export to folder and pick an empty folder. It writes DemoFile.xpj and DemoFile_[ProjectData] with 6 WAVs..mpckit-export-… folder is left behind.DemoFile.xpj. It opens with the changed tune, and no pad shows as missing..xpj and the data folder with 6 WAVs.