fix(desktop): publish mac updater metadata (#9945)
Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
@@ -108,11 +108,16 @@ jobs:
|
||||
id-token: write # Required for Sigstore OIDC signing
|
||||
attestations: write # Required for SLSA build provenance attestations
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Generate macOS update manifest
|
||||
run: node ui/desktop/scripts/generate-mac-update-manifest.js --version "${GITHUB_REF_NAME}" --directory .
|
||||
|
||||
- name: Attest build provenance
|
||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
||||
with:
|
||||
@@ -124,6 +129,7 @@ jobs:
|
||||
*.deb
|
||||
*.rpm
|
||||
*.flatpak
|
||||
latest-mac.yml
|
||||
download_cli.sh
|
||||
|
||||
# Create/update the versioned release
|
||||
@@ -139,6 +145,7 @@ jobs:
|
||||
*.deb
|
||||
*.rpm
|
||||
*.flatpak
|
||||
latest-mac.yml
|
||||
download_cli.sh
|
||||
allowUpdates: true
|
||||
omitBody: true
|
||||
@@ -159,6 +166,7 @@ jobs:
|
||||
*.deb
|
||||
*.rpm
|
||||
*.flatpak
|
||||
latest-mac.yml
|
||||
download_cli.sh
|
||||
allowUpdates: true
|
||||
omitBody: true
|
||||
|
||||
Generated
+1
-1
@@ -4995,7 +4995,7 @@ dependencies = [
|
||||
"pem",
|
||||
"pkcs1",
|
||||
"pkcs8 0.11.0",
|
||||
"rand 0.8.6",
|
||||
"rand 0.10.1",
|
||||
"regex",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp",
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const crypto = require('node:crypto');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function usage() {
|
||||
console.error(
|
||||
'Usage: node scripts/generate-mac-update-manifest.js --version <version> [--directory <path>]'
|
||||
);
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
directory: process.cwd(),
|
||||
version: '',
|
||||
};
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
if (arg === '--version') {
|
||||
args.version = argv[++i] || '';
|
||||
} else if (arg === '--directory') {
|
||||
args.directory = argv[++i] || '';
|
||||
} else {
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.version || !args.directory) {
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
args.version = args.version.replace(/^v/, '');
|
||||
args.directory = path.resolve(args.directory);
|
||||
return args;
|
||||
}
|
||||
|
||||
function ensureFile(filePath) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`Missing required file: ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function copyIfDifferent(source, target) {
|
||||
ensureFile(source);
|
||||
if (path.resolve(source) === path.resolve(target)) {
|
||||
return;
|
||||
}
|
||||
fs.copyFileSync(source, target);
|
||||
}
|
||||
|
||||
function sha512(filePath) {
|
||||
const hash = crypto.createHash('sha512');
|
||||
hash.update(fs.readFileSync(filePath));
|
||||
return hash.digest('base64');
|
||||
}
|
||||
|
||||
function yamlString(value) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function writeManifest({ directory, version }) {
|
||||
const files = [
|
||||
{
|
||||
sourceName: 'Goose.zip',
|
||||
updateName: 'Goose-darwin-arm64.zip',
|
||||
},
|
||||
{
|
||||
sourceName: 'Goose_intel_mac.zip',
|
||||
updateName: 'Goose-darwin-x64.zip',
|
||||
},
|
||||
];
|
||||
|
||||
const entries = files.map(({ sourceName, updateName }) => {
|
||||
const sourcePath = path.join(directory, sourceName);
|
||||
const updatePath = path.join(directory, updateName);
|
||||
copyIfDifferent(sourcePath, updatePath);
|
||||
|
||||
const stats = fs.statSync(updatePath);
|
||||
return {
|
||||
url: updateName,
|
||||
sha512: sha512(updatePath),
|
||||
size: stats.size,
|
||||
};
|
||||
});
|
||||
|
||||
const manifest = [
|
||||
`version: ${yamlString(version)}`,
|
||||
'files:',
|
||||
...entries.flatMap((entry) => [
|
||||
` - url: ${yamlString(entry.url)}`,
|
||||
` sha512: ${yamlString(entry.sha512)}`,
|
||||
` size: ${entry.size}`,
|
||||
]),
|
||||
`path: ${yamlString(entries[0].url)}`,
|
||||
`sha512: ${yamlString(entries[0].sha512)}`,
|
||||
`releaseDate: ${yamlString(new Date().toISOString())}`,
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(path.join(directory, 'latest-mac.yml'), manifest);
|
||||
}
|
||||
|
||||
try {
|
||||
writeManifest(parseArgs(process.argv.slice(2)));
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -81,7 +81,8 @@ const i18n = defineMessages({
|
||||
},
|
||||
autoDownload: {
|
||||
id: 'updateSection.autoDownload',
|
||||
defaultMessage: 'Update will be downloaded automatically in the background.',
|
||||
defaultMessage:
|
||||
'Goose will download the update in the background and install it the next time you quit or restart.',
|
||||
},
|
||||
manualInstallNote: {
|
||||
id: 'updateSection.manualInstallNote',
|
||||
@@ -89,7 +90,7 @@ const i18n = defineMessages({
|
||||
},
|
||||
autoInstallNote: {
|
||||
id: 'updateSection.autoInstallNote',
|
||||
defaultMessage: 'The update will be installed automatically when you quit the app.',
|
||||
defaultMessage: 'No manual install is needed.',
|
||||
},
|
||||
readyInstallManual: {
|
||||
id: 'updateSection.readyInstallManual',
|
||||
@@ -101,11 +102,12 @@ const i18n = defineMessages({
|
||||
},
|
||||
readyInstallAuto: {
|
||||
id: 'updateSection.readyInstallAuto',
|
||||
defaultMessage: '✓ Update is ready! It will be installed when you quit Goose.',
|
||||
defaultMessage:
|
||||
"✓ Update is ready. Restart Goose to finish installing it, or quit when you're done.",
|
||||
},
|
||||
installNowHint: {
|
||||
id: 'updateSection.installNowHint',
|
||||
defaultMessage: 'Or click "Install & Restart" to update now.',
|
||||
defaultMessage: 'Click "Install & Restart" to update now.',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -176,7 +178,6 @@ export default function UpdateSection() {
|
||||
|
||||
// Listen for updater events
|
||||
window.electron.onUpdaterEvent((event) => {
|
||||
|
||||
switch (event.event) {
|
||||
case 'checking-for-update':
|
||||
setUpdateStatus('checking');
|
||||
@@ -354,10 +355,15 @@ export default function UpdateSection() {
|
||||
<div className="text-text-primary text-2xl font-mono">
|
||||
{updateInfo.currentVersion || intl.formatMessage(i18n.loading)}
|
||||
</div>
|
||||
<div className="text-xs text-text-secondary">{intl.formatMessage(i18n.currentVersion)}</div>
|
||||
<div className="text-xs text-text-secondary">
|
||||
{intl.formatMessage(i18n.currentVersion)}
|
||||
</div>
|
||||
</div>
|
||||
{updateInfo.latestVersion && updateInfo.isUpdateAvailable && (
|
||||
<span className="text-text-secondary"> {intl.formatMessage(i18n.versionAvailable, { version: updateInfo.latestVersion })}</span>
|
||||
<span className="text-text-secondary">
|
||||
{' '}
|
||||
{intl.formatMessage(i18n.versionAvailable, { version: updateInfo.latestVersion })}
|
||||
</span>
|
||||
)}
|
||||
{updateInfo.currentVersion && updateInfo.isUpdateAvailable === false && (
|
||||
<span className="text-text-primary"> {intl.formatMessage(i18n.upToDate)}</span>
|
||||
@@ -375,11 +381,13 @@ export default function UpdateSection() {
|
||||
{intl.formatMessage(i18n.checkForUpdates)}
|
||||
</Button>
|
||||
|
||||
{updateInfo.isUpdateAvailable && updateStatus === 'idle' && autoDownloadEffectivelyDisabled && (
|
||||
<Button onClick={downloadUpdate} variant="secondary" size="sm">
|
||||
{intl.formatMessage(i18n.downloadNow)}
|
||||
</Button>
|
||||
)}
|
||||
{updateInfo.isUpdateAvailable &&
|
||||
updateStatus === 'idle' &&
|
||||
autoDownloadEffectivelyDisabled && (
|
||||
<Button onClick={downloadUpdate} variant="secondary" size="sm">
|
||||
{intl.formatMessage(i18n.downloadNow)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{updateStatus === 'ready' && (
|
||||
<Button onClick={installUpdate} variant="default" size="sm">
|
||||
|
||||
@@ -4698,7 +4698,7 @@
|
||||
"defaultMessage": "URL:"
|
||||
},
|
||||
"updateSection.autoDownload": {
|
||||
"defaultMessage": "Update will be downloaded automatically in the background."
|
||||
"defaultMessage": "Goose will download the update in the background and install it the next time you quit or restart."
|
||||
},
|
||||
"updateSection.autoDownloadDisabledByEnv": {
|
||||
"defaultMessage": "Automatic downloads are disabled via the GOOSE_DISABLE_AUTO_DOWNLOAD environment variable."
|
||||
@@ -4707,7 +4707,7 @@
|
||||
"defaultMessage": "Automatic download is disabled. Click \"Download Now\" to download manually."
|
||||
},
|
||||
"updateSection.autoInstallNote": {
|
||||
"defaultMessage": "The update will be installed automatically when you quit the app."
|
||||
"defaultMessage": "No manual install is needed."
|
||||
},
|
||||
"updateSection.checkForUpdates": {
|
||||
"defaultMessage": "Check for Updates"
|
||||
@@ -4740,7 +4740,7 @@
|
||||
"defaultMessage": "Install & Restart"
|
||||
},
|
||||
"updateSection.installNowHint": {
|
||||
"defaultMessage": "Or click \"Install & Restart\" to update now."
|
||||
"defaultMessage": "Click \"Install & Restart\" to update now."
|
||||
},
|
||||
"updateSection.latestVersion": {
|
||||
"defaultMessage": "You are running the latest version!"
|
||||
@@ -4755,7 +4755,7 @@
|
||||
"defaultMessage": "Manual installation required for this update method."
|
||||
},
|
||||
"updateSection.readyInstallAuto": {
|
||||
"defaultMessage": "✓ Update is ready! It will be installed when you quit Goose."
|
||||
"defaultMessage": "✓ Update is ready. Restart Goose to finish installing it, or quit when you're done."
|
||||
},
|
||||
"updateSection.readyInstallManual": {
|
||||
"defaultMessage": "✓ Update is ready! Click \"Install & Restart\" for installation instructions."
|
||||
|
||||
Reference in New Issue
Block a user