Repository navigation
building and packaging
Every release ships six zip packages - one per supported runtime identifier:
release_<version>_<rid>.zip (e.g. release_3.1.1_win-x64.zip)
<rid> = win-x64 | win-arm64 | linux-x64 | linux-arm64 | osx-x64 | osx-arm64
| Package | Contents |
|---|---|
release_<version>_win-x64.zip |
SimpleZipDrive.exe, 7za.exe, 7zip-license.txt, winfsp-msil.dll, Avalonia native libraries (libSkiaSharp.dll, libHarfBuzzSharp.dll, av_libglesv2.dll), ReadMe.md, LICENSE.txt, WhatsNew.md
|
release_<version>_win-arm64.zip |
same layout (ARM64 7za.exe) |
release_<version>_linux-x64.zip / release_<version>_linux-arm64.zip
|
SimpleZipDrive (apphost), 7zzs, 7zip-license.txt, libSkiaSharp.so, libHarfBuzzSharp.so, docs |
release_<version>_osx-x64.zip / release_<version>_osx-arm64.zip
|
SimpleZipDrive, universal 7zz, 7zip-license.txt, libSkiaSharp.dylib, libHarfBuzzSharp.dylib, docs |
All are framework-dependent single-file executables: the .NET 10 runtime and the filesystem driver (WinFsp/Dokan on Windows, libfuse3/macFUSE on Linux/macOS) are the only prerequisites.
Three workflows live in .github/workflows, and GitHub's built-in Pages deployment publishes the
documentation site from docs/:
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
Every push/PR to master
|
Builds and tests the solution on Windows and compiles the app on Ubuntu and macOS |
release.yml |
workflow_dispatch (version input) or a release_* tag push |
Verifies the csproj version matches, builds + tests on Windows, publishes and packages the six bundles on Windows/Linux/macOS runners, uploads them as release-bundles-* artifacts, then waits for approval in the protected release environment before creating/updating the GitHub release |
wiki-sync.yml |
Push to master touching docs/** (or manually) |
Mirrors docs/*.md into the repository wiki (index.md → Home.md); the wiki side menu comes from docs/_Sidebar.md
|
Built-in Pages build (pages-build-deployment) |
Push to master touching docs/**
|
Builds docs/ with Jekyll (just-the-docs theme) and deploys https://purelogiccode.github.io/SimpleZipDrive/; the site side menu comes from the parent/nav_order front matter in each page |
- Run Release from the Actions tab (or push a
release_x.y.ztag). - When the Build bundles jobs finish, download the release-bundles-* artifacts from the run summary - the six
release_<version>_<rid>.zipfiles. The run summary lists sizes and SHA256 checksums, and nothing is public yet. - Inspect the zips and smoke-test at least one packaged executable per OS.
- Approve the waiting Publish release job in the
releaseenvironment (Settings → Environments →release→ required reviewer). The workflow then creates the GitHub release with the six bundles attached and the matchingWhatsNew.mdsection as the release notes.
If the bundles are wrong, do not approve: cancel the run, fix, and re-run.
First-time setup: the
releaseenvironment needs at least one required reviewer, and the wiki sync needs aWIKI_TOKENrepository secret (a PAT with repository access -GITHUB_TOKENcannot push to the wiki repository). Both are already configured for this repository.
scripts/package-release.ps1 performs the same publish/package steps locally:
# All six targets (any host can build any runtime identifier)
.\scripts\package-release.ps1 -Version 3.1.1
# Only the targets for one OS
.\scripts\package-release.ps1 -Version 3.1.1 -RuntimeIdentifiers win-x64,win-arm64It runs the test suite first (pass -SkipTests to skip), publishes the requested runtime identifiers, and writes the bundles into SimpleZipDrive\bin\Release next to the historical releases. On Linux/macOS the script uses the zip CLI so the apphost and the 7-Zip binary keep their executable bit; on Windows it writes the zip through System.IO.Compression, records the Unix executable bit (external attributes) explicitly for Linux/macOS bundles, and patches the version-made-by host byte of every real central-directory header to Unix (located through the end-of-central-directory record, never by scanning for the signature - compressed data can contain the same bytes) - so all six bundles can be produced from any host (CI still builds each OS on its matching runner).
SimpleZipDrive\bin\Releaseis append-only - never delete files in it. It holds locally produced bundles (and may hold historical ones), and they are intentionally not tracked by git (.gitignoreignoresbin/), so a deleted bundle is gone permanently. Never runRemove-Item -Recurse,git clean,rm -rfor any "clear output directory" step against that path. Packaging may only overwrite the exact bundle file being regenerated (release_<version>_<rid>.zip); every other file must stay untouched.scripts/package-release.ps1follows this rule - keep it that way when editing the script.
Important: the
.csprojcontains<SelfContained>true</SelfContained>, but releases are built framework-dependent - the publish command must override it with--self-contained false. Publishing without the override produces huge self-contained bundles that also break the packaging assumptions documented below.
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r win-x64 --self-contained false -o out\win-x64
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r win-arm64 --self-contained false -o out\win-arm64
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r linux-x64 --self-contained false -o out\linux-x64
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r linux-arm64 --self-contained false -o out\linux-arm64
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r osx-x64 --self-contained false -o out\osx-x64
dotnet publish SimpleZipDrive\SimpleZipDrive.csproj -c Release -r osx-arm64 --self-contained false -o out\osx-arm64When packaging by hand, include the single-file executable, every file in the publish root except PDBs, and ReadMe.md/LICENSE.txt/WhatsNew.md. The support files in the publish root (the platform 7-Zip binary and its 7zip-license.txt, Avalonia's Skia/HarfBuzz/ANGLE libraries, and on Windows winfsp-msil.dll) must ship loose beside the executable.
Three constraints make the file layout non-negotiable:
-
winfsp-msil.dllmust stay a real file beside the exe. Its static initializer (Fsp.Interop.Api.CheckVersion) callsFileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);Assembly.Locationis an empty string inside a single-file bundle, soPath.GetFullPath("")throws and every WinFsp mount dies with "The path is empty (Parameter 'path')" before the driver is ever contacted. The csproj contains a target that runs before the bundler computes its file list:<Target Name="KeepWinFspInteropOutOfBundle" BeforeTargets="_ComputeFilesToBundle"> <ItemGroup> <ResolvedFileToPublish Update="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.Filename)%(ResolvedFileToPublish.Extension)' == 'winfsp-msil.dll'" ExcludeFromSingleFile="true" /> </ItemGroup> </Target>
Timing matters:
AfterTargets="ComputeFilesToPublish"/BeforeTargets="BundleFiles"do not work - the SDK splits bundled/non-bundled files in_ComputeFilesToBundle. -
The 7-Zip fallback binary must stay a real file beside the exe on every platform.
SevenZipFallbackprobesAppContext.BaseDirectoryfor7za.exe(Windows),7zzs(Linux) or7zz(macOS); the csproj copies exactly the file matching the publishRuntimeIdentifierand links it under its canonical name. Bundling it into the single file would make the fallback silently unavailable. On Unix the app sets the executable bit at runtime, and the release bundles preserve the bit through thezipCLI. The Unix binaries are committed with mode100755(git update-index --chmod=+x) so a publish from Linux/macOS keeps them executable.7zip-license.txtmust ship beside them: it contains the Windows (7za.exe) and Linux/macOS (7zz,7zzs) license texts, since the Windows and Unix packages carry different notices. The Unix binaries are committed with mode100755, and the packaging script restores that mode in the zip even when the bundle is written on Windows (external attributes plus the central-directory host byte). -
winfsp.net stays at 2.1.x (
2.1.25156). Interop 2.2.x rejects the stable native 2.1 driver ("incorrect dll version (need 2.2, have 2.1)"); interop 2.1 accepts both the 2.1 stable driver and 2.2+ betas. Version gates live inWinFspMountService(RequiredWinFspVersion = 2.1).
- Bump
<AssemblyVersion>/<FileVersion>to the new version inSimpleZipDrive.csprojandSimpleZipDrive.Tests.csproj(there is no explicit<Version>property;AssemblyVersiondrives the published version). - Update
WhatsNew.md(user-facing Added/Fixed/Changed/Internal sections) - the matching## <version>section becomes the GitHub release notes automatically. - Push to
master- the CI workflow must be green. - Run Release from the Actions tab with the version, or push a
release_<version>tag. The workflow verifies the csproj version, runs the tests again, and builds the six bundles. - Download the release-bundles-* artifacts and review the zips before approving - the run summary lists sizes and SHA256 checksums.
- Smoke-test a downloaded bundle on each OS: mount a stored ZIP, a compressed archive, a
.zarcontainer, and an Xbox.iso/.csoimage through the packaged executable (both a drive letter and a folder; elevated and non-elevated for WinFsp; a folder mount on Linux/macOS). - Approve the Publish release job in the
releaseenvironment. The workflow creates the GitHub release with the six bundles attached (full release - not draft/prerelease). - Inform issue reporters whose bugs the release fixes.
Bundles can also be produced locally with scripts/package-release.ps1 -Version <version> and uploaded by hand with gh release create release_<version> --title "<version>" --notes-file . *.zip, but the Actions path is the supported one.
Resources