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
Harvmaster 6c29a7a0cc Update readme 2026-09-08 17:35:59 +00:00
4 changed files with 48 additions and 22 deletions
+14 -1
View File
@@ -81,13 +81,26 @@ then the TUI restarts.
Updates can also be checked or installed directly:
# TODO
Fix restart required to switch wallets
Add invitation import from file to tui
Document Node24
Document xo-cli commands when not gloabally installed
Remove debug text in app.ts
investigate `xo-cli mnemonic list` not showing mnemonics saved from tui
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?)
Improve updater so it can run on dev mode
```bash
xo-cli update --check
xo-cli update
```
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
+10 -6
View File
@@ -3,7 +3,9 @@ import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
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;
export interface CommandResult {
@@ -99,8 +101,8 @@ export class GitUpdateService {
const fetchResult = await this.git([
"fetch",
"--quiet",
UPDATE_REMOTE,
`+refs/heads/${branch}:refs/remotes/${UPDATE_REMOTE}/${branch}`,
UPDATE_REMOTE_URL,
`+refs/heads/${branch}:refs/remotes/${UPDATE_REF_NAMESPACE}/${branch}`,
]);
if (fetchResult.code !== 0) {
return {
@@ -111,13 +113,13 @@ export class GitUpdateService {
const [headResult, targetResult, dirtyResult] = await Promise.all([
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"]),
]);
if (headResult.code !== 0 || targetResult.code !== 0) {
return {
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 {
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,
"--to",
request.targetCommit,
"--target-ref",
`refs/remotes/${UPDATE_REF_NAMESPACE}/${request.branch}`,
"--parent",
String(process.pid),
];
+17 -9
View File
@@ -9,6 +9,7 @@ interface HelperOptions {
branch: string;
from: string;
to: string;
targetRef: string;
parent: number;
restart: boolean;
}
@@ -34,14 +35,25 @@ function parseOptions(args: string[]): HelperOptions {
const branch = values.get("--branch");
const from = values.get("--from");
const to = values.get("--to");
const targetRef = values.get("--target-ref");
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.");
}
if (!/^[0-9a-f]{40,64}$/u.test(from) || !/^[0-9a-f]{40,64}$/u.test(to)) {
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> {
@@ -129,11 +141,7 @@ async function main(): Promise<void> {
const [branch, head, target, status] = await Promise.all([
capture("git", ["branch", "--show-current"], options.repo),
capture("git", ["rev-parse", "HEAD"], options.repo),
capture(
"git",
["rev-parse", `refs/remotes/origin/${options.branch}`],
options.repo,
),
capture("git", ["rev-parse", options.targetRef], options.repo),
capture("git", ["status", "--porcelain"], options.repo),
]);
if (
@@ -151,7 +159,7 @@ async function main(): Promise<void> {
await run("git", ["merge", "--ff-only", options.to], options.repo);
updated = true;
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.");
} catch (error) {
console.error("Update failed; restoring the previous revision.");
@@ -159,7 +167,7 @@ async function main(): Promise<void> {
try {
await run("git", ["reset", "--hard", options.from], 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.");
} catch (rollbackError) {
console.error("Automatic rollback also failed:", rollbackError);
+7 -6
View File
@@ -3,6 +3,7 @@ import { describe, expect, test } from "vitest";
import {
GitUpdateService,
UPDATE_REMOTE_URL,
type CommandResult,
type CommandRunner,
} from "../../src/updater/git-update-service.js";
@@ -24,11 +25,11 @@ describe("GitUpdateService", () => {
new Map([
["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(),
],
["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 merge-base --is-ancestor aaa bbb", result()],
["git log -1 --format=%s bbb", result("Useful update\n")],
@@ -52,11 +53,11 @@ describe("GitUpdateService", () => {
new Map([
["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(),
],
["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 merge-base --is-ancestor aaa bbb", result()],
["git log -1 --format=%s bbb", result("Update\n")],
@@ -74,11 +75,11 @@ describe("GitUpdateService", () => {
new Map([
["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(),
],
["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 merge-base --is-ancestor bbb aaa", result("", 1)],
["git merge-base --is-ancestor aaa bbb", result()],