diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a67772..099f20b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,15 @@ on: push: tags: ['v*'] workflow_dispatch: + inputs: + publish_release: + description: 'Publish the GitHub release after all platform QA is certified' + type: boolean + default: false + certified_cross_platform: + description: 'Confirm Windows, Linux, and macOS clean-machine QA passed' + type: boolean + default: false permissions: contents: write @@ -89,7 +98,11 @@ jobs: publish: name: Publish GitHub release - if: github.ref_type == 'tag' + if: >- + github.event_name == 'workflow_dispatch' && + startsWith(github.ref, 'refs/tags/v') && + inputs.publish_release && + inputs.certified_cross_platform needs: build runs-on: ubuntu-22.04 steps: diff --git a/RELEASE.md b/RELEASE.md index cba45c1..27590e4 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -53,15 +53,23 @@ node scripts/verify-binaries.js --search-root "$APP" --target aarch64-apple-darw node scripts/smoke-packaged-app.js --executable "$APP/Contents/MacOS/firelink" ``` -## Automated release +GitHub release publication is intentionally manual. Tag pushes build and upload +artifacts, but the `publish` job only runs from a `workflow_dispatch` on a `v*` +tag when both release-certification inputs are checked after Windows, Linux, +and macOS clean-machine QA. -Push a version tag: +## Automated release builds + +Push a version tag to build and verify native artifacts: ```bash git tag v git push origin v ``` -GitHub Actions builds all targets on native runners, verifies engines inside final package contents, performs packaged launch smoke where supported, creates SHA-256 sums, then publishes one GitHub Release. +GitHub Actions builds all targets on native runners, verifies engines inside +final package contents, performs packaged launch smoke where supported, and +uploads artifacts. Publish the GitHub Release with a manual `workflow_dispatch` +run on the same tag after clean-machine QA is complete. No target may silently skip missing engines, failed extraction, checksum mismatch, or missing package output. diff --git a/src-tauri/build.rs b/src-tauri/build.rs index d860e1e..a270b53 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,5 @@ fn main() { + std::fs::create_dir_all("engine-dist") + .expect("failed to create generated engine resource directory"); tauri_build::build() } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6fc4623..c8ad836 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2997,7 +2997,7 @@ fn get_free_space(app_handle: tauri::AppHandle, path: String) -> Result max_match_len { max_match_len = match_len; diff --git a/src/utils/downloadLocations.test.ts b/src/utils/downloadLocations.test.ts index 4bcbdaf..3fda862 100644 --- a/src/utils/downloadLocations.test.ts +++ b/src/utils/downloadLocations.test.ts @@ -17,9 +17,9 @@ import { } from './downloadLocations'; describe('download locations', () => { - it('compares Windows and macOS locations case-insensitively', () => { + it('matches backend platform path case semantics', () => { expect(downloadLocationEquals('D:\\Downloads', 'Movie.MP4', 'd:/downloads', 'movie.mp4', 'windows')).toBe(true); - expect(downloadLocationEquals('/Users/Test', 'Movie.MP4', '/users/test', 'movie.mp4', 'macos')).toBe(true); + expect(downloadLocationEquals('/Users/Test', 'Movie.MP4', '/users/test', 'movie.mp4', 'macos')).toBe(false); expect(downloadLocationEquals('/home/Test', 'Movie.MP4', '/home/test', 'movie.mp4', 'linux')).toBe(false); }); beforeEach(() => { diff --git a/src/utils/downloadLocations.ts b/src/utils/downloadLocations.ts index ef7df9b..bbe3d45 100644 --- a/src/utils/downloadLocations.ts +++ b/src/utils/downloadLocations.ts @@ -153,7 +153,7 @@ export const downloadLocationEquals = ( ): boolean => { const normalize = (value: string) => { const normalized = value.replace(/\\/g, '/').replace(/\/+$/, ''); - return os === 'windows' || os === 'macos' + return os === 'windows' ? normalized.toLocaleLowerCase() : normalized; };