Preserve Cargo artifacts during deploy and install

This commit is contained in:
2026-09-11 12:41:39 -05:00
parent 5d2a0ea2de
commit ba56d83fc6
4 changed files with 50 additions and 61 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ This file provides guidance when working with code in this repository.
- `cargo bundle --bin warp` - Bundle the main app - `cargo bundle --bin warp` - Bundle the main app
- `make run` - Build and run `galaxy-oss` - `make run` - Build and run `galaxy-oss`
- `make clean` - Remove Cargo build artifacts - `make clean` - Remove Cargo build artifacts
- `make deploy` - Validate, update, clean, bundle, and upload Galaxy to Hermes - `make deploy` - Validate, update, bundle incrementally, and upload Galaxy to Hermes
- `make install` - Validate, update, clean, bundle, and install Galaxy to `/Applications` - `make install` - Validate, update, bundle incrementally, and install Galaxy to `/Applications`
### Running with local warp-server ### Running with local warp-server
To connect Warp client to a local warp-server instance: To connect Warp client to a local warp-server instance:
+1
View File
@@ -3,6 +3,7 @@
run: run:
cargo run --bin galaxy-oss cargo run --bin galaxy-oss
# Explicit cleanup; deploy and install preserve Cargo build artifacts.
clean: clean:
cargo clean cargo clean
+34 -40
View File
@@ -282,7 +282,6 @@ function App() {
const [steps, setSteps] = useState<Step[]>([ const [steps, setSteps] = useState<Step[]>([
{ label: "Check working directory", status: "pending" }, { label: "Check working directory", status: "pending" },
{ label: "Pull current branch", status: "pending" }, { label: "Pull current branch", status: "pending" },
{ label: "Clean Cargo build artifacts", status: "pending" },
{ label: "Bundle Galaxy", status: "pending" }, { label: "Bundle Galaxy", status: "pending" },
{ label: "Sign and create Galaxy.zip", status: "pending" }, { label: "Sign and create Galaxy.zip", status: "pending" },
{ label: "Authenticate with Hermes", status: "pending" }, { label: "Authenticate with Hermes", status: "pending" },
@@ -332,22 +331,17 @@ function App() {
); );
updateStep(1, { status: "done", detail: branch }); updateStep(1, { status: "done", detail: branch });
// ─── Step 2: Clean ────────────────────────────────────────────── // ─── Step 2: Bundle ─────────────────────────────────────────────
updateStep(2, { status: "running" }); updateStep(2, { status: "running" });
await runCommandStreaming("cargo clean", WORKSPACE_ROOT, (line) => appendLog(2, line));
updateStep(2, { status: "done" });
// ─── Step 3: Bundle ─────────────────────────────────────────────
updateStep(3, { status: "running" });
await runCommandStreaming( await runCommandStreaming(
"cargo bundle --release --bin galaxy-oss --package galaxy --features release_bundle,extern_plist", "cargo bundle --release --bin galaxy-oss --package galaxy --features release_bundle,extern_plist",
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(3, line) (line) => appendLog(2, line)
); );
updateStep(3, { status: "done" }); updateStep(2, { status: "done" });
// ─── Step 4: Sign and zip ─────────────────────────────────────── // ─── Step 3: Sign and zip ───────────────────────────────────────
updateStep(4, { status: "running" }); updateStep(3, { status: "running" });
// Distribution bundles must use a release profile so rust-embed includes assets in the // Distribution bundles must use a release profile so rust-embed includes assets in the
// executable instead of resolving them from the build machine's checkout at runtime. // executable instead of resolving them from the build machine's checkout at runtime.
@@ -358,12 +352,12 @@ function App() {
await runCommandStreaming( await runCommandStreaming(
`/usr/bin/codesign --force --deep --sign - "${appPath}"`, `/usr/bin/codesign --force --deep --sign - "${appPath}"`,
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(4, line) (line) => appendLog(3, line)
); );
await runCommandStreaming( await runCommandStreaming(
`/usr/bin/codesign --verify --deep --strict --verbose=2 "${appPath}"`, `/usr/bin/codesign --verify --deep --strict --verbose=2 "${appPath}"`,
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(4, line) (line) => appendLog(3, line)
); );
// Remove old zip if exists // Remove old zip if exists
@@ -372,15 +366,15 @@ function App() {
await runCommandStreaming( await runCommandStreaming(
`ditto -c -k --keepParent "${appPath}" "${zipPath}"`, `ditto -c -k --keepParent "${appPath}" "${zipPath}"`,
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(4, line) (line) => appendLog(3, line)
); );
const fileSize = statSync(zipPath).size; const fileSize = statSync(zipPath).size;
const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(1); const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(1);
updateStep(4, { status: "done", detail: `${fileSizeMB} MB` }); updateStep(3, { status: "done", detail: `${fileSizeMB} MB` });
// ─── Step 5: Authenticate ─────────────────────────────────────── // ─── Step 4: Authenticate ───────────────────────────────────────
updateStep(5, { status: "running" }); updateStep(4, { status: "running" });
const loginRes = await apiPost<{ token: string }>( const loginRes = await apiPost<{ token: string }>(
`${BASE_URL}/api/auth/login`, `${BASE_URL}/api/auth/login`,
@@ -388,11 +382,11 @@ function App() {
COMMON_HEADERS COMMON_HEADERS
); );
const token = loginRes.token; const token = loginRes.token;
appendLog(5, `Authenticated as ${HERMES_USER}`); appendLog(4, `Authenticated as ${HERMES_USER}`);
updateStep(5, { status: "done" }); updateStep(4, { status: "done" });
// ─── Step 6: Start upload ─────────────────────────────────────── // ─── Step 5: Start upload ───────────────────────────────────────
updateStep(6, { status: "running" }); updateStep(5, { status: "running" });
const fileHash = computeFileHash(zipPath); const fileHash = computeFileHash(zipPath);
@@ -408,14 +402,14 @@ function App() {
); );
const { uploadId, urls: partUrls, totalParts, partSize } = startRes; const { uploadId, urls: partUrls, totalParts, partSize } = startRes;
appendLog(6, `Upload ID: ${uploadId.slice(0, 32)}...`); appendLog(5, `Upload ID: ${uploadId.slice(0, 32)}...`);
appendLog(6, `File hash: ${fileHash}`); appendLog(5, `File hash: ${fileHash}`);
appendLog(6, `File size: ${fileSizeMB} MB`); appendLog(5, `File size: ${fileSizeMB} MB`);
appendLog(6, `Parts: ${totalParts}`); appendLog(5, `Parts: ${totalParts}`);
updateStep(6, { status: "done", detail: `${totalParts} parts` }); updateStep(5, { status: "done", detail: `${totalParts} parts` });
// ─── Step 7: Upload parts ─────────────────────────────────────── // ─── Step 6: Upload parts ───────────────────────────────────────
updateStep(7, { status: "running", progress: { bytes: 0, totalBytes: fileSize } }); updateStep(6, { status: "running", progress: { bytes: 0, totalBytes: fileSize } });
// Use the partSize from the server response // Use the partSize from the server response
const completedParts: { partNumber: number; etag: string }[] = []; const completedParts: { partNumber: number; etag: string }[] = [];
@@ -430,7 +424,7 @@ function App() {
partSize, partSize,
totalParts, totalParts,
(partBytes) => { (partBytes) => {
updateStep(7, { updateStep(6, {
status: "running", status: "running",
progress: { bytes: prevPartsBytes + partBytes, totalBytes: fileSize }, progress: { bytes: prevPartsBytes + partBytes, totalBytes: fileSize },
}); });
@@ -457,14 +451,14 @@ function App() {
); );
} }
updateStep(7, { updateStep(6, {
status: "done", status: "done",
detail: `${fileSizeMB} MB uploaded`, detail: `${fileSizeMB} MB uploaded`,
progress: undefined, progress: undefined,
}); });
// ─── Step 8: Complete ──────────────────────────────────────────── // ─── Step 7: Complete ────────────────────────────────────────────
updateStep(8, { status: "running" }); updateStep(7, { status: "running" });
await apiPost( await apiPost(
`${BASE_URL}/api/uploads/complete`, `${BASE_URL}/api/uploads/complete`,
@@ -476,11 +470,11 @@ function App() {
authHeaders(token) authHeaders(token)
); );
appendLog(8, `Key: ${UPLOAD_KEY}`); appendLog(7, `Key: ${UPLOAD_KEY}`);
updateStep(8, { status: "done" }); updateStep(7, { status: "done" });
// ─── Step 9: Upload install-galaxy.sh ──────────────────────────── // ─── Step 8: Upload install-galaxy.sh ────────────────────────────
updateStep(9, { status: "running" }); updateStep(8, { status: "running" });
const installScriptPath = path.join(WORKSPACE_ROOT, "script", "install-galaxy.sh"); const installScriptPath = path.join(WORKSPACE_ROOT, "script", "install-galaxy.sh");
const scriptFileSize = statSync(installScriptPath).size; const scriptFileSize = statSync(installScriptPath).size;
@@ -500,7 +494,7 @@ function App() {
const scriptCompletedParts: { partNumber: number; etag: string }[] = []; const scriptCompletedParts: { partNumber: number; etag: string }[] = [];
let scriptBytesUploaded = 0; let scriptBytesUploaded = 0;
updateStep(9, { status: "running", progress: { bytes: 0, totalBytes: scriptFileSize } }); updateStep(8, { status: "running", progress: { bytes: 0, totalBytes: scriptFileSize } });
for (const part of scriptStartRes.urls) { for (const part of scriptStartRes.urls) {
const prevBytes = scriptBytesUploaded; const prevBytes = scriptBytesUploaded;
@@ -511,7 +505,7 @@ function App() {
scriptStartRes.partSize, scriptStartRes.partSize,
scriptStartRes.totalParts, scriptStartRes.totalParts,
(partBytes) => { (partBytes) => {
updateStep(9, { updateStep(8, {
status: "running", status: "running",
progress: { bytes: prevBytes + partBytes, totalBytes: scriptFileSize }, progress: { bytes: prevBytes + partBytes, totalBytes: scriptFileSize },
}); });
@@ -546,8 +540,8 @@ function App() {
authHeaders(token) authHeaders(token)
); );
appendLog(9, `Key: ${INSTALL_SCRIPT_KEY}`); appendLog(8, `Key: ${INSTALL_SCRIPT_KEY}`);
updateStep(9, { status: "done", detail: `${(scriptFileSize / 1024).toFixed(1)} KB`, progress: undefined }); updateStep(8, { status: "done", detail: `${(scriptFileSize / 1024).toFixed(1)} KB`, progress: undefined });
// Cleanup zip // Cleanup zip
execSync(`rm -f "${zipPath}"`, { stdio: "pipe" }); execSync(`rm -f "${zipPath}"`, { stdio: "pipe" });
@@ -165,7 +165,6 @@ function App() {
const [steps, setSteps] = useState<Step[]>([ const [steps, setSteps] = useState<Step[]>([
{ label: "Check working directory", status: "pending" }, { label: "Check working directory", status: "pending" },
{ label: "Pull current branch", status: "pending" }, { label: "Pull current branch", status: "pending" },
{ label: "Clean Cargo build artifacts", status: "pending" },
{ label: "Bundle Galaxy", status: "pending" }, { label: "Bundle Galaxy", status: "pending" },
{ label: "Copy to /Applications", status: "pending" }, { label: "Copy to /Applications", status: "pending" },
{ label: "Launch Galaxy", status: "pending" }, { label: "Launch Galaxy", status: "pending" },
@@ -216,47 +215,42 @@ function App() {
); );
updateStep(1, { status: "done", detail: branch }); updateStep(1, { status: "done", detail: branch });
// ─── Step 2: Clean ────────────────────────────────────────────── // ─── Step 2: Bundle ─────────────────────────────────────────────
updateStep(2, { status: "running" }); updateStep(2, { status: "running" });
await runCommandStreaming("cargo clean", WORKSPACE_ROOT, (line) => appendLog(2, line));
updateStep(2, { status: "done" });
// ─── Step 3: Bundle ─────────────────────────────────────────────
updateStep(3, { status: "running" });
await runCommandStreaming( await runCommandStreaming(
"cargo bundle --release --bin galaxy-oss --package galaxy --features release_bundle,extern_plist", "cargo bundle --release --bin galaxy-oss --package galaxy --features release_bundle,extern_plist",
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(3, line) (line) => appendLog(2, line)
); );
updateStep(3, { status: "done" }); updateStep(2, { status: "done" });
// ─── Step 4: Copy to /Applications ───────────────────────────── // ─── Step 3: Copy to /Applications ─────────────────────────────
updateStep(4, { status: "running" }); updateStep(3, { status: "running" });
if (!fs.existsSync(appPath)) { if (!fs.existsSync(appPath)) {
throw new Error(`Built app not found at ${appPath}`); throw new Error(`Built app not found at ${appPath}`);
} }
await stopApp("Galaxy", (line) => appendLog(4, line)); await stopApp("Galaxy", (line) => appendLog(3, line));
if (fs.existsSync(destPath)) { if (fs.existsSync(destPath)) {
appendLog(4, `Removing existing ${destPath}`); appendLog(3, `Removing existing ${destPath}`);
execSync(`rm -rf "${destPath}"`, { stdio: "pipe" }); execSync(`rm -rf "${destPath}"`, { stdio: "pipe" });
} }
appendLog(4, `Copying ${appPath}${destPath}`); appendLog(3, `Copying ${appPath}${destPath}`);
await runCommandStreaming( await runCommandStreaming(
`ditto "${appPath}" "${destPath}"`, `ditto "${appPath}" "${destPath}"`,
WORKSPACE_ROOT, WORKSPACE_ROOT,
(line) => appendLog(4, line) (line) => appendLog(3, line)
); );
updateStep(4, { status: "done", detail: destPath }); updateStep(3, { status: "done", detail: destPath });
// ─── Step 5: Launch ──────────────────────────────────────────── // ─── Step 4: Launch ────────────────────────────────────────────
updateStep(5, { status: "running" }); updateStep(4, { status: "running" });
execSync(`open "${destPath}"`, { stdio: "ignore" }); execSync(`open "${destPath}"`, { stdio: "ignore" });
updateStep(5, { status: "done" }); updateStep(4, { status: "done" });
} catch (err: any) { } catch (err: any) {
setSteps((prev) => setSteps((prev) =>
prev.map((s) => prev.map((s) =>