4 Commits
Author SHA1 Message Date
Harvmaster 0152be5df6 Update readme 2026-09-08 17:44:18 +00:00
Harvmaster 2dba6d3b72 Build unsafe 2026-09-08 17:43:49 +00:00
Harvmaster be2a5bcb1a Update readme 2026-09-08 17:41:27 +00:00
Harvmaster 973fd9fa93 Constant for repo url 2026-09-08 17:41:27 +00:00
4 changed files with 37 additions and 22 deletions
+3 -1
View File
@@ -91,6 +91,7 @@ Remove debug text in app.ts
investigate `xo-cli mnemonic list` not showing mnemonics saved from tui investigate `xo-cli mnemonic list` not showing mnemonics saved from tui
Add sats into the send tx page or something to make it nicer Add sats into the send tx page or something to make it nicer
Throw error for 0 change output (not sure how I handle fees?) Throw error for 0 change output (not sure how I handle fees?)
Improve updater so it can run on dev mode
```bash ```bash
xo-cli update --check xo-cli update --check
@@ -98,7 +99,8 @@ xo-cli update
``` ```
Automatic updates require a clean working tree and a fast-forward update from Automatic updates require a clean working tree and a fast-forward update from
`origin`. Local changes are never stashed or discarded automatically. the HTTPS update repository. Local changes are never stashed or discarded
automatically.
## TODO ## TODO
+10 -6
View File
@@ -3,7 +3,9 @@ import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path"; import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
const UPDATE_REMOTE = "origin"; export const UPDATE_REMOTE_URL =
"https://git.harvmaster.com/Harvmaster/xo-cli.git";
const UPDATE_REF_NAMESPACE = "xo-updater";
const COMMAND_TIMEOUT_MS = 10_000; const COMMAND_TIMEOUT_MS = 10_000;
export interface CommandResult { export interface CommandResult {
@@ -99,8 +101,8 @@ export class GitUpdateService {
const fetchResult = await this.git([ const fetchResult = await this.git([
"fetch", "fetch",
"--quiet", "--quiet",
UPDATE_REMOTE, UPDATE_REMOTE_URL,
`+refs/heads/${branch}:refs/remotes/${UPDATE_REMOTE}/${branch}`, `+refs/heads/${branch}:refs/remotes/${UPDATE_REF_NAMESPACE}/${branch}`,
]); ]);
if (fetchResult.code !== 0) { if (fetchResult.code !== 0) {
return { return {
@@ -111,13 +113,13 @@ export class GitUpdateService {
const [headResult, targetResult, dirtyResult] = await Promise.all([ const [headResult, targetResult, dirtyResult] = await Promise.all([
this.git(["rev-parse", "HEAD"]), this.git(["rev-parse", "HEAD"]),
this.git(["rev-parse", `refs/remotes/${UPDATE_REMOTE}/${branch}`]), this.git(["rev-parse", `refs/remotes/${UPDATE_REF_NAMESPACE}/${branch}`]),
this.git(["status", "--porcelain"]), this.git(["status", "--porcelain"]),
]); ]);
if (headResult.code !== 0 || targetResult.code !== 0) { if (headResult.code !== 0 || targetResult.code !== 0) {
return { return {
status: "unavailable", status: "unavailable",
reason: `The branch ${branch} is not available on ${UPDATE_REMOTE}.`, reason: `The branch ${branch} is not available at the update repository.`,
}; };
} }
@@ -145,7 +147,7 @@ export class GitUpdateService {
} }
return { return {
status: "unavailable", status: "unavailable",
reason: `Local branch ${branch} has diverged from ${UPDATE_REMOTE}/${branch}; update it manually.`, reason: `Local branch ${branch} has diverged from the update repository; update it manually.`,
}; };
} }
@@ -191,6 +193,8 @@ export class GitUpdateService {
request.currentCommit, request.currentCommit,
"--to", "--to",
request.targetCommit, request.targetCommit,
"--target-ref",
`refs/remotes/${UPDATE_REF_NAMESPACE}/${request.branch}`,
"--parent", "--parent",
String(process.pid), String(process.pid),
]; ];
+17 -9
View File
@@ -9,6 +9,7 @@ interface HelperOptions {
branch: string; branch: string;
from: string; from: string;
to: string; to: string;
targetRef: string;
parent: number; parent: number;
restart: boolean; restart: boolean;
} }
@@ -34,14 +35,25 @@ function parseOptions(args: string[]): HelperOptions {
const branch = values.get("--branch"); const branch = values.get("--branch");
const from = values.get("--from"); const from = values.get("--from");
const to = values.get("--to"); const to = values.get("--to");
const targetRef = values.get("--target-ref");
const parent = Number(values.get("--parent")); const parent = Number(values.get("--parent"));
if (!repo || !branch || !from || !to || !Number.isInteger(parent)) { if (
!repo ||
!branch ||
!from ||
!to ||
!targetRef ||
!Number.isInteger(parent)
) {
throw new Error("Missing updater helper arguments."); throw new Error("Missing updater helper arguments.");
} }
if (!/^[0-9a-f]{40,64}$/u.test(from) || !/^[0-9a-f]{40,64}$/u.test(to)) { if (!/^[0-9a-f]{40,64}$/u.test(from) || !/^[0-9a-f]{40,64}$/u.test(to)) {
throw new Error("Invalid updater commit identifiers."); throw new Error("Invalid updater commit identifiers.");
} }
return { repo, branch, from, to, parent, restart }; if (targetRef !== `refs/remotes/xo-updater/${branch}`) {
throw new Error("Invalid updater target reference.");
}
return { repo, branch, from, to, targetRef, parent, restart };
} }
function run(command: string, args: string[], cwd: string): Promise<void> { function run(command: string, args: string[], cwd: string): Promise<void> {
@@ -129,11 +141,7 @@ async function main(): Promise<void> {
const [branch, head, target, status] = await Promise.all([ const [branch, head, target, status] = await Promise.all([
capture("git", ["branch", "--show-current"], options.repo), capture("git", ["branch", "--show-current"], options.repo),
capture("git", ["rev-parse", "HEAD"], options.repo), capture("git", ["rev-parse", "HEAD"], options.repo),
capture( capture("git", ["rev-parse", options.targetRef], options.repo),
"git",
["rev-parse", `refs/remotes/origin/${options.branch}`],
options.repo,
),
capture("git", ["status", "--porcelain"], options.repo), capture("git", ["status", "--porcelain"], options.repo),
]); ]);
if ( if (
@@ -151,7 +159,7 @@ async function main(): Promise<void> {
await run("git", ["merge", "--ff-only", options.to], options.repo); await run("git", ["merge", "--ff-only", options.to], options.repo);
updated = true; updated = true;
await run("npm", ["ci"], options.repo); await run("npm", ["ci"], options.repo);
await run("npm", ["run", "build"], options.repo); await run("npm", ["run", "build:unsafe"], options.repo);
console.log("XO was updated successfully."); console.log("XO was updated successfully.");
} catch (error) { } catch (error) {
console.error("Update failed; restoring the previous revision."); console.error("Update failed; restoring the previous revision.");
@@ -159,7 +167,7 @@ async function main(): Promise<void> {
try { try {
await run("git", ["reset", "--hard", options.from], options.repo); await run("git", ["reset", "--hard", options.from], options.repo);
await run("npm", ["ci"], options.repo); await run("npm", ["ci"], options.repo);
await run("npm", ["run", "build"], options.repo); await run("npm", ["run", "build:unsafe"], options.repo);
console.error("The previous revision was restored."); console.error("The previous revision was restored.");
} catch (rollbackError) { } catch (rollbackError) {
console.error("Automatic rollback also failed:", rollbackError); console.error("Automatic rollback also failed:", rollbackError);
+7 -6
View File
@@ -3,6 +3,7 @@ import { describe, expect, test } from "vitest";
import { import {
GitUpdateService, GitUpdateService,
UPDATE_REMOTE_URL,
type CommandResult, type CommandResult,
type CommandRunner, type CommandRunner,
} from "../../src/updater/git-update-service.js"; } from "../../src/updater/git-update-service.js";
@@ -24,11 +25,11 @@ describe("GitUpdateService", () => {
new Map([ new Map([
["git branch --show-current", result("feature/demo\n")], ["git branch --show-current", result("feature/demo\n")],
[ [
"git fetch --quiet origin +refs/heads/feature/demo:refs/remotes/origin/feature/demo", `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/feature/demo:refs/remotes/xo-updater/feature/demo`,
result(), result(),
], ],
["git rev-parse HEAD", result("aaa\n")], ["git rev-parse HEAD", result("aaa\n")],
["git rev-parse refs/remotes/origin/feature/demo", result("bbb\n")], ["git rev-parse refs/remotes/xo-updater/feature/demo", result("bbb\n")],
["git status --porcelain", result()], ["git status --porcelain", result()],
["git merge-base --is-ancestor aaa bbb", result()], ["git merge-base --is-ancestor aaa bbb", result()],
["git log -1 --format=%s bbb", result("Useful update\n")], ["git log -1 --format=%s bbb", result("Useful update\n")],
@@ -52,11 +53,11 @@ describe("GitUpdateService", () => {
new Map([ new Map([
["git branch --show-current", result("main\n")], ["git branch --show-current", result("main\n")],
[ [
"git fetch --quiet origin +refs/heads/main:refs/remotes/origin/main", `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/main:refs/remotes/xo-updater/main`,
result(), result(),
], ],
["git rev-parse HEAD", result("aaa\n")], ["git rev-parse HEAD", result("aaa\n")],
["git rev-parse refs/remotes/origin/main", result("bbb\n")], ["git rev-parse refs/remotes/xo-updater/main", result("bbb\n")],
["git status --porcelain", result(" M src/example.ts\n")], ["git status --porcelain", result(" M src/example.ts\n")],
["git merge-base --is-ancestor aaa bbb", result()], ["git merge-base --is-ancestor aaa bbb", result()],
["git log -1 --format=%s bbb", result("Update\n")], ["git log -1 --format=%s bbb", result("Update\n")],
@@ -74,11 +75,11 @@ describe("GitUpdateService", () => {
new Map([ new Map([
["git branch --show-current", result("main\n")], ["git branch --show-current", result("main\n")],
[ [
"git fetch --quiet origin +refs/heads/main:refs/remotes/origin/main", `git fetch --quiet ${UPDATE_REMOTE_URL} +refs/heads/main:refs/remotes/xo-updater/main`,
result(), result(),
], ],
["git rev-parse HEAD", result("bbb\n")], ["git rev-parse HEAD", result("bbb\n")],
["git rev-parse refs/remotes/origin/main", result("aaa\n")], ["git rev-parse refs/remotes/xo-updater/main", result("aaa\n")],
["git status --porcelain", result()], ["git status --porcelain", result()],
["git merge-base --is-ancestor bbb aaa", result("", 1)], ["git merge-base --is-ancestor bbb aaa", result("", 1)],
["git merge-base --is-ancestor aaa bbb", result()], ["git merge-base --is-ancestor aaa bbb", result()],