@omega.js/desktop 0.51.0 → 0.52.0

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.
@@ -78,8 +78,8 @@ module.exports = defineCases({
78
78
  jetpack.remove(realish); // a RED run of this case is what would create it
79
79
  try {
80
80
  // Every verb that CHANGES the machine — stop kills real listeners under
81
- // the home, self-update runs a global npm install.
82
- for (const sub of ['install', 'config', 'register-org', 'start', 'restart', 'stop', 'uninstall', 'self-update']) {
81
+ // the home.
82
+ for (const sub of ['install', 'config', 'register-org', 'start', 'restart', 'stop', 'uninstall']) {
83
83
  let threw;
84
84
  try {
85
85
  // `_env: {}` is a fixture for the config walk. It is NOT an answer
@@ -1720,6 +1720,9 @@ module.exports = defineCases({
1720
1720
  const logged = [];
1721
1721
  const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
1722
1722
  try {
1723
+ // Installed and current, so `start` goes straight to its spawns (#937).
1724
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
1725
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
1723
1726
  let threw;
1724
1727
  try {
1725
1728
  await runner({ _: ['runner', 'start'], _home: home, _env: {}, _logger: logger, _prompt: {}, _interactive: false });
@@ -1814,6 +1817,9 @@ module.exports = defineCases({
1814
1817
  const spawned = [];
1815
1818
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
1816
1819
  try {
1820
+ // Installed and current, so `start` goes straight to its spawns (#937).
1821
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
1822
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
1817
1823
  for (const org of orgs) jetpack.dir(path.join(home, `actions-runner-${org}`));
1818
1824
  for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
1819
1825
 
@@ -1863,6 +1869,9 @@ module.exports = defineCases({
1863
1869
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
1864
1870
  const aliveDir = path.join(home, 'actions-runner-acme');
1865
1871
  try {
1872
+ // Installed and current, so `start` goes straight to its spawns (#937).
1873
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
1874
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
1866
1875
  for (const org of orgs) jetpack.dir(path.join(home, `actions-runner-${org}`));
1867
1876
  for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
1868
1877
 
@@ -1907,6 +1916,9 @@ module.exports = defineCases({
1907
1916
  const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
1908
1917
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
1909
1918
  try {
1919
+ // Installed and current, so `start` goes straight to its spawns (#937).
1920
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
1921
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
1910
1922
  for (const org of orgs) jetpack.dir(path.join(home, `actions-runner-${org}`));
1911
1923
  for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
1912
1924
 
@@ -1949,6 +1961,9 @@ module.exports = defineCases({
1949
1961
  const spawned = [];
1950
1962
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
1951
1963
  try {
1964
+ // Installed and current, so `start` goes straight to its spawns (#937).
1965
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
1966
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
1952
1967
  jetpack.dir(path.join(home, 'actions-runner-acme'));
1953
1968
  jetpack.write(shortcut, '@echo off\r\n');
1954
1969
 
@@ -2043,6 +2058,9 @@ module.exports = defineCases({
2043
2058
  const waits = [];
2044
2059
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2045
2060
  try {
2061
+ // Installed and current, so `start` goes straight to its spawns (#937).
2062
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
2063
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
2046
2064
  jetpack.dir(path.join(home, 'actions-runner-acme'));
2047
2065
  jetpack.write(shortcut, '@echo off\r\n');
2048
2066
 
@@ -2092,6 +2110,9 @@ module.exports = defineCases({
2092
2110
  const waits = [];
2093
2111
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2094
2112
  try {
2113
+ // Installed and current, so `start` goes straight to its spawns (#937).
2114
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
2115
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
2095
2116
  jetpack.dir(path.join(home, 'actions-runner-acme'));
2096
2117
  jetpack.write(shortcut, '@echo off\r\n');
2097
2118
 
@@ -2130,7 +2151,7 @@ module.exports = defineCases({
2130
2151
  ctx.expect(startSrc.includes("stdio: 'inherit'")).toBe(false);
2131
2152
  ctx.expect(startSrc.includes('spawnSync')).toBe(false);
2132
2153
  // ...and install hands off to it rather than foregrounding one org.
2133
- ctx.expect(src).toContain('await startServices({ ...options, _home: home })');
2154
+ ctx.expect(src).toContain('await startServices({ ...options, _home: home, _reconcileDone: true })');
2134
2155
 
2135
2156
  // The spawn's own options, pinned the same way: the listener is handed
2136
2157
  // the runner's private HOME ([#807](https://github.com/Omega-JS-Stack/omega/issues/807))
@@ -2166,6 +2187,9 @@ module.exports = defineCases({
2166
2187
  const spawned = [];
2167
2188
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2168
2189
  try {
2190
+ // Installed and current, so `start` goes straight to its spawns (#937).
2191
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
2192
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
2169
2193
  for (const org of orgs) jetpack.dir(path.join(home, `actions-runner-${org}`));
2170
2194
  for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
2171
2195
 
@@ -2218,6 +2242,9 @@ module.exports = defineCases({
2218
2242
  const spawned = [];
2219
2243
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2220
2244
  try {
2245
+ // Installed and current, so `start` goes straight to its spawns (#937).
2246
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
2247
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
2221
2248
  jetpack.dir(path.join(home, 'actions-runner-acme'));
2222
2249
  jetpack.write(shortcut, '@echo off\r\n');
2223
2250
 
@@ -2585,6 +2612,9 @@ module.exports = defineCases({
2585
2612
  const logger = { log: (m) => logged.push(String(m)), warn: (m) => logged.push(String(m)), error: (m) => logged.push(String(m)) };
2586
2613
  const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2587
2614
  try {
2615
+ // Installed and current, so `start` goes straight to its spawns (#937).
2616
+ jetpack.write(path.join(home, '_template', 'config.cmd'), '@echo off\r\n');
2617
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION });
2588
2618
  for (const org of orgs) jetpack.write(path.join(home, `actions-runner-${org}`, 'config.cmd'), '@echo off\r\n');
2589
2619
  for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
2590
2620
  ctx.expect(jetpack.exists(path.join(home, 'home'))).toBe(false);
@@ -2618,5 +2648,582 @@ module.exports = defineCases({
2618
2648
  }
2619
2649
  },
2620
2650
  },
2651
+ {
2652
+ name: '`start` on a bare box installs: a home with no `_template/config.cmd` hands the whole run to install, once',
2653
+ run: async (ctx) => {
2654
+ // `start` is the one command (#937). A box with nothing installed gets
2655
+ // the install, which itself ends by bringing every org online, so
2656
+ // nothing else of `start` runs: no config walk of its own, no refresh,
2657
+ // no org check, no spawn.
2658
+ const fs = require('fs');
2659
+ const os = require('os');
2660
+ const jetpack = require('fs-jetpack');
2661
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2662
+
2663
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2664
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2665
+ process.env.OMEGA_RUNNER_FORCE = '1';
2666
+ const logged = [];
2667
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2668
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2669
+ const installs = [];
2670
+ const spawned = [];
2671
+ let downloads = 0;
2672
+ let discovers = 0;
2673
+ let registers = 0;
2674
+ try {
2675
+ const options = {
2676
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2677
+ _listeners: () => [],
2678
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 1 }; },
2679
+ _install: async (o) => { installs.push(o); },
2680
+ _download: async () => { downloads++; },
2681
+ _discoverOrgs: async () => { discovers++; return []; },
2682
+ _registerOrg: async () => { registers++; },
2683
+ };
2684
+ await runner(options);
2685
+
2686
+ ctx.expect(installs.length).toBe(1);
2687
+ ctx.expect(installs[0]).toBe(options); // the same options, untouched
2688
+ ctx.expect(spawned).toEqual([]);
2689
+ ctx.expect(downloads).toBe(0);
2690
+ ctx.expect(discovers).toBe(0);
2691
+ ctx.expect(registers).toBe(0);
2692
+ ctx.expect(jetpack.exists(path.join(home, '.env'))).toBe(false); // start's own config walk never ran
2693
+ } finally {
2694
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2695
+ jetpack.remove(home);
2696
+ }
2697
+ },
2698
+ },
2699
+ {
2700
+ name: '`start` refreshes a stale actions/runner in place: the template is re-downloaded and copied over each stopped org, its registration kept',
2701
+ run: async (ctx) => {
2702
+ // A bumped ACTIONS_RUNNER_VERSION used to mean `install`, which tears
2703
+ // the box down. The template is re-downloaded and laid over each org dir
2704
+ // instead; `.runner`, `.credentials` and `.env` are the registration and
2705
+ // the template never carries them, so they come through byte-identical.
2706
+ const fs = require('fs');
2707
+ const os = require('os');
2708
+ const jetpack = require('fs-jetpack');
2709
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2710
+
2711
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2712
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2713
+ const origExit = process.exitCode;
2714
+ process.env.OMEGA_RUNNER_FORCE = '1';
2715
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
2716
+ const shortcut = path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}acme.cmd`);
2717
+ const logged = [];
2718
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2719
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2720
+ const templateDir = path.join(home, '_template');
2721
+ const orgDir = path.join(home, 'actions-runner-acme');
2722
+ const downloads = [];
2723
+ const spawned = [];
2724
+ // The registration files, exactly as the preflight leaves `.env`, so a
2725
+ // byte change can only have come from the copy.
2726
+ const kept = {
2727
+ '.runner': '{"agentName":"omega-runner-box-acme"}',
2728
+ '.credentials': '{"scheme":"OAuth"}',
2729
+ '.env': `https_proxy=http://proxy\nHOME=${path.join(home, 'home')}\nACTIONS_RUNNER_HOOK_JOB_STARTED=${path.join(home, 'job-started.js')}\n`,
2730
+ };
2731
+ try {
2732
+ jetpack.write(path.join(templateDir, 'config.cmd'), 'old config');
2733
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: '2.300.0', registeredOrgs: ['acme'] });
2734
+ jetpack.write(path.join(orgDir, 'config.cmd'), 'old config');
2735
+ jetpack.write(path.join(orgDir, 'bin', 'Runner.Listener.exe'), 'old listener');
2736
+ for (const [name, content] of Object.entries(kept)) jetpack.write(path.join(orgDir, name), content);
2737
+ jetpack.write(shortcut, '@echo off\r\n');
2738
+
2739
+ await runner({
2740
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2741
+ _listeners: () => [],
2742
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 5150 }; },
2743
+ _download: async (dir, h) => {
2744
+ downloads.push([dir, h]);
2745
+ jetpack.write(path.join(dir, 'config.cmd'), 'new config');
2746
+ jetpack.write(path.join(dir, 'bin', 'Runner.Listener.exe'), 'new listener');
2747
+ },
2748
+ _discoverOrgs: async () => ['acme'],
2749
+ _registerOrg: async () => { throw new Error('nothing to register'); },
2750
+ });
2751
+
2752
+ ctx.expect(downloads).toEqual([[templateDir, home]]);
2753
+ ctx.expect(fs.readFileSync(path.join(orgDir, 'config.cmd'), 'utf8')).toBe('new config');
2754
+ ctx.expect(fs.readFileSync(path.join(orgDir, 'bin', 'Runner.Listener.exe'), 'utf8')).toBe('new listener');
2755
+ for (const [name, content] of Object.entries(kept)) {
2756
+ ctx.expect(fs.readFileSync(path.join(orgDir, name), 'utf8')).toBe(content);
2757
+ }
2758
+ ctx.expect(jetpack.read(path.join(home, 'config.json'), 'json').actionsRunnerVersion).toBe(runner.ACTIONS_RUNNER_VERSION);
2759
+ ctx.expect(spawned).toEqual([orgDir]); // and the org still comes online
2760
+ ctx.expect(process.exitCode).toBe(origExit);
2761
+ } finally {
2762
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2763
+ process.exitCode = origExit;
2764
+ jetpack.remove(shortcut);
2765
+ jetpack.remove(home);
2766
+ }
2767
+ },
2768
+ },
2769
+ {
2770
+ name: '`start` never refreshes an org whose listener is alive: its binaries are locked, so it names `restart` and the version stays unrecorded',
2771
+ run: async (ctx) => {
2772
+ // A live listener holds its dir's binaries open. Copying over them is a
2773
+ // half-updated runner, so that org is skipped with the command that
2774
+ // stops it first, and the box is not called refreshed until every org
2775
+ // dir took the new version.
2776
+ const fs = require('fs');
2777
+ const os = require('os');
2778
+ const jetpack = require('fs-jetpack');
2779
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2780
+
2781
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2782
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2783
+ const origExit = process.exitCode;
2784
+ process.env.OMEGA_RUNNER_FORCE = '1';
2785
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
2786
+ const orgs = ['acme', 'zeta'];
2787
+ const shortcuts = orgs.map((org) => path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}${org}.cmd`));
2788
+ const logged = [];
2789
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2790
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2791
+ const templateDir = path.join(home, '_template');
2792
+ const aliveDir = path.join(home, 'actions-runner-acme');
2793
+ const stoppedDir = path.join(home, 'actions-runner-zeta');
2794
+ try {
2795
+ jetpack.write(path.join(templateDir, 'config.cmd'), 'old config');
2796
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: '2.300.0', registeredOrgs: orgs });
2797
+ for (const dir of [aliveDir, stoppedDir]) jetpack.write(path.join(dir, 'config.cmd'), 'old config');
2798
+ for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
2799
+
2800
+ await runner({
2801
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2802
+ _listeners: (dir) => (dir === aliveDir ? [{ pid: 4242, sessionId: 1, execPath: path.join(aliveDir, 'bin', 'Runner.Listener.exe') }] : []),
2803
+ _spawn: () => ({ ok: true, pid: 6000 }),
2804
+ _download: async (dir) => { jetpack.write(path.join(dir, 'config.cmd'), 'new config'); },
2805
+ _discoverOrgs: async () => orgs,
2806
+ _registerOrg: async () => { throw new Error('nothing to register'); },
2807
+ });
2808
+
2809
+ ctx.expect(fs.readFileSync(path.join(aliveDir, 'config.cmd'), 'utf8')).toBe('old config'); // untouched
2810
+ ctx.expect(fs.readFileSync(path.join(stoppedDir, 'config.cmd'), 'utf8')).toBe('new config'); // the stopped one took it
2811
+ const skip = logged.map((l) => l[1]).filter((l) => l.includes('npx omega runner restart'));
2812
+ ctx.expect(skip.length).toBe(1);
2813
+ ctx.expect(skip[0]).toContain('acme');
2814
+ ctx.expect(jetpack.read(path.join(home, 'config.json'), 'json').actionsRunnerVersion).toBe('2.300.0');
2815
+ } finally {
2816
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2817
+ process.exitCode = origExit;
2818
+ for (const file of shortcuts) jetpack.remove(file);
2819
+ jetpack.remove(home);
2820
+ }
2821
+ },
2822
+ },
2823
+ {
2824
+ name: '`start` downloads nothing when the recorded actions/runner version is the pinned one',
2825
+ run: async (ctx) => {
2826
+ // The refresh is for a stale box only: a current one never pays for a
2827
+ // 150 MB download on every start.
2828
+ const fs = require('fs');
2829
+ const os = require('os');
2830
+ const jetpack = require('fs-jetpack');
2831
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2832
+
2833
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2834
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2835
+ const origExit = process.exitCode;
2836
+ process.env.OMEGA_RUNNER_FORCE = '1';
2837
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
2838
+ const shortcut = path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}acme.cmd`);
2839
+ const logged = [];
2840
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2841
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2842
+ const orgDir = path.join(home, 'actions-runner-acme');
2843
+ const spawned = [];
2844
+ let downloads = 0;
2845
+ try {
2846
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'config');
2847
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION, registeredOrgs: ['acme'] });
2848
+ jetpack.write(path.join(orgDir, 'config.cmd'), 'config');
2849
+ jetpack.write(shortcut, '@echo off\r\n');
2850
+
2851
+ await runner({
2852
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2853
+ _listeners: () => [],
2854
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 7 }; },
2855
+ _download: async () => { downloads++; },
2856
+ _discoverOrgs: async () => ['acme'],
2857
+ _registerOrg: async () => { throw new Error('nothing to register'); },
2858
+ });
2859
+
2860
+ ctx.expect(downloads).toBe(0);
2861
+ ctx.expect(spawned).toEqual([orgDir]);
2862
+ } finally {
2863
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2864
+ process.exitCode = origExit;
2865
+ jetpack.remove(shortcut);
2866
+ jetpack.remove(home);
2867
+ }
2868
+ },
2869
+ },
2870
+ {
2871
+ name: '`start` registers an admin org the box does not serve yet, honouring OMEGA_RUNNER_ORGS, and nothing already registered',
2872
+ run: async (ctx) => {
2873
+ // A new admin org used to need `install` again. `start` registers the
2874
+ // missing one itself: named by the filter, not yet in registeredOrgs.
2875
+ const fs = require('fs');
2876
+ const os = require('os');
2877
+ const jetpack = require('fs-jetpack');
2878
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2879
+
2880
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2881
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2882
+ const origExit = process.exitCode;
2883
+ process.env.OMEGA_RUNNER_FORCE = '1';
2884
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
2885
+ const shortcut = path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}acme.cmd`);
2886
+ const logged = [];
2887
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2888
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2889
+ const registered = [];
2890
+ try {
2891
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'config');
2892
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION, registeredOrgs: ['acme'] });
2893
+ jetpack.write(path.join(home, 'actions-runner-acme', 'config.cmd'), 'config');
2894
+ jetpack.write(shortcut, '@echo off\r\n');
2895
+
2896
+ await runner({
2897
+ _: ['runner', 'start'], _home: home, _env: { ...filled, OMEGA_RUNNER_ORGS: 'acme, Beta' }, _logger: logger, _interactive: false, _prompt: {},
2898
+ _listeners: () => [],
2899
+ _spawn: () => ({ ok: true, pid: 8 }),
2900
+ _download: async () => { throw new Error('the version matches: no download'); },
2901
+ _discoverOrgs: async () => ['acme', 'beta', 'gamma'],
2902
+ _registerOrg: async (o) => { registered.push({ org: o._[2], home: o._home }); },
2903
+ });
2904
+
2905
+ // beta is named and new; acme is already served; gamma is not named.
2906
+ ctx.expect(registered).toEqual([{ org: 'beta', home }]);
2907
+ ctx.expect(process.exitCode).toBe(origExit);
2908
+ } finally {
2909
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2910
+ process.exitCode = origExit;
2911
+ jetpack.remove(shortcut);
2912
+ jetpack.remove(home);
2913
+ }
2914
+ },
2915
+ },
2916
+ {
2917
+ name: '`start` works offline: an org discovery that throws is ONE skip line, and every registered org still comes up',
2918
+ run: async (ctx) => {
2919
+ // The org check is the only part of `start` that needs GitHub. A box
2920
+ // with no network still gets its listeners.
2921
+ const fs = require('fs');
2922
+ const os = require('os');
2923
+ const jetpack = require('fs-jetpack');
2924
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2925
+
2926
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2927
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2928
+ const origExit = process.exitCode;
2929
+ process.env.OMEGA_RUNNER_FORCE = '1';
2930
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
2931
+ const orgs = ['acme', 'zeta'];
2932
+ const shortcuts = orgs.map((org) => path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}${org}.cmd`));
2933
+ const logged = [];
2934
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2935
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2936
+ const spawned = [];
2937
+ let registers = 0;
2938
+ try {
2939
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'config');
2940
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION, registeredOrgs: orgs });
2941
+ for (const org of orgs) jetpack.write(path.join(home, `actions-runner-${org}`, 'config.cmd'), 'config');
2942
+ for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
2943
+
2944
+ await runner({
2945
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2946
+ _listeners: () => [],
2947
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 9 }; },
2948
+ _download: async () => { throw new Error('the version matches: no download'); },
2949
+ _discoverOrgs: async () => { throw new Error('getaddrinfo ENOTFOUND api.github.com'); },
2950
+ _registerOrg: async () => { registers++; },
2951
+ });
2952
+
2953
+ const skips = logged.map((l) => l[1]).filter((l) => /org check skipped/i.test(l));
2954
+ ctx.expect(skips.length).toBe(1);
2955
+ ctx.expect(skips[0]).toContain('ENOTFOUND');
2956
+ ctx.expect(registers).toBe(0);
2957
+ ctx.expect(spawned).toEqual(orgs.map((org) => path.join(home, `actions-runner-${org}`)));
2958
+ ctx.expect(process.exitCode).toBe(origExit);
2959
+ } finally {
2960
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
2961
+ process.exitCode = origExit;
2962
+ for (const file of shortcuts) jetpack.remove(file);
2963
+ jetpack.remove(home);
2964
+ }
2965
+ },
2966
+ },
2967
+ {
2968
+ name: '`start` in a test run never asks GitHub unless both GitHub seams are injected',
2969
+ run: async (ctx) => {
2970
+ // Registration is not home-scoped: a real `registerOrg` with a real
2971
+ // token registers the org on GitHub whatever scratch home the case
2972
+ // made. A test run without `_discoverOrgs` AND `_registerOrg` skips
2973
+ // the org check on one line, the way `uninstall` skips the surfaces no
2974
+ // path redirects.
2975
+ const fs = require('fs');
2976
+ const os = require('os');
2977
+ const jetpack = require('fs-jetpack');
2978
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
2979
+
2980
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
2981
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
2982
+ const origExit = process.exitCode;
2983
+ process.env.OMEGA_RUNNER_FORCE = '1';
2984
+ const logged = [];
2985
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
2986
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
2987
+ let discovers = 0;
2988
+ try {
2989
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'config');
2990
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: runner.ACTIONS_RUNNER_VERSION, registeredOrgs: [] });
2991
+
2992
+ await runner({
2993
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
2994
+ _listeners: () => [],
2995
+ _spawn: () => ({ ok: true, pid: 10 }),
2996
+ _discoverOrgs: async () => { discovers++; return ['acme']; }, // no _registerOrg
2997
+ });
2998
+
2999
+ ctx.expect(discovers).toBe(0);
3000
+ const skips = logged.map((l) => l[1]).filter((l) => /org check skipped/i.test(l));
3001
+ ctx.expect(skips.length).toBe(1);
3002
+ ctx.expect(skips[0]).toContain('_registerOrg');
3003
+ } finally {
3004
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
3005
+ process.exitCode = origExit;
3006
+ jetpack.remove(home);
3007
+ }
3008
+ },
3009
+ },
3010
+ {
3011
+ name: '`self-update` is gone: the dispatcher calls it an unknown subcommand',
3012
+ run: async (ctx) => {
3013
+ // Every runner verb runs as `npx omega runner <verb>` from the box's
3014
+ // clone, so there is no global package for it to update (#937). The
3015
+ // home is one the test guard refuses, so no build of this command,
3016
+ // however old, can run a global npm install from this case.
3017
+ const os = require('os');
3018
+ const jetpack = require('fs-jetpack');
3019
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
3020
+
3021
+ const realish = path.join(os.homedir(), 'omega-runner-NOT-A-SCRATCH');
3022
+ let threw;
3023
+ try {
3024
+ await runner({ _: ['runner', 'self-update'], _home: realish });
3025
+ } catch (e) { threw = e; }
3026
+ ctx.expect(threw).toBeDefined();
3027
+ ctx.expect(threw.message).toBe('Unknown runner subcommand: self-update');
3028
+ ctx.expect(jetpack.exists(realish)).toBe(false);
3029
+ },
3030
+ },
3031
+ {
3032
+ name: '`start` called from install (`_reconcileDone`) skips the reconcile: no download and no org check, even on a stale version with a missing org',
3033
+ run: async (ctx) => {
3034
+ // install has just downloaded the template and registered every org, so
3035
+ // its closing start has nothing to reconcile, and a retry of an org that
3036
+ // failed there would print its error a second time.
3037
+ const fs = require('fs');
3038
+ const os = require('os');
3039
+ const jetpack = require('fs-jetpack');
3040
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
3041
+
3042
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
3043
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
3044
+ const origExit = process.exitCode;
3045
+ process.env.OMEGA_RUNNER_FORCE = '1';
3046
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
3047
+ const shortcut = path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}acme.cmd`);
3048
+ const logged = [];
3049
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
3050
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
3051
+ const orgDir = path.join(home, 'actions-runner-acme');
3052
+ const spawned = [];
3053
+ let downloads = 0;
3054
+ let discovers = 0;
3055
+ let registers = 0;
3056
+ try {
3057
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'config');
3058
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: '2.300.0', registeredOrgs: ['acme'] });
3059
+ jetpack.write(path.join(orgDir, 'config.cmd'), 'config');
3060
+ jetpack.write(shortcut, '@echo off\r\n');
3061
+
3062
+ await runner({
3063
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
3064
+ _reconcileDone: true,
3065
+ _listeners: () => [],
3066
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 11 }; },
3067
+ _download: async () => { downloads++; },
3068
+ _discoverOrgs: async () => { discovers++; return ['acme', 'beta']; },
3069
+ _registerOrg: async () => { registers++; },
3070
+ });
3071
+
3072
+ ctx.expect(downloads).toBe(0);
3073
+ ctx.expect(discovers).toBe(0);
3074
+ ctx.expect(registers).toBe(0);
3075
+ ctx.expect(jetpack.read(path.join(home, 'config.json'), 'json').actionsRunnerVersion).toBe('2.300.0');
3076
+ ctx.expect(spawned).toEqual([orgDir]); // the spawns still run
3077
+ } finally {
3078
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
3079
+ process.exitCode = origExit;
3080
+ jetpack.remove(shortcut);
3081
+ jetpack.remove(home);
3082
+ }
3083
+ },
3084
+ },
3085
+ {
3086
+ name: '`start` survives a refresh download that fails: one warn, no version recorded, every org still comes up',
3087
+ run: async (ctx) => {
3088
+ // A stale box with no network must still come online: the refresh is
3089
+ // skipped until the next start, never the whole run.
3090
+ const fs = require('fs');
3091
+ const os = require('os');
3092
+ const jetpack = require('fs-jetpack');
3093
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
3094
+
3095
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
3096
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
3097
+ const origExit = process.exitCode;
3098
+ process.env.OMEGA_RUNNER_FORCE = '1';
3099
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
3100
+ const orgs = ['acme', 'zeta'];
3101
+ const shortcuts = orgs.map((org) => path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}${org}.cmd`));
3102
+ const logged = [];
3103
+ const logger = { log: (m) => logged.push(['log', String(m)]), warn: (m) => logged.push(['warn', String(m)]), error: (m) => logged.push(['error', String(m)]) };
3104
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
3105
+ const spawned = [];
3106
+ try {
3107
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'old config');
3108
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: '2.300.0', registeredOrgs: orgs });
3109
+ for (const org of orgs) jetpack.write(path.join(home, `actions-runner-${org}`, 'config.cmd'), 'old config');
3110
+ for (const file of shortcuts) jetpack.write(file, '@echo off\r\n');
3111
+
3112
+ await runner({
3113
+ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
3114
+ _listeners: () => [],
3115
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 12 }; },
3116
+ _download: async () => { throw new Error('curl exit 6'); },
3117
+ _discoverOrgs: async () => orgs,
3118
+ _registerOrg: async () => { throw new Error('nothing to register'); },
3119
+ });
3120
+
3121
+ const warns = logged.filter((l) => l[0] === 'warn' && l[1].includes('curl exit 6'));
3122
+ ctx.expect(warns.length).toBe(1);
3123
+ ctx.expect(jetpack.read(path.join(home, 'config.json'), 'json').actionsRunnerVersion).toBe('2.300.0');
3124
+ for (const org of orgs) ctx.expect(fs.readFileSync(path.join(home, `actions-runner-${org}`, 'config.cmd'), 'utf8')).toBe('old config');
3125
+ ctx.expect(spawned).toEqual(orgs.map((org) => path.join(home, `actions-runner-${org}`)));
3126
+ ctx.expect(process.exitCode).toBe(origExit);
3127
+ } finally {
3128
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
3129
+ process.exitCode = origExit;
3130
+ for (const file of shortcuts) jetpack.remove(file);
3131
+ jetpack.remove(home);
3132
+ }
3133
+ },
3134
+ },
3135
+ {
3136
+ name: '`start` on a bare box in a test run refuses without an injected `_install`, and installs nothing',
3137
+ run: async (ctx) => {
3138
+ // The real install downloads, discovers and registers against GitHub.
3139
+ // A test run reaches it only through the seam, the way deregistration
3140
+ // refuses without its own.
3141
+ const fs = require('fs');
3142
+ const os = require('os');
3143
+ const jetpack = require('fs-jetpack');
3144
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
3145
+
3146
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
3147
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
3148
+ process.env.OMEGA_RUNNER_FORCE = '1';
3149
+ const logger = { log: () => {}, warn: () => {}, error: () => {} };
3150
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
3151
+ try {
3152
+ let threw;
3153
+ try {
3154
+ await runner({ _: ['runner', 'start'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {}, _listeners: () => [], _spawn: () => ({ ok: true, pid: 13 }) });
3155
+ } catch (e) { threw = e; }
3156
+ ctx.expect(threw).toBeDefined();
3157
+ ctx.expect(threw.message).toContain('_install');
3158
+ ctx.expect(jetpack.exists(path.join(home, '_template'))).toBe(false);
3159
+ ctx.expect(jetpack.exists(path.join(home, '.env'))).toBe(false);
3160
+ } finally {
3161
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
3162
+ jetpack.remove(home);
3163
+ }
3164
+ },
3165
+ },
3166
+ {
3167
+ name: '`restart` refreshes a stale actions/runner AFTER its stop, once the listener is gone, and records the version',
3168
+ run: async (ctx) => {
3169
+ // The reconcile is not gated on restart's preflight: after the stop is
3170
+ // exactly when an org's binaries are free to be replaced.
3171
+ const fs = require('fs');
3172
+ const os = require('os');
3173
+ const jetpack = require('fs-jetpack');
3174
+ const runner = require(path.join(__dirname, '..', '..', '..', 'commands', 'runner.js'));
3175
+
3176
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'omega-runner-home-'));
3177
+ const origForce = process.env.OMEGA_RUNNER_FORCE;
3178
+ const origExit = process.exitCode;
3179
+ process.env.OMEGA_RUNNER_FORCE = '1';
3180
+ const hostPrefix = `omega-runner-${os.hostname().toLowerCase()}-`;
3181
+ const shortcut = path.join(SCRATCH_STARTUP_DIR, `${hostPrefix}acme.cmd`);
3182
+ const order = [];
3183
+ const logger = {
3184
+ log: (m) => { if (String(m).includes('already stopped')) order.push('stop'); },
3185
+ warn: () => {},
3186
+ error: () => {},
3187
+ };
3188
+ const filled = { GH_TOKEN: 'ghp_from_the_box', WIN_EV_TOKEN_PATH: 'a'.repeat(40), WIN_CSC_KEY_PASSWORD: '1234', SIGNTOOL_PATH: 'C:/sdk/signtool.exe' };
3189
+ const orgDir = path.join(home, 'actions-runner-acme');
3190
+ const spawned = [];
3191
+ try {
3192
+ jetpack.write(path.join(home, '_template', 'config.cmd'), 'old config');
3193
+ jetpack.write(path.join(home, 'config.json'), { actionsRunnerVersion: '2.300.0', registeredOrgs: ['acme'] });
3194
+ jetpack.write(path.join(orgDir, 'config.cmd'), 'old config');
3195
+ jetpack.write(shortcut, '@echo off\r\n');
3196
+
3197
+ // Alive for the first two polls, gone after.
3198
+ let looks = 0;
3199
+ await runner({
3200
+ _: ['runner', 'restart'], _home: home, _env: filled, _logger: logger, _interactive: false, _prompt: {},
3201
+ _delay: async () => {},
3202
+ _listeners: () => {
3203
+ const alive = ++looks <= 2;
3204
+ order.push(alive ? 'alive' : 'gone');
3205
+ return alive ? [{ pid: 909, sessionId: 1, execPath: path.join(orgDir, 'bin', 'Runner.Listener.exe') }] : [];
3206
+ },
3207
+ _spawn: (dir) => { spawned.push(dir); return { ok: true, pid: 14 }; },
3208
+ _download: async (dir) => { order.push('download'); jetpack.write(path.join(dir, 'config.cmd'), 'new config'); },
3209
+ _discoverOrgs: async () => ['acme'],
3210
+ _registerOrg: async () => { throw new Error('nothing to register'); },
3211
+ });
3212
+
3213
+ ctx.expect(order.indexOf('stop')).toBeGreaterThan(-1);
3214
+ ctx.expect(order.indexOf('download')).toBeGreaterThan(order.indexOf('stop'));
3215
+ ctx.expect(order.indexOf('download')).toBeGreaterThan(order.lastIndexOf('alive'));
3216
+ ctx.expect(fs.readFileSync(path.join(orgDir, 'config.cmd'), 'utf8')).toBe('new config');
3217
+ ctx.expect(jetpack.read(path.join(home, 'config.json'), 'json').actionsRunnerVersion).toBe(runner.ACTIONS_RUNNER_VERSION);
3218
+ ctx.expect(spawned).toEqual([orgDir]);
3219
+ ctx.expect(process.exitCode).toBe(origExit);
3220
+ } finally {
3221
+ if (origForce === undefined) delete process.env.OMEGA_RUNNER_FORCE; else process.env.OMEGA_RUNNER_FORCE = origForce;
3222
+ process.exitCode = origExit;
3223
+ jetpack.remove(shortcut);
3224
+ jetpack.remove(home);
3225
+ }
3226
+ },
3227
+ },
2621
3228
  ],
2622
3229
  });