Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120#!/usr/bin/env node// release.mjs — publish a MacPal release for over-the-air self-update.//// Every installed pal (Fía's star, the neo/blueberry badges) polls// https://releases.aesthetic.computer/macpal/latest.json// hourly via UpdatePlugin.swift and swaps itself for anything newer — so// shipping a build is just://// node macpal/release.mjs # package.sh (sign+notarize) → upload// node macpal/release.mjs --no-package # reuse an existing dist/MacPal.zip// node macpal/release.mjs --dry-run # show what would upload//// The version comes from Resources/Info.plist (bump CFBundleShortVersionString// before releasing). The zip lands at macpal/MacPal-<version>.zip (immutable),// the manifest at macpal/latest.json (no-cache), both public-read on the// releases-aesthetic-computer Space — the same bucket the desktop app uses.//// Credentials: SPACES_KEY / SPACES_SECRET / SPACES_ENDPOINT from the vault// silo/.env (same file ac-electron's publish-release.mjs reads).
import fs from "fs";import path from "path";import { createHash } from "crypto";import { execSync } from "child_process";import { fileURLToPath } from "url";import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const __dirname = path.dirname(fileURLToPath(import.meta.url));const args = process.argv.slice(2);const dryRun = args.includes("--dry-run");const noPackage = args.includes("--no-package");
// ── creds (vault silo/.env, or the environment) ────────────────────────────for (const envPath of [ path.join(__dirname, "..", "..", "aesthetic-computer-vault", "silo", ".env"), path.join(__dirname, "..", "silo", ".env"),]) { if (!fs.existsSync(envPath)) continue; for (const line of fs.readFileSync(envPath, "utf8").split("\n")) { const m = line.match(/^([A-Z_]+)=(.*)$/); if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^["']|["']$/g, ""); } break;}
// ── version from Info.plist ─────────────────────────────────────────────────const plist = fs.readFileSync(path.join(__dirname, "Resources", "Info.plist"), "utf8");const version = plist.match( /CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/,)?.[1];if (!version) { console.error("✗ couldn't read CFBundleShortVersionString from Resources/Info.plist"); process.exit(1);}
// ── package (sign + notarize) unless told not to ────────────────────────────const zipPath = path.join(__dirname, "dist", "MacPal.zip");if (!noPackage) { console.log(`› packaging v${version} (sign + notarize — a few minutes)`); execSync("./package.sh", { cwd: __dirname, stdio: "inherit" });} else if (!fs.existsSync(zipPath)) { console.error("✗ --no-package but dist/MacPal.zip doesn't exist"); process.exit(1);}
const zip = fs.readFileSync(zipPath);const sha256 = createHash("sha256").update(zip).digest("hex");
const BUCKET = "releases-aesthetic-computer";const key = `macpal/MacPal-${version}.zip`;const url = `https://releases.aesthetic.computer/${key}`;const manifest = { version, url, sha256, size: zip.length, at: new Date().toISOString(),};
console.log(`› MacPal v${version} — ${(zip.length / 1024).toFixed(0)} KB, sha256 ${sha256.slice(0, 12)}…`);if (dryRun) { console.log(" [dry-run]", JSON.stringify(manifest, null, 2)); process.exit(0);}
const s3 = new S3Client({ endpoint: process.env.SPACES_ENDPOINT || "https://sfo3.digitaloceanspaces.com", region: "us-east-1", credentials: { accessKeyId: process.env.SPACES_KEY || "", secretAccessKey: process.env.SPACES_SECRET || "", }, requestChecksumCalculation: "WHEN_REQUIRED", responseChecksumValidation: "WHEN_REQUIRED",});
console.log(`› uploading ${key}`);await s3.send(new PutObjectCommand({ Bucket: BUCKET, Key: key, Body: zip, ContentType: "application/zip", CacheControl: "public, max-age=31536000", // versioned name → immutable ACL: "public-read",}));
console.log("› uploading macpal/latest.json");await s3.send(new PutObjectCommand({ Bucket: BUCKET, Key: "macpal/latest.json", Body: JSON.stringify(manifest, null, 2), ContentType: "application/json", CacheControl: "no-cache, no-store, must-revalidate", ACL: "public-read",}));
console.log(`✓ published — pals pick it up within the hour`);console.log(` ${url}`);console.log(` https://releases.aesthetic.computer/macpal/latest.json`);