@@ -33,6 +33,8 @@ export async function packageDesktop(options = {}) {
3333 options . outputRoot ?? resolve ( workspaceRoot , 'dist' , 'desktop' ) ,
3434 ) ;
3535 const desktopRoot = resolve ( workspaceRoot , 'apps' , 'desktop' ) ;
36+ const releaseChannel = options . releaseChannel ?? 'preview' ;
37+ const osxSign = createDesktopSignOptions ( releaseChannel ) ;
3638 const stage = resolve ( outputRoot , `stage-${ architecture } ` ) ;
3739 const output = resolve ( outputRoot , architecture ) ;
3840 const resources = resolve ( stage , 'release-input' , 'portreeve' ) ;
@@ -94,7 +96,7 @@ export async function packageDesktop(options = {}) {
9496 private : true ,
9597 type : 'module' ,
9698 main : 'main/index.js' ,
97- portreeveReleaseChannel : options . releaseChannel ?? 'preview' ,
99+ portreeveReleaseChannel : releaseChannel ,
98100 } ,
99101 null ,
100102 2 ,
@@ -109,7 +111,7 @@ export async function packageDesktop(options = {}) {
109111 artifactVersion : artifact . version ,
110112 artifactSha256 : artifact . sha256 ,
111113 architecture,
112- releaseChannel : options . releaseChannel ?? 'preview' ,
114+ releaseChannel,
113115 moduleGraph : {
114116 directLifecycleController : true ,
115117 verifiedArtifactResolver : true ,
@@ -142,6 +144,7 @@ export async function packageDesktop(options = {}) {
142144 prune : false ,
143145 ignore : / ^ \/ r e l e a s e - i n p u t (?: \/ | $ ) / ,
144146 extraResource : [ resolve ( stage , 'release-input' , 'portreeve' ) ] ,
147+ osxSign,
145148 } ) ;
146149 if ( paths . length !== 1 ) {
147150 throw new Error ( `Desktop packager returned ${ paths . length } output paths.` ) ;
@@ -171,6 +174,43 @@ export async function packageDesktop(options = {}) {
171174 } ;
172175}
173176
177+ /**
178+ * Preview bundles use an ad-hoc identity so every nested executable and the final
179+ * application bundle are sealed consistently. This proves bundle integrity without
180+ * claiming a Developer ID identity or Gatekeeper trust. Stable packaging remains
181+ * unavailable until the separate Developer ID and notarization path is configured.
182+ *
183+ * @param {'preview'|'stable' } releaseChannel
184+ */
185+ export function createDesktopSignOptions ( releaseChannel ) {
186+ if ( releaseChannel !== 'preview' ) {
187+ throw new Error (
188+ 'Stable Desktop packaging requires configured Developer ID signing and notarization.' ,
189+ ) ;
190+ }
191+ return {
192+ identity : '-' ,
193+ identityValidation : false ,
194+ continueOnError : false ,
195+ preAutoEntitlements : false ,
196+ preEmbedProvisioningProfile : false ,
197+ ignore : isPromotedCliResource ,
198+ optionsForFile : ( ) => ( { hardenedRuntime : false , timestamp : 'none' } ) ,
199+ } ;
200+ }
201+
202+ /**
203+ * The promoted CLI must remain byte-for-byte identical to its release manifest.
204+ * The application signature seals that exact resource without re-signing it.
205+ *
206+ * @param {string } filePath
207+ */
208+ export function isPromotedCliResource ( filePath ) {
209+ return / \/ C o n t e n t s \/ R e s o u r c e s \/ p o r t r e e v e \/ p o r t r e e v e - v [ ^ / ] + $ / u. test (
210+ filePath . replaceAll ( '\\' , '/' ) ,
211+ ) ;
212+ }
213+
174214/** @returns {'arm64'|'x64' } */
175215function nativeArchitecture ( ) {
176216 if ( process . arch === 'arm64' || process . arch === 'x64' ) return process . arch ;
0 commit comments