@stacksjs/defaults 0.75.310 → 0.75.315
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Shared Chrome executable preparation for the standalone skill and testing package.
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, renameSync, rmSync } from 'node:fs'
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync } from 'node:fs'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
|
-
import { join } from 'node:path'
|
|
4
|
+
import { basename, join } from 'node:path'
|
|
5
5
|
import process from 'node:process'
|
|
6
6
|
|
|
7
7
|
const MAC_CHROMES = [
|
|
@@ -56,12 +56,33 @@ export interface PreparedChrome {
|
|
|
56
56
|
reused: boolean
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** Postorder, without following symlinks: nested frameworks must be sealed before their parents. */
|
|
60
|
+
function frameworkVersions(directory: string): { path: string, identifier: string }[] {
|
|
61
|
+
const result: { path: string, identifier: string }[] = []
|
|
62
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
63
|
+
if (!entry.isDirectory()) continue
|
|
64
|
+
const child = join(directory, entry.name)
|
|
65
|
+
result.push(...frameworkVersions(child))
|
|
66
|
+
if (!entry.name.endsWith('.framework') || !existsSync(join(child, 'Versions'))) continue
|
|
67
|
+
const identifier = plist(join(child, 'Resources/Info.plist'), 'Print :CFBundleIdentifier')
|
|
68
|
+
if (!identifier.ok || !identifier.out) throw new Error(`Framework has no signing identifier: ${child}`)
|
|
69
|
+
for (const version of readdirSync(join(child, 'Versions'), { withFileTypes: true }).filter(item => item.isDirectory()).sort((a, b) => a.name.localeCompare(b.name)))
|
|
70
|
+
result.push({ path: join(child, 'Versions', version.name), identifier: identifier.out })
|
|
71
|
+
}
|
|
72
|
+
return result
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validSignature(app: string): boolean {
|
|
76
|
+
return Bun.spawnSync(['codesign', '--verify', '--deep', '--strict', app], { stdout: 'pipe', stderr: 'pipe' }).exitCode === 0
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
/**
|
|
60
80
|
* A private clone of a macOS Chrome that GoogleUpdater leaves alone (see the
|
|
61
81
|
* module docs): `cp -cR`, the `KS*` keys deleted from `Info.plist`, its own
|
|
62
|
-
* `CFBundleIdentifier`,
|
|
63
|
-
*
|
|
64
|
-
*
|
|
82
|
+
* `CFBundleIdentifier`, and a complete ad-hoc signature. Every framework
|
|
83
|
+
* version shares an explicit designated requirement, as required for a
|
|
84
|
+
* multi-version bundle (Apple TN2206). Kept in `directory` (default a
|
|
85
|
+
* per-version temp folder) and reused only after strict signature validation.
|
|
65
86
|
*/
|
|
66
87
|
export function prepareMacChrome(options: { source?: string, directory?: string } = {}): PreparedChrome {
|
|
67
88
|
if (process.platform !== 'darwin') throw new Error('prepareMacChrome is for macOS; elsewhere launch Chrome as installed')
|
|
@@ -73,12 +94,15 @@ export function prepareMacChrome(options: { source?: string, directory?: string
|
|
|
73
94
|
const executableName = plist(sourceInfo, 'Print :CFBundleExecutable').out || 'Google Chrome'
|
|
74
95
|
const directory = options.directory ?? join(tmpdir(), `stacks-chrome-${version.replace(/[^\w.-]/g, '_')}`)
|
|
75
96
|
const app = join(directory, 'Chrome.app')
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
const candidates = existsSync(directory) ? readdirSync(directory, { withFileTypes: true })
|
|
98
|
+
.filter(entry => entry.isDirectory() && (entry.name === 'Chrome.app' || (entry.name.startsWith('Chrome-') && entry.name.endsWith('.app'))))
|
|
99
|
+
.map(entry => join(directory, entry.name)).sort() : []
|
|
100
|
+
for (const candidate of candidates) {
|
|
101
|
+
const executable = join(candidate, 'Contents/MacOS', executableName), info = join(candidate, 'Contents/Info.plist')
|
|
102
|
+
if (existsSync(executable) && plist(info, 'Print :CFBundleIdentifier').out === PRIVATE_BUNDLE_ID
|
|
103
|
+
&& plist(info, 'Print :CFBundleShortVersionString').out === version && validSignature(candidate))
|
|
104
|
+
return { executable, app: candidate, reused: true }
|
|
105
|
+
}
|
|
82
106
|
|
|
83
107
|
// Build beside the final path and move it in, so a run killed half-way
|
|
84
108
|
// never leaves a half-signed app that a later run would reuse.
|
|
@@ -94,13 +118,26 @@ export function prepareMacChrome(options: { source?: string, directory?: string
|
|
|
94
118
|
const stagedInfo = join(staged, 'Contents/Info.plist')
|
|
95
119
|
for (const key of UPDATER_KEYS) plist(stagedInfo, `Delete :${key}`)
|
|
96
120
|
run(['/usr/libexec/PlistBuddy', '-c', `Set :CFBundleIdentifier ${PRIVATE_BUNDLE_ID}`, stagedInfo], 'Renaming the Chrome bundle')
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
const versions = frameworkVersions(staged)
|
|
122
|
+
// --deep only signs the current framework version. Prepare the code in
|
|
123
|
+
// every version first, then seal nested frameworks and their parents.
|
|
124
|
+
for (const item of versions) run(['codesign', '--force', '--deep', '--sign', '-', item.path], 'Re-signing framework code')
|
|
125
|
+
for (const item of versions) run(['codesign', '--force', '--sign', '-', '--requirements', `=designated => identifier ${JSON.stringify(item.identifier)}`, item.path], 'Sealing a framework version')
|
|
126
|
+
run(['codesign', '--force', ...(versions.length ? [] : ['--deep']), '--sign', '-', staged], 'Sealing Chrome')
|
|
127
|
+
run(['codesign', '--verify', '--deep', '--strict', staged], 'Verifying Chrome')
|
|
128
|
+
// Never remove a cached bundle that another browser process may still be
|
|
129
|
+
// using. An invalid cache, or a concurrent preparation, gets a new generation.
|
|
130
|
+
let destination = existsSync(app) ? join(directory, `Chrome-${basename(staging)}.app`) : app
|
|
131
|
+
try { renameSync(staged, destination) }
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (destination !== app || !['EEXIST', 'ENOTEMPTY'].includes((error as NodeJS.ErrnoException).code ?? '')) throw error
|
|
134
|
+
destination = join(directory, `Chrome-${basename(staging)}.app`)
|
|
135
|
+
renameSync(staged, destination)
|
|
136
|
+
}
|
|
137
|
+
return { executable: join(destination, 'Contents/MacOS', executableName), app: destination, reused: false }
|
|
100
138
|
}
|
|
101
139
|
finally {
|
|
102
140
|
rmSync(staging, { recursive: true, force: true })
|
|
103
141
|
}
|
|
104
|
-
return { executable, app, reused: false }
|
|
105
142
|
}
|
|
106
143
|
|
|
@@ -18,6 +18,21 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
18
18
|
|
|
19
19
|
## Runtime
|
|
20
20
|
|
|
21
|
+
For offline native projects (`stacks.type: native`), Buddy compiles the app and
|
|
22
|
+
Pantry owns runtime provisioning and distribution. Declare immutable runtime
|
|
23
|
+
pins, entitlements, native checks and publishing targets in `pantry.json.native`.
|
|
24
|
+
Buddy uses `ts-pantry/native` to provision Craft with its locked compiler and
|
|
25
|
+
first-party pins and to prepare signing inputs. Keep `craft.config.ts` for app
|
|
26
|
+
metadata and UI. Do not scaffold app-specific runtime scripts, entitlement XML,
|
|
27
|
+
keychain scripts, notarization commands or store-upload workflows. Use Pantry's
|
|
28
|
+
versioned `.github/workflows/native-app.yml` reusable workflow for checks and
|
|
29
|
+
releases. New/adopted native apps also use Buddy-encrypted `.env.production`,
|
|
30
|
+
with `.env.keys` ignored and only `DOTENV_PRIVATE_KEY_PRODUCTION` passed to
|
|
31
|
+
release Actions. Native `buddy release --bump patch|minor|major` delegates
|
|
32
|
+
qualification and versioning to Pantry; the tag invokes its delivery workflow.
|
|
33
|
+
`CRAFT_BIN` remains a local development override; release pipelines
|
|
34
|
+
require the declared immutable runtime.
|
|
35
|
+
|
|
21
36
|
Stacks desktop applications use the native [Craft](https://github.com/home-lang/craft)
|
|
22
37
|
runtime. `buddy dev:desktop` hosts the dashboard/application and opens it in a
|
|
23
38
|
Craft window. `buddy build:desktop` compiles a small Stacks launcher and places
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.315",
|
|
6
6
|
"description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.315",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@iconify-json/f7": "^1.2.2",
|
|
47
47
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
48
|
-
"@stacksjs/mobile": "0.75.
|
|
48
|
+
"@stacksjs/mobile": "0.75.315",
|
|
49
49
|
"@stacksjs/sanitizer": "0.2.691",
|
|
50
50
|
"@stacksjs/stx": "0.2.691",
|
|
51
51
|
"@xterm/addon-fit": "^0.11.0",
|