git-stack-cli 1.15.1 → 2.0.1-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "1.15.1",
3
+ "version": "2.0.1-beta",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
7
+ "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/magus/git-stack-cli.git"
10
11
  },
11
12
  "type": "module",
12
13
  "bin": {
13
- "git-stack": "dist/cjs/index.cjs"
14
+ "git-stack": "dist/js/index.js"
14
15
  },
15
16
  "files": [
16
- "dist/cjs",
17
+ "dist/js",
18
+ "package.json",
19
+ "pnpm-lock.yaml",
17
20
  "scripts",
18
21
  "src",
19
- "package-lock.json",
20
- "rollup.config.js",
21
22
  "tsconfig.json"
22
23
  ],
23
24
  "scripts": {
24
- "dev": "npm run build -- --watch",
25
- "build": "rollup -c rollup.config.js",
26
- "build:standalone": "GIT_STACK_STANDALONE=true bun run scripts/build-standalone.ts",
27
- "link": "bun run scripts/link.ts",
25
+ "dev": "pnpm run build --watch",
26
+ "build": "bun run scripts/bun-build.ts",
27
+ "compile": "bun run scripts/bun-compile.ts",
28
28
  "release:npm": "bun run scripts/release-npm.ts",
29
29
  "release:github": "bun run scripts/release-github.ts",
30
30
  "release:brew": "bun run scripts/release-brew.ts",
31
- "release": "npm run release:npm && npm run release:github && npm run release:brew",
31
+ "release": "pnpm run release:npm && pnpm run release:github && pnpm run release:brew",
32
32
  "lint:check": "eslint . --cache",
33
- "lint": "npm run lint:check -- --fix",
34
- "prettier:check": "prettier ./src --check --cache",
35
- "prettier": "npm run prettier:check -- --write",
33
+ "lint": "pnpm run lint:check --fix",
34
+ "prettier:check": "prettier src scripts config .eslintrc.cjs --check --cache",
35
+ "prettier": "pnpm run prettier:check --write",
36
36
  "test": "bun test",
37
- "test:watch": "npm run test -- --watch",
37
+ "test:watch": "pnpm run test --watch",
38
38
  "test:types": "tsc",
39
- "test:all": "npm run prettier:check && npm run lint:check && npm run test:types",
39
+ "test:all": "pnpm run prettier:check && pnpm run lint:check && pnpm run test:types",
40
40
  "prepublishOnly": "bun run scripts/npm-prepublishOnly.ts"
41
41
  },
42
42
  "dependencies": {
@@ -52,12 +52,6 @@
52
52
  "zustand": "^4.4.4"
53
53
  },
54
54
  "devDependencies": {
55
- "@rollup/plugin-alias": "^5.1.0",
56
- "@rollup/plugin-commonjs": "^25.0.7",
57
- "@rollup/plugin-json": "^6.1.0",
58
- "@rollup/plugin-node-resolve": "^15.2.3",
59
- "@rollup/plugin-replace": "^5.0.5",
60
- "@rollup/plugin-typescript": "^11.1.6",
61
55
  "@types/chalk": "^2.2.0",
62
56
  "@types/lodash": "^4.17.7",
63
57
  "@types/luxon": "^3.4.2",
@@ -66,14 +60,13 @@
66
60
  "@types/yargs": "^17.0.29",
67
61
  "@typescript-eslint/eslint-plugin": "^6.9.0",
68
62
  "@typescript-eslint/parser": "^6.9.0",
69
- "bun-types": "^1.0.21",
63
+ "bun": "1.1.44",
64
+ "bun-types": "1.1.44",
70
65
  "eslint": "^8.52.0",
71
66
  "eslint-import-resolver-typescript": "^3.6.1",
72
67
  "eslint-plugin-import": "^2.29.0",
73
68
  "eslint-plugin-react": "^7.33.2",
74
- "pkg": "^5.8.1",
75
69
  "prettier": "^3.0.3",
76
- "rollup": "^4.10.0",
77
70
  "typescript": "^5.2.2"
78
71
  }
79
72
  }
@@ -0,0 +1,111 @@
1
+ import * as fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import * as util from "util";
4
+
5
+ import * as file from "~/core/file";
6
+ import { spawn } from "~/core/spawn";
7
+
8
+ const parsed_args = util.parseArgs({
9
+ args: Bun.argv,
10
+ options: {
11
+ watch: {
12
+ type: "boolean",
13
+ default: false,
14
+ },
15
+ verbose: {
16
+ type: "boolean",
17
+ default: false,
18
+ },
19
+ },
20
+ strict: true,
21
+ allowPositionals: true,
22
+ });
23
+
24
+ const WATCH = parsed_args.values.watch;
25
+ const VERBOSE = parsed_args.values.verbose;
26
+
27
+ console.debug("📦 bundle", WATCH ? "watch" : "build");
28
+
29
+ if (VERBOSE) {
30
+ console.debug(parsed_args);
31
+ }
32
+
33
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
34
+
35
+ const PACKAGE_JSON = await file.read_json(path.join(REPO_ROOT, "package.json"));
36
+ const GIT_SEQUENCE_EDITOR_SCRIPT_PATH = path.join(REPO_ROOT, "scripts", "git-sequence-editor.sh");
37
+ const UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT = await file.read_text(GIT_SEQUENCE_EDITOR_SCRIPT_PATH);
38
+ const GIT_SEQUENCE_EDITOR_SCRIPT = UNSAFE_GIT_SEQUENCE_EDITOR_SCRIPT.replace(/`/g, "\\`");
39
+
40
+ let GITIGNORE = (await file.read_text(path.join(REPO_ROOT, ".gitignore"))).split("\n");
41
+ GITIGNORE = GITIGNORE.filter((line) => line.trim() && !line.startsWith("#"));
42
+ GITIGNORE.push(".git");
43
+
44
+ const define = {
45
+ "process.env.NODE_ENV": JSON.stringify("production"),
46
+ "process.env.CLI_VERSION": JSON.stringify(String(PACKAGE_JSON.version)),
47
+ "process.env.GIT_SEQUENCE_EDITOR_SCRIPT": JSON.stringify(GIT_SEQUENCE_EDITOR_SCRIPT),
48
+ };
49
+
50
+ if (VERBOSE) {
51
+ console.debug({ define });
52
+ }
53
+
54
+ async function run_build() {
55
+ const start = Date.now();
56
+
57
+ const result = await Bun.build({
58
+ entrypoints: ["./src/index.tsx"],
59
+ outdir: "./dist/js",
60
+ target: "node",
61
+ env: "inline",
62
+ format: "esm",
63
+ define,
64
+ });
65
+
66
+ const duration_ms = Date.now() - start;
67
+
68
+ console.debug(`✅ Build (${duration_ms}ms)`);
69
+
70
+ if (VERBOSE) {
71
+ console.debug({ result });
72
+ }
73
+ }
74
+
75
+ if (!WATCH) {
76
+ await run_build();
77
+ } else {
78
+ await run_build();
79
+
80
+ console.debug("👀 Watching for changes…");
81
+
82
+ const { signal } = new AbortController();
83
+
84
+ const watcher = fs.watch(REPO_ROOT, { recursive: true, signal });
85
+ for await (const event of watcher) {
86
+ const filename = event.filename;
87
+
88
+ if (!filename) {
89
+ continue;
90
+ }
91
+
92
+ // ignore this file
93
+ if (import.meta.filename == path.join(REPO_ROOT, filename)) {
94
+ continue;
95
+ }
96
+
97
+ // ignore files in gitignore
98
+ const ignored = GITIGNORE.some((pattern) => filename.startsWith(pattern));
99
+ if (ignored) {
100
+ continue;
101
+ }
102
+
103
+ console.debug(`⚠️ Change ${filename}`);
104
+
105
+ if (VERBOSE) {
106
+ console.debug({ ignored, filename, event });
107
+ }
108
+
109
+ await run_build();
110
+ }
111
+ }
@@ -0,0 +1,79 @@
1
+ import path from "node:path";
2
+ import * as util from "util";
3
+
4
+ import * as file from "~/core/file";
5
+ import { spawn } from "~/core/spawn";
6
+
7
+ const parsed_args = util.parseArgs({
8
+ args: Bun.argv,
9
+ options: {
10
+ target: {
11
+ type: "string",
12
+ },
13
+ verbose: {
14
+ type: "boolean",
15
+ default: false,
16
+ },
17
+ },
18
+ strict: true,
19
+ allowPositionals: true,
20
+ });
21
+
22
+ const TARGET = parsed_args.values.target;
23
+ const VERBOSE = parsed_args.values.verbose;
24
+
25
+ console.debug("📦 compile");
26
+
27
+ if (VERBOSE) {
28
+ console.debug(parsed_args);
29
+ }
30
+
31
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
32
+ const DIST_DIR = path.join(REPO_ROOT, "dist");
33
+ const INPUT_JS = path.join(DIST_DIR, "js", "index.js");
34
+
35
+ if (!(await file.exists(INPUT_JS))) {
36
+ console.error(`❌ Missing ${path.relative(REPO_ROOT, INPUT_JS)}`);
37
+ console.debug("Run `pnpm run build` first to generate the input file.");
38
+ process.exit(12);
39
+ }
40
+
41
+ if (TARGET) {
42
+ const target = TARGET;
43
+ await compile_target({ target });
44
+ process.exit(0);
45
+ }
46
+
47
+ const TARGET_LIST = ["bun-linux-x64", "bun-windows-x64", "bun-darwin-arm64", "bun-darwin-x64"];
48
+
49
+ for (const target of TARGET_LIST) {
50
+ await compile_target({ target });
51
+ }
52
+
53
+ type CompileTargetArgs = {
54
+ target: string;
55
+ };
56
+ async function compile_target(args: CompileTargetArgs) {
57
+ const outfile = path.join(DIST_DIR, "bin", `git-stack-${args.target}`);
58
+
59
+ const start = Date.now();
60
+
61
+ // bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp
62
+ const bun_compile = await spawn.sync([
63
+ "bun",
64
+ "build",
65
+ "--compile",
66
+ `--target=${args.target}`,
67
+ INPUT_JS,
68
+ `--outfile=${outfile}`,
69
+ ]);
70
+
71
+ if (bun_compile.proc.exitCode) {
72
+ console.error(bun_compile.stderr);
73
+ process.exit(bun_compile.proc.exitCode);
74
+ }
75
+
76
+ const duration_ms = Date.now() - start;
77
+
78
+ console.debug(`✅ ${path.relative(REPO_ROOT, outfile)} (${duration_ms}ms)`);
79
+ }
@@ -2,7 +2,7 @@
2
2
  // ensure we are publishing through the custom script
3
3
 
4
4
  if (!process.env.GS_RELEASE_NPM) {
5
- console.error("Must publish using `npm run release:npm`");
5
+ console.error("Must publish using `pnpm run release:npm`");
6
6
  console.error();
7
7
  process.exit(10);
8
8
  }
@@ -5,26 +5,19 @@ import * as file from "~/core/file";
5
5
  import { spawn } from "~/core/spawn";
6
6
 
7
7
  // get paths relative to this script
8
- const SCRIPT_DIR = import.meta.dir;
9
- const PROJECT_DIR = path.join(SCRIPT_DIR, "..");
10
- const DIST_DIR = path.join(PROJECT_DIR, "dist");
11
- const STANDALONE_DIR = path.join(DIST_DIR, "standalone");
12
- const HOMEBREW_DIR = path.join(PROJECT_DIR, "homebrew");
13
-
14
- const package_json = await file.read_json(
15
- path.join(PROJECT_DIR, "package.json")
16
- );
8
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
9
+ const DIST_DIR = path.join(REPO_ROOT, "dist");
10
+ const BIN_DIR = path.join(DIST_DIR, "bin");
11
+ const HOMEBREW_DIR = path.join(REPO_ROOT, "homebrew");
12
+
13
+ const package_json = await file.read_json(path.join(REPO_ROOT, "package.json"));
17
14
 
18
15
  const version = package_json.version;
19
16
 
20
17
  process.chdir(HOMEBREW_DIR);
21
18
 
22
19
  // before creating new formula, mv the previous into a versioned formula name
23
- const previous_formula_path = path.join(
24
- HOMEBREW_DIR,
25
- "Formula",
26
- "git-stack.rb"
27
- );
20
+ const previous_formula_path = path.join(HOMEBREW_DIR, "Formula", "git-stack.rb");
28
21
 
29
22
  // match either version format from core or tap formula
30
23
  //
@@ -42,36 +35,29 @@ if (!previous_version_match?.groups) {
42
35
 
43
36
  const previous_version = previous_version_match.groups.version;
44
37
  // convert `1.0.4` to `104`
45
- const not_dot_version = previous_version.replace(/\./g, "");
38
+ const not_dot_version = previous_version.replace(/[^a-z0-9]/gi, "");
46
39
  const previous_class = `GitStackAT${not_dot_version}`;
47
- previous_formula = previous_formula.replace(
48
- "class GitStack",
49
- `class ${previous_class}`
50
- );
40
+ previous_formula = previous_formula.replace("class GitStack", `class ${previous_class}`);
51
41
 
52
42
  await file.write_text(
53
43
  path.join(HOMEBREW_DIR, "Formula", `git-stack@${previous_version}.rb`),
54
- previous_formula
44
+ previous_formula,
55
45
  );
56
46
 
57
- process.chdir(PROJECT_DIR);
47
+ process.chdir(REPO_ROOT);
58
48
 
59
- // download github asset and calculate sha256
60
- // prettier-ignore
61
- await spawn.sync(["gh", "release", "download", version, "-p", `git-stack-cli-${version}.tgz`]);
62
- // prettier-ignore
63
49
  const tarball_asset = await create_asset(`git-stack-cli-${version}.tgz`, { version });
64
- await file.rm(tarball_asset.filepath);
65
50
 
66
- await spawn(`npm run build:standalone`);
51
+ await spawn(`pnpm run compile`);
67
52
 
68
- process.chdir(STANDALONE_DIR);
53
+ process.chdir(BIN_DIR);
69
54
 
70
- const linux_asset = await create_asset("git-stack-cli-linux", { version });
71
- const macos_asset = await create_asset("git-stack-cli-macos", { version });
72
- const win_asset = await create_asset("git-stack-cli-win.exe", { version });
55
+ const linux_x64_asset = await create_asset("git-stack-bun-linux-x64.zip", { version });
56
+ const macos_x64_asset = await create_asset("git-stack-bun-darwin-x64.zip", { version });
57
+ const macos_arm64_asset = await create_asset("git-stack-bun-darwin-arm64.zip", { version });
58
+ const win_x64_asset = await create_asset("git-stack-bun-windows-x64.exe.zip", { version });
73
59
 
74
- console.debug({ linux_asset, macos_asset, win_asset });
60
+ console.debug({ linux_x64_asset, macos_x64_asset, macos_arm64_asset, win_x64_asset });
75
61
 
76
62
  const re_token = (name: string) => new RegExp(`{{ ${name} }}`, "g");
77
63
 
@@ -79,35 +65,27 @@ process.chdir(HOMEBREW_DIR);
79
65
 
80
66
  // homebrew tap formula (binaries)
81
67
 
82
- let tap = await file.read_text(
83
- path.join("templates", "git-stack.tap.rb.template")
84
- );
68
+ let tap = await file.read_text(path.join("templates", "git-stack.tap.rb.template"));
85
69
 
86
70
  tap = tap.replace(re_token("version"), version);
87
- tap = tap.replace(re_token("mac_bin"), macos_asset.filepath);
88
- tap = tap.replace(re_token("mac_sha256"), macos_asset.sha256);
89
- tap = tap.replace(re_token("linux_bin"), linux_asset.filepath);
90
- tap = tap.replace(re_token("linux_sha256"), linux_asset.sha256);
71
+ tap = tap.replace(re_token("mac_x64_bin"), macos_x64_asset.filepath);
72
+ tap = tap.replace(re_token("mac_x64_sha256"), macos_x64_asset.sha256);
73
+ tap = tap.replace(re_token("mac_arm64_bin"), macos_arm64_asset.filepath);
74
+ tap = tap.replace(re_token("mac_arm64_sha256"), macos_arm64_asset.sha256);
75
+ tap = tap.replace(re_token("linux_x64_bin"), linux_x64_asset.filepath);
76
+ tap = tap.replace(re_token("linux_x64_sha256"), linux_x64_asset.sha256);
91
77
 
92
78
  await file.write_text(path.join("Formula", "git-stack.rb"), tap);
93
79
 
94
80
  // homebrew/core formula (build from source)
95
81
 
96
- let core = await file.read_text(
97
- path.join("templates", "git-stack.core.rb.template")
98
- );
82
+ let core = await file.read_text(path.join("templates", "git-stack.core.rb.template"));
99
83
 
100
84
  core = core.replace(re_token("version"), version);
101
85
  core = core.replace(re_token("tarball_sha256"), tarball_asset.sha256);
102
86
 
103
87
  await file.write_text(path.join("Formula", "git-stack.core.rb"), core);
104
88
 
105
- // finally upload the assets to the github release
106
- process.chdir(STANDALONE_DIR);
107
- await spawn.sync(`gh release upload ${version} ${linux_asset.filepath}`);
108
- await spawn.sync(`gh release upload ${version} ${macos_asset.filepath}`);
109
- await spawn.sync(`gh release upload ${version} ${win_asset.filepath}`);
110
-
111
89
  // commit homebrew repo changes
112
90
  process.chdir(HOMEBREW_DIR);
113
91
  await spawn.sync(`git add .`);
@@ -115,7 +93,7 @@ await spawn.sync(`git commit -m ${version}`);
115
93
  await spawn.sync(`git push`);
116
94
 
117
95
  // commmit changes to main repo
118
- process.chdir(PROJECT_DIR);
96
+ process.chdir(REPO_ROOT);
119
97
  // prettier-ignore
120
98
  await spawn.sync(["git", "commit", "-a", "-m", `homebrew-git-stack ${version}`]);
121
99
  await spawn.sync(`git push`);
@@ -5,29 +5,41 @@ import * as file from "~/core/file";
5
5
  import { spawn } from "~/core/spawn";
6
6
 
7
7
  // get paths relative to this script
8
- const SCRIPT_DIR = import.meta.dir;
9
- const PROJECT_DIR = path.join(SCRIPT_DIR, "..");
8
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
9
+ const DIST_DIR = path.join(REPO_ROOT, "dist");
10
+ const BIN_DIR = path.join(DIST_DIR, "bin");
10
11
 
11
- const package_json = await file.read_json(
12
- path.join(PROJECT_DIR, "package.json")
13
- );
12
+ const package_json = await file.read_json(path.join(REPO_ROOT, "package.json"));
14
13
 
15
14
  const version = package_json.version;
16
15
 
17
16
  // generates local tarball e.g. git-stack-cli-1.2.0.tgz
18
- await spawn("npm pack");
17
+ await spawn("pnpm pack");
19
18
 
20
- // prettier-ignore
21
- const tarball_asset = await create_asset(`git-stack-cli-${version}.tgz`, { version });
19
+ // generate single file executables for all targets
20
+ await spawn(`pnpm run compile`);
22
21
 
23
22
  await spawn.sync(`gh release create ${version} -t ${version} --generate-notes`);
24
23
 
24
+ const tarball_asset = await create_asset(`git-stack-cli-${version}.tgz`, { version });
25
25
  await spawn.sync(`gh release upload ${version} ${tarball_asset.filepath}`);
26
26
 
27
- await file.rm(tarball_asset.filepath);
27
+ process.chdir(BIN_DIR);
28
+
29
+ await zip_upload("git-stack-bun-darwin-arm64");
30
+ await zip_upload("git-stack-bun-darwin-x64");
31
+ await zip_upload("git-stack-bun-linux-x64");
32
+ await zip_upload("git-stack-bun-windows-x64.exe");
28
33
 
29
34
  console.debug();
30
35
  console.debug("✅", "published", version);
31
36
  console.debug();
32
37
  console.debug("https://github.com/magus/git-stack-cli/releases");
33
38
  console.debug();
39
+
40
+ async function zip_upload(filepath: string) {
41
+ const zip_filepath = `${filepath}.zip`;
42
+ await spawn.sync(`zip -r ${zip_filepath} ${filepath}`);
43
+ const asset = await create_asset(zip_filepath, { version });
44
+ await spawn.sync(`gh release upload ${version} ${asset.filepath}`);
45
+ }
@@ -7,15 +7,14 @@ import { spawn } from "~/core/spawn";
7
7
  process.env.NODE_ENV = "production";
8
8
 
9
9
  // get paths relative to this script
10
- const SCRIPT_DIR = import.meta.dir;
11
- const PROJECT_DIR = path.join(SCRIPT_DIR, "..");
12
- const DIST_DIR = path.join(PROJECT_DIR, "dist");
10
+ const REPO_ROOT = (await spawn.sync("git rev-parse --show-toplevel")).stdout;
11
+ const DIST_DIR = path.join(REPO_ROOT, "dist");
13
12
 
14
13
  // clear entire dist output directory
15
14
  await fs.rmdir(DIST_DIR, { recursive: true });
16
15
  await fs.mkdir(DIST_DIR, { recursive: true });
17
16
 
18
- process.chdir(PROJECT_DIR);
17
+ process.chdir(REPO_ROOT);
19
18
 
20
19
  // require clean git status besides changes to package.json version
21
20
  const git_status = await spawn.sync("git status --porcelain");
@@ -24,9 +23,7 @@ if (!/^M\s+package.json/.test(git_status.stdout)) {
24
23
  process.exit(4);
25
24
  }
26
25
 
27
- const package_json = await file.read_json(
28
- path.join(PROJECT_DIR, "package.json")
29
- );
26
+ const package_json = await file.read_json(path.join(REPO_ROOT, "package.json"));
30
27
 
31
28
  const version = package_json.version;
32
29
 
@@ -40,11 +37,11 @@ if (git_tag.stdout) {
40
37
  }
41
38
 
42
39
  // install all dependencies even though we are NODE_ENV=production
43
- await spawn(`npm install --production=false`);
40
+ await spawn(`pnpm install --frozen-lockfile --production=false`);
44
41
 
45
- await spawn(`npm run test:all`);
42
+ await spawn(`pnpm run test:all`);
46
43
 
47
- await spawn(`npm run build`);
44
+ await spawn(`pnpm run build`);
48
45
 
49
46
  // confirm all files specified exist
50
47
  for (const filepath of package_json.files) {
@@ -61,7 +58,7 @@ console.info("Publishing to NPM requires a one-time password");
61
58
  const otp = await input("Enter OTP: ");
62
59
  await spawn(["npm", "publish", `--otp=${otp}`]);
63
60
 
64
- process.chdir(PROJECT_DIR);
61
+ process.chdir(REPO_ROOT);
65
62
 
66
63
  await spawn.sync(`git commit -a -m ${version}`);
67
64
  await spawn.sync(`git push`);
package/src/app/Store.tsx CHANGED
@@ -33,6 +33,11 @@ type SyncGithubState = {
33
33
  // async function that returns exit code
34
34
  type AbortHandler = () => Promise<number>;
35
35
 
36
+ type ExitArgs = {
37
+ quiet?: boolean;
38
+ clear?: boolean;
39
+ };
40
+
36
41
  export type State = {
37
42
  // set immediately in `index.tsx` so no `null` scenario
38
43
  process_argv: Array<string>;
@@ -53,7 +58,7 @@ export type State = {
53
58
  sync_github: null | SyncGithubState;
54
59
  is_dirty_check_stash: boolean;
55
60
  abort_handler: null | AbortHandler;
56
- is_exiting: boolean;
61
+ exit_mode: null | "normal" | "quiet";
57
62
 
58
63
  step:
59
64
  | "github-api-error"
@@ -74,7 +79,7 @@ export type State = {
74
79
  pr: { [branch: string]: PullRequest };
75
80
 
76
81
  actions: {
77
- exit(code: number, clear?: boolean): void;
82
+ exit(code: number, args?: ExitArgs): void;
78
83
  clear(): void;
79
84
  unmount(): void;
80
85
  newline(): void;
@@ -124,7 +129,7 @@ const BaseStore = createStore<State>()(
124
129
  sync_github: null,
125
130
  is_dirty_check_stash: false,
126
131
  abort_handler: null,
127
- is_exiting: false,
132
+ exit_mode: null,
128
133
 
129
134
  step: "loading",
130
135
 
@@ -134,9 +139,15 @@ const BaseStore = createStore<State>()(
134
139
  pr: {},
135
140
 
136
141
  actions: {
137
- exit(code, clear = true) {
142
+ exit(code, args) {
138
143
  set((state) => {
139
- state.is_exiting = true;
144
+ if (args?.quiet ?? code === 0) {
145
+ state.exit_mode = "quiet";
146
+ } else {
147
+ state.exit_mode = "normal";
148
+ }
149
+
150
+ let clear = args?.clear ?? true;
140
151
 
141
152
  const node = <Exit clear={clear} code={code} />;
142
153
  state.mutate.output(state, { node });
@@ -118,4 +118,6 @@ async function run() {
118
118
  actions.output(<Ink.Text color={colors.green}>✅ Changes restored from stash</Ink.Text>);
119
119
  }
120
120
  }
121
+
122
+ actions.exit(0);
121
123
  }
@@ -65,4 +65,6 @@ async function run(args: Args) {
65
65
  const result = await cli(command);
66
66
 
67
67
  actions.output(result.stdout);
68
+
69
+ actions.exit(0);
68
70
  }
@@ -11,17 +11,27 @@ type Props = {
11
11
  };
12
12
 
13
13
  export function ExitingGate(props: Props) {
14
- const is_exiting = Store.useState((state) => state.is_exiting);
14
+ const exit_mode = Store.useState((state) => state.exit_mode);
15
15
 
16
- if (!is_exiting) {
16
+ if (!exit_mode) {
17
17
  return props.children;
18
18
  }
19
19
 
20
- return (
21
- <Ink.Box flexDirection="column">
22
- <Ink.Text color={colors.red}>
23
- <FormatText message="🚨 Exiting…" />
24
- </Ink.Text>
25
- </Ink.Box>
26
- );
20
+ switch (exit_mode) {
21
+ case "quiet":
22
+ return null;
23
+
24
+ case "normal":
25
+ return (
26
+ <Ink.Box flexDirection="column">
27
+ <Ink.Text color={colors.red}>
28
+ <FormatText message="🚨 Exiting…" />
29
+ </Ink.Text>
30
+ </Ink.Box>
31
+ );
32
+
33
+ default:
34
+ exit_mode satisfies never;
35
+ return null;
36
+ }
27
37
  }
@@ -112,7 +112,9 @@ GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
112
112
  const tmp_git_sequence_editor_path = path.join(os.tmpdir(), "git-sequence-editor.sh");
113
113
 
114
114
  // replaced at build time with literal contents of `scripts/git-sequence-editor.sh`
115
- const GIT_SEQUENCE_EDITOR_SCRIPT = `process.env.GIT_SEQUENCE_EDITOR_SCRIPT`;
115
+ const GIT_SEQUENCE_EDITOR_SCRIPT = process.env.GIT_SEQUENCE_EDITOR_SCRIPT;
116
+
117
+ invariant(GIT_SEQUENCE_EDITOR_SCRIPT, "GIT_SEQUENCE_EDITOR_SCRIPT must exist");
116
118
 
117
119
  // write script to temporary path
118
120
  await fs.writeFile(tmp_git_sequence_editor_path, GIT_SEQUENCE_EDITOR_SCRIPT);
@@ -131,10 +133,10 @@ GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
131
133
  `revise --edit -i ${args.rebase_merge_base}`,
132
134
  ];
133
135
 
134
- // ignore here is important to prevent scrollback clear
135
- // change to pipe to see output temporarily
136
+ // `ignore` hdies output which helps prevent scrollback clear
137
+ // `pipe` helps see failures when git revise fails
136
138
  // https://github.com/magus/git-stack-cli/commit/f9f10e3ac3cd9a35ee75d3e0851a48391967a23f
137
- await cli(command, { stdio: ["ignore", "ignore", "ignore"] });
139
+ await cli(command, { stdio: ["pipe", "pipe", "pipe"] });
138
140
 
139
141
  // cleanup tmp_git_sequence_editor_path
140
142
  await safe_rm(tmp_git_sequence_editor_path);
package/src/index.tsx CHANGED
@@ -15,6 +15,10 @@ import { pretty_json } from "~/core/pretty_json";
15
15
  try {
16
16
  const argv = await command();
17
17
 
18
+ // required to get bun working with ink
19
+ // https://github.com/oven-sh/bun/issues/6862#issuecomment-2429444852
20
+ process.stdin.resume();
21
+
18
22
  process.on("uncaughtException", (error) => {
19
23
  console.error("🚨 uncaughtException");
20
24
  console.error(error);