@pixlcore/xycli 1.0.2 → 1.0.3

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.
@@ -0,0 +1,39 @@
1
+ name: Offline Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ test:
12
+ name: ${{ matrix.os }} / Node.js ${{ matrix.node }}
13
+ runs-on: ${{ matrix.os }}
14
+
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os:
19
+ - ubuntu-latest
20
+ - windows-latest
21
+ node:
22
+ - 20.19.0
23
+ - 24
24
+
25
+ steps:
26
+ - name: Check out repository
27
+ uses: actions/checkout@v7
28
+
29
+ - name: Use Node.js ${{ matrix.node }}
30
+ uses: actions/setup-node@v7
31
+ with:
32
+ node-version: ${{ matrix.node }}
33
+ cache: npm
34
+
35
+ - name: Install dependencies
36
+ run: npm ci
37
+
38
+ - name: Run offline tests
39
+ run: npm run test:offline
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # xyCLI Changelog
2
2
 
3
+ ## Version v1.0.2
4
+
5
+ > September 20, 2026
6
+
7
+ - [`7b9023b`](https://github.com/pixlcore/xycli/commit/7b9023b390ea45b0615ff43b4d5a8d3daa3a68f3): Version 1.0.2
8
+ - [`2114287`](https://github.com/pixlcore/xycli/commit/2114287dc98ca2cedcc6cf1b15eb7e164f697851): Feature: Sync Setup: Write out events and workflows into category sub-folders. Fixes #2.
9
+ - [`3f3e8a5`](https://github.com/pixlcore/xycli/commit/3f3e8a515be030e551c33a701c43e2bb3ff72569): Doc: Add two-way git tutorial
10
+ - [`4336dc2`](https://github.com/pixlcore/xycli/commit/4336dc25924616038213b77c5892990c09fa22f5): Feature: Sync setup now supports its own "--down_cmd" for running a custom command if and only if files were written out.
11
+ - [`7b94e63`](https://github.com/pixlcore/xycli/commit/7b94e632e2c0d86eaef5211325b259195cb30f3c): Feature: Add optional "--new" flag to sync setup, to add newly added items not already in sync set
12
+ - [`90d065f`](https://github.com/pixlcore/xycli/commit/90d065f941578ed83783cc79bebbbdc7018a6664): Allow "--default_ext" switch to override default ".txt" file extension for neighbor property files when language cannot be detected. Ref #1.
13
+
14
+ ## Version v1.0.1
15
+
16
+ > September 20, 2026
17
+
18
+ - [`ee12585`](https://github.com/pixlcore/xycli/commit/ee12585b36e7237d8514b9c34ce0411debf16494): Version 1.0.1
19
+ - [`c18090a`](https://github.com/pixlcore/xycli/commit/c18090a03ebdb91772fc565b59eec22e4378df3f): Fix: Do not start delete pass if any warnings or errors occurred during the sync.
20
+ - [`ef4e68b`](https://github.com/pixlcore/xycli/commit/ef4e68b229b8e0114c0af4a44dc1b61ba81e9e39): Test: Add more event unit tests
21
+
3
22
  ## Version v1.0.0
4
23
 
5
24
  > September 20, 2026
package/README.md CHANGED
@@ -49,7 +49,7 @@ Save your xyOps URL and API Key:
49
49
  xy config --base_url https://xyops.example.com --api_key YOUR_API_KEY
50
50
  ```
51
51
 
52
- Use the base URL of your xyOps instance, without the `/api` endpoint suffix. The command creates `~/.config/xyops/cli.json` and saves the settings there.
52
+ Use the base URL of your xyOps instance, without the `/api` endpoint suffix. On Unix and macOS, the command creates `~/.config/xyops/cli.json`. On Windows, it creates `%USERPROFILE%\.config\xyops\cli.json`.
53
53
 
54
54
  Now check your connection:
55
55
 
@@ -63,11 +63,11 @@ You should see the dashboard. If you need to set up xyOps itself, start with its
63
63
 
64
64
  xyCLI loads optional JSON configuration files on every invocation, then applies environment variables. Settings are merged in this order:
65
65
 
66
- | Order | Source | Purpose |
67
- | --- | --- | --- |
68
- | 1 | `/etc/xyops/cli.json` | Shared settings for the machine. |
69
- | 2 | `~/.config/xyops/cli.json` | Settings for your user account, overriding matching system settings. |
70
- | 3 | `XYOPS_` environment variables | Overrides for the current shell or command. |
66
+ | Order | Unix and macOS | Windows | Purpose |
67
+ | --- | --- | --- | --- |
68
+ | 1 | `/etc/xyops/cli.json` | `%PROGRAMDATA%\xyops\cli.json` | Shared settings for the machine. |
69
+ | 2 | `~/.config/xyops/cli.json` | `%USERPROFILE%\.config\xyops\cli.json` | Settings for your user account, overriding matching system settings. |
70
+ | 3 | `XYOPS_` environment variables | `XYOPS_` environment variables | Overrides for the current shell or command. |
71
71
 
72
72
  Neither file is required if you supply your connection settings through the environment. Missing files are skipped; files you create must contain valid JSON. Matching top-level properties are replaced by later sources, so a user-level object such as `sync` replaces the system-level object of the same name.
73
73
 
@@ -92,7 +92,7 @@ xy config --suggest false
92
92
  xy config --color false
93
93
  ```
94
94
 
95
- `xy config` masks your API Key when displaying it. Updates load the **user configuration file** separately, apply only the settings specified on the command line, and save it with owner-only read/write permissions. Other user settings are preserved; settings inherited from the system file or environment are not copied into the user file. If the user file does not exist, it is created with only the settings you supply. Environment overrides still take precedence on the next invocation.
95
+ `xy config` masks your API Key when displaying it. Updates load the **user configuration file** separately, apply only the settings specified on the command line, and save it with owner-only read/write permissions on Unix and macOS. On Windows, the file inherits the access controls of your user profile because Windows does not implement Unix permission modes. Other user settings are preserved; settings inherited from the system file or environment are not copied into the user file. If the user file does not exist, it is created with only the settings you supply. Environment overrides still take precedence on the next invocation.
96
96
 
97
97
  Every `XYOPS_` variable maps to a lowercase configuration key. For example, `XYOPS_BASE_URL` sets `base_url` and `XYOPS_ITEMS_PER_PAGE` sets `items_per_page`. Literal `true` and `false` become booleans, and numeric strings become numbers.
98
98
 
@@ -104,6 +104,14 @@ export XYOPS_API_KEY="YOUR_API_KEY"
104
104
  xy
105
105
  ```
106
106
 
107
+ In Windows PowerShell, use the equivalent environment syntax:
108
+
109
+ ```powershell
110
+ $env:XYOPS_BASE_URL = "https://xyops.example.com"
111
+ $env:XYOPS_API_KEY = "YOUR_API_KEY"
112
+ xy
113
+ ```
114
+
107
115
  Or override a display setting for one command:
108
116
 
109
117
  ```sh
package/docs/help.md CHANGED
@@ -258,7 +258,7 @@ Every sync and setup command uses a built-in host-local PID lock keyed by `base_
258
258
 
259
259
  **Saved defaults**
260
260
 
261
- Put sync defaults under a `sync` object in `/etc/xyops/cli.json` or `~/.config/xyops/cli.json`:
261
+ Put sync defaults under a `sync` object in the machine or user configuration file. These are `/etc/xyops/cli.json` and `~/.config/xyops/cli.json` on Unix and macOS, or `%PROGRAMDATA%\xyops\cli.json` and `%USERPROFILE%\.config\xyops\cli.json` on Windows:
262
262
 
263
263
  ```json
264
264
  {
@@ -341,7 +341,7 @@ Type `.exit` or hit `Ctrl-C` to exit.
341
341
 
342
342
  ## config
343
343
 
344
- View the effective CLI configuration or save one or more user settings. API keys are masked when displayed, and updated values are written to `~/.config/xyops/cli.json`.
344
+ View the effective CLI configuration or save one or more user settings. API keys are masked when displayed. Updated values are written to `~/.config/xyops/cli.json` on Unix and macOS, or `%USERPROFILE%\.config\xyops\cli.json` on Windows.
345
345
 
346
346
  ```sh
347
347
  xy config
@@ -353,7 +353,7 @@ xy config --base_url http://localhost:5522 --api_key YOUR_API_KEY
353
353
 
354
354
  Updates load the user file separately and apply only the settings specified on the command line, preserving other user settings. System settings and environment overrides are not copied into the file. If the user file does not exist, it is created with only the supplied settings.
355
355
 
356
- Configuration loads `/etc/xyops/cli.json` first, then `~/.config/xyops/cli.json`, then `XYOPS_`-prefixed environment variables. Later sources override matching top-level settings. Environment overrides still take precedence over saved user settings. Common keys include `api_key`, `base_url`, `temp_dir`, `color`, `suggest`, `items_per_page`, `cache_ttl`, `raw`, and `invisible`.
356
+ Configuration loads the machine file first, then the user file, then `XYOPS_`-prefixed environment variables. The machine and user files are `/etc/xyops/cli.json` and `~/.config/xyops/cli.json` on Unix and macOS, or `%PROGRAMDATA%\xyops\cli.json` and `%USERPROFILE%\.config\xyops\cli.json` on Windows. Later sources override matching top-level settings. Environment overrides still take precedence over saved user settings. Common keys include `api_key`, `base_url`, `temp_dir`, `color`, `suggest`, `items_per_page`, `cache_ttl`, `raw`, and `invisible`.
357
357
 
358
358
  ## doc
359
359
 
package/docs/sync.md CHANGED
@@ -308,7 +308,7 @@ After changes begin, individual API or write failures are logged and the engine
308
308
 
309
309
  ### Save defaults in a CLI config file
310
310
 
311
- Put a `sync` object in `/etc/xyops/cli.json` for machine or service defaults, or `~/.config/xyops/cli.json` for your own user account:
311
+ Put a `sync` object in the machine configuration file for machine or service defaults, or the user configuration file for your own account. These are `/etc/xyops/cli.json` and `~/.config/xyops/cli.json` on Unix and macOS, or `%PROGRAMDATA%\xyops\cli.json` and `%USERPROFILE%\.config\xyops\cli.json` on Windows:
312
312
 
313
313
  ```json
314
314
  {
package/lib/config.js CHANGED
@@ -1,21 +1,37 @@
1
1
  // Config Layer
2
2
 
3
3
  const fs = require('fs');
4
+ const os = require('os');
4
5
  const Path = require('path');
5
6
  const cli = require('pixl-cli');
6
7
  const Tools = cli.Tools;
7
8
  const chalk = cli.chalk;
8
9
 
9
- module.exports = {
10
+ function getConfigFiles(platform, env, home_dir) {
11
+ // Keep the existing Unix paths, but use Windows's shared application-data
12
+ // directory instead of resolving /etc against whichever drive is current.
13
+ // ProgramData should always exist in an ordinary Windows login. If a custom
14
+ // environment omits it, skip the optional machine file rather than guessing.
15
+ var path_api = (platform == 'win32') ? Path.win32 : Path.posix;
16
+ var config_files = [];
17
+ var system_dir = (platform == 'win32') ?
18
+ (env.ProgramData || env.PROGRAMDATA || env.ALLUSERSPROFILE) : '/etc';
19
+
20
+ if (system_dir) config_files.push( path_api.join(system_dir, 'xyops', 'cli.json') );
21
+
22
+ // os.homedir() uses USERPROFILE on Windows and has an operating-system
23
+ // fallback, unlike reading HOME or HOMEPATH directly.
24
+ config_files.push( path_api.join(home_dir, '.config', 'xyops', 'cli.json') );
25
+ return config_files;
26
+ }
27
+
28
+ var config_layer = module.exports = {
10
29
 
11
30
  loadConfig() {
12
31
  // handle loading config at startup
13
32
  // optional config files, sync with env, two-way
14
33
  var config = {};
15
- var config_files = this.configFiles = [
16
- Path.join( '/etc', 'xyops', 'cli.json' ),
17
- Path.join( process.env.HOME, '.config', 'xyops', 'cli.json' )
18
- ];
34
+ var config_files = this.configFiles = getConfigFiles(process.platform, process.env, os.homedir());
19
35
 
20
36
  config_files.forEach( function(file) {
21
37
  if (!fs.existsSync(file)) return;
@@ -80,7 +96,13 @@ module.exports = {
80
96
  Tools.mkdirpSync( Path.dirname(dest_file) );
81
97
  println( "\n " + bold.cyan("Writing to: ") + dest_file );
82
98
  fs.writeFileSync( dest_file, JSON.stringify(user_config, null, "\t") + "\n", { mode: 0o600 } );
83
- fs.chmodSync(dest_file, 0o600);
99
+
100
+ // Windows inherits an ACL from the user's profile directory and does not
101
+ // implement Unix owner/group/other mode distinctions.
102
+ if (process.platform != 'win32') fs.chmodSync(dest_file, 0o600);
84
103
  }
85
-
86
104
  };
105
+
106
+ // Expose the pure path selector to unit tests without merging another command
107
+ // method into the runtime app object.
108
+ Object.defineProperty(config_layer, 'getConfigFiles', { value: getConfigFiles });
package/lib/sync.js CHANGED
@@ -651,7 +651,8 @@ module.exports = {
651
651
  cwd: cwd,
652
652
  timeout: cmd_timeout * 1000,
653
653
  encoding: 'utf8',
654
- stdio: ['ignore', 'pipe', 'pipe']
654
+ stdio: ['ignore', 'pipe', 'pipe'],
655
+ windowsHide: true
655
656
  });
656
657
 
657
658
  if (output.match(/\S/)) {
package/lib/utils.js CHANGED
@@ -254,7 +254,21 @@ module.exports = {
254
254
  // This mirrors the xyOps UI helper, while avoiding slow source auto-detection
255
255
  // for common interpreters.
256
256
  if ((typeof(bin) != 'string') || !bin.trim()) return null;
257
- var cmd = Path.basename( bin.trim() ).replace(/\s+.+$/, '').replace(/\d+$/, '').toLowerCase();
257
+
258
+ // Normalize both path styles because Plugin definitions may come from a
259
+ // different operating system than the CLI displaying them. Remove command
260
+ // arguments before taking the basename, including quoted paths with spaces.
261
+ var cmdline = bin.trim().replace(/\\/g, '/');
262
+ var cmd = cmdline;
263
+ if (cmd.match(/^(?:.*\/)?env(?:\.exe)?\s+(?:\-S\s+)?(\S+)/i)) cmd = RegExp.$1;
264
+ else if (cmd.match(/^"([^"]+)"/)) cmd = RegExp.$1;
265
+ else if (cmd.match(/^'([^']+)'/)) cmd = RegExp.$1;
266
+ else cmd = cmd.replace(/\s+.+$/, '');
267
+ cmd = Path.posix.basename(cmd);
268
+
269
+ // Windows executables conventionally include .exe. Version suffixes are
270
+ // also common for interpreters such as python3.12 and node24.
271
+ cmd = cmd.replace(/\.exe$/i, '').replace(/\d+(?:\.\d+)*$/, '').toLowerCase();
258
272
 
259
273
  switch (cmd) {
260
274
  case 'sh':
@@ -268,14 +282,26 @@ module.exports = {
268
282
  break;
269
283
 
270
284
  case 'node':
285
+ case 'nodejs':
271
286
  case 'deno':
272
287
  case 'bun':
273
288
  cmd = 'javascript';
274
289
  break;
275
290
 
276
291
  case 'pwsh':
292
+ case 'powershell':
277
293
  cmd = 'powershell';
278
294
  break;
295
+
296
+ case 'cmd':
297
+ cmd = 'dos';
298
+ break;
299
+
300
+ case 'py':
301
+ case 'pythonw':
302
+ case 'pypy':
303
+ cmd = 'python';
304
+ break;
279
305
  }
280
306
 
281
307
  return HIGHLIGHT_LANGUAGES.includes(cmd) ? cmd : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixlcore/xycli",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A complete command-line interface for the xyOps workflow automation system.",
5
5
  "author": "Joseph Huckaby <jhuckaby@pixlcore.com>",
6
6
  "homepage": "https://github.com/pixlcore/xycli",
@@ -21,6 +21,7 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "test": "node test.js",
24
+ "test:offline": "node --test test/config.test.js test/help.test.js test/markdown.test.js test/sync-unit.test.js test/toast.test.js test/utils-unit.test.js",
24
25
  "changelog": "node tools/changelog.js"
25
26
  },
26
27
  "dependencies": {
@@ -4,12 +4,25 @@ const fs = require('node:fs');
4
4
  const os = require('node:os');
5
5
  const Path = require('node:path');
6
6
  const { spawnSync } = require('node:child_process');
7
+ const config = require('../lib/config.js');
8
+
9
+ function assertPrivateMode(file) {
10
+ // Windows permissions are represented by ACLs, not Unix owner/group/other
11
+ // mode bits. The file still has to exist, but an exact 0600 assertion only
12
+ // describes the Unix behavior that fs.chmodSync can enforce.
13
+ assert.equal(fs.existsSync(file), true);
14
+ if (process.platform != 'win32') assert.equal(fs.statSync(file).mode & 0o777, 0o600);
15
+ }
7
16
 
8
17
  function createConfigFixture(t, initial) {
9
18
  // Give each CLI process a disposable home directory. Config commands exit
10
19
  // before contacting xyOps, so these checks need no development server.
11
20
  const home_dir = fs.mkdtempSync(Path.join(os.tmpdir(), 'xycli-config-'));
12
- t.after(() => fs.rmSync(home_dir, { recursive: true, force: true }));
21
+ const program_data = fs.mkdtempSync(Path.join(os.tmpdir(), 'xycli-program-data-'));
22
+ t.after(() => {
23
+ fs.rmSync(home_dir, { recursive: true, force: true });
24
+ fs.rmSync(program_data, { recursive: true, force: true });
25
+ });
13
26
  const file = Path.join(home_dir, '.config', 'xyops', 'cli.json');
14
27
 
15
28
  if (initial) {
@@ -20,17 +33,26 @@ function createConfigFixture(t, initial) {
20
33
  return {
21
34
  file,
22
35
  run(args) {
36
+ const env = {
37
+ ...process.env,
38
+ HOME: home_dir,
39
+ USERPROFILE: home_dir,
40
+ ProgramData: program_data,
41
+ XYOPS_API_KEY: 'fixture-key',
42
+ XYOPS_BASE_URL: 'https://fixture.invalid',
43
+ XYOPS_ITEMS_PER_PAGE: '99',
44
+ XYOPS_COLOR: 'false'
45
+ };
46
+
47
+ // Prove the Windows code path does not receive a compatibility HOME
48
+ // variable from Git Bash, WSL, or the parent test environment.
49
+ if (process.platform == 'win32') delete env.HOME;
50
+
23
51
  const result = spawnSync(process.execPath, [Path.join(__dirname, '..', 'index.js'), 'config', ...args], {
24
52
  encoding: 'utf8',
25
53
  timeout: 10000,
26
- env: {
27
- ...process.env,
28
- HOME: home_dir,
29
- XYOPS_API_KEY: 'fixture-key',
30
- XYOPS_BASE_URL: 'https://fixture.invalid',
31
- XYOPS_ITEMS_PER_PAGE: '99',
32
- XYOPS_COLOR: 'false'
33
- }
54
+ windowsHide: true,
55
+ env
34
56
  });
35
57
 
36
58
  // Keep captured CLI output out of assertion messages, since a config
@@ -43,6 +65,24 @@ function createConfigFixture(t, initial) {
43
65
  };
44
66
  }
45
67
 
68
+ test('config resolves native Unix and Windows source paths', () => {
69
+ assert.deepEqual(config.getConfigFiles('linux', {}, '/home/nick'), [
70
+ '/etc/xyops/cli.json',
71
+ '/home/nick/.config/xyops/cli.json'
72
+ ]);
73
+ assert.deepEqual(config.getConfigFiles('win32', { ProgramData: 'C:\\ProgramData' }, 'C:\\Users\\Nick'), [
74
+ 'C:\\ProgramData\\xyops\\cli.json',
75
+ 'C:\\Users\\Nick\\.config\\xyops\\cli.json'
76
+ ]);
77
+ assert.deepEqual(config.getConfigFiles('win32', { ALLUSERSPROFILE: 'D:\\SharedData' }, 'C:\\Users\\Nick'), [
78
+ 'D:\\SharedData\\xyops\\cli.json',
79
+ 'C:\\Users\\Nick\\.config\\xyops\\cli.json'
80
+ ]);
81
+ assert.deepEqual(config.getConfigFiles('win32', {}, 'C:\\Users\\Nick'), [
82
+ 'C:\\Users\\Nick\\.config\\xyops\\cli.json'
83
+ ]);
84
+ });
85
+
46
86
  test('config updates only requested user settings without saving inherited overrides', t => {
47
87
  const fixture = createConfigFixture(t, {
48
88
  items_per_page: 10,
@@ -61,7 +101,7 @@ test('config updates only requested user settings without saving inherited overr
61
101
  suggest: true,
62
102
  sync: { up: ['events'], down: false }
63
103
  });
64
- assert.equal(fs.statSync(fixture.file).mode & 0o777, 0o600);
104
+ assertPrivateMode(fixture.file);
65
105
  });
66
106
 
67
107
  test('config creates a missing user file with only supplied settings', t => {
@@ -69,7 +109,7 @@ test('config creates a missing user file with only supplied settings', t => {
69
109
  fixture.run(['--items_per_page', '25']);
70
110
 
71
111
  assert.deepEqual(fixture.read(), { items_per_page: 25 });
72
- assert.equal(fs.statSync(fixture.file).mode & 0o777, 0o600);
112
+ assertPrivateMode(fixture.file);
73
113
  });
74
114
 
75
115
  test('config dotted updates preserve sibling user settings and explicit false or zero values', t => {
@@ -59,6 +59,7 @@ function runCLI(args, options = {}) {
59
59
  timeout: 30000,
60
60
  maxBuffer: 16 * 1024 * 1024,
61
61
  input: options.input,
62
+ windowsHide: true,
62
63
  env: {
63
64
  ...process.env,
64
65
  XYOPS_COLOR: 'false',
@@ -104,7 +104,8 @@ function runSync(t, options) {
104
104
  })], {
105
105
  cwd: Path.join(__dirname, '..'),
106
106
  encoding: 'utf8',
107
- timeout: 10000
107
+ timeout: 10000,
108
+ windowsHide: true
108
109
  });
109
110
  assert.ifError(result.error);
110
111
  assert.equal(result.signal, null);
@@ -659,7 +660,8 @@ test('sync PID lock rejects an overlapping run and cleans up afterward', async t
659
660
  `;
660
661
  const holder = spawn(process.execPath, ['-e', holderScript, lockFile], {
661
662
  cwd: Path.join(__dirname, '..'),
662
- stdio: ['ignore', 'pipe', 'pipe']
663
+ stdio: ['ignore', 'pipe', 'pipe'],
664
+ windowsHide: true
663
665
  });
664
666
 
665
667
  await new Promise((resolve, reject) => {
@@ -0,0 +1,19 @@
1
+ const { test } = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+ const utils = require('../lib/utils.js');
4
+
5
+ test('executable language detection accepts Unix and Windows command forms', () => {
6
+ // Plugin definitions are portable, so exercise both path styles regardless of
7
+ // the operating system running this test.
8
+ assert.equal(utils.getLangFromBinary('/usr/bin/node --use-strict'), 'javascript');
9
+ assert.equal(utils.getLangFromBinary('/usr/bin/python3.12 -u'), 'python');
10
+ assert.equal(utils.getLangFromBinary('/bin/bash'), 'shell');
11
+ assert.equal(utils.getLangFromBinary('/usr/bin/env -S node --use-strict'), 'javascript');
12
+ assert.equal(utils.getLangFromBinary('"C:\\Program Files\\nodejs\\node.exe" --use-strict'), 'javascript');
13
+ assert.equal(utils.getLangFromBinary('C:\\Python312\\python.exe -u'), 'python');
14
+ assert.equal(utils.getLangFromBinary('pwsh.exe -File'), 'powershell');
15
+ assert.equal(utils.getLangFromBinary('powershell.exe -File'), 'powershell');
16
+ assert.equal(utils.getLangFromBinary('cmd.exe /c'), 'dos');
17
+ assert.equal(utils.getLangFromBinary('unknown-binary.exe'), null);
18
+ assert.equal(utils.getLangFromBinary(null), null);
19
+ });
package/test.js CHANGED
@@ -30,7 +30,8 @@ else {
30
30
  // suite does not prevent the remaining suites from running and reporting.
31
31
  const child = spawn(process.execPath, ['--test', '--test-concurrency=1', ...files], {
32
32
  cwd: __dirname,
33
- stdio: 'inherit'
33
+ stdio: 'inherit',
34
+ windowsHide: true
34
35
  });
35
36
 
36
37
  child.on('error', error => {
@@ -13,14 +13,14 @@ var debug = (process.argv[2] == 'debug');
13
13
  process.chdir( Path.dirname( __dirname ) );
14
14
 
15
15
  // make sure git sandbox is clean
16
- var porcelain = cp.execSync('git status --porcelain', { encoding: 'utf8' }).trim();
16
+ var porcelain = cp.execSync('git status --porcelain', { encoding: 'utf8', windowsHide: true }).trim();
17
17
  if (!debug && porcelain.length) {
18
18
  console.error("\nERROR: Git sandbox has local changes. Please commit these before updating the changelog.\n");
19
19
  process.exit(1);
20
20
  }
21
21
 
22
22
  // get list of tags
23
- var tags = cp.execSync('git tag --list --sort=version:refname', { encoding: 'utf8' }).trim().split(/\n/).reverse();
23
+ var tags = cp.execSync('git tag --list --sort=version:refname', { encoding: 'utf8', windowsHide: true }).trim().split(/\n/).reverse();
24
24
 
25
25
  var md = '';
26
26
  md += "# xyCLI Changelog\n";
@@ -36,7 +36,7 @@ tags.forEach( function(tag, idx) {
36
36
  md += `\n## Version ${tag}\n\n`;
37
37
 
38
38
  var cmd = `git log ${prev_tag}..${tag} --no-merges --pretty=format:'%h %H %ad %s%n%n%b%n----PX----' --date=short`;
39
- var output = cp.execSync(cmd, { encoding: 'utf8' }).trim();
39
+ var output = cp.execSync(cmd, { encoding: 'utf8', windowsHide: true }).trim();
40
40
  var commits = output.split('----PX----');
41
41
  var first = true;
42
42
 
@@ -77,10 +77,10 @@ if (debug) {
77
77
  fs.writeFileSync( 'CHANGELOG.md', md );
78
78
 
79
79
  // make sure log has actually changed
80
- porcelain = cp.execSync('git status --porcelain', { encoding: 'utf8' }).trim();
80
+ porcelain = cp.execSync('git status --porcelain', { encoding: 'utf8', windowsHide: true }).trim();
81
81
  if (!porcelain.length) {
82
82
  console.error("\nWarning: Changelog has not changed since last run. Skipping actions.\n");
83
83
  process.exit(1);
84
84
  }
85
85
 
86
- cp.execSync('git add CHANGELOG.md && git commit -m "Update CHANGELOG" && git push', { stdio: 'inherit' } );
86
+ cp.execSync('git add CHANGELOG.md && git commit -m "Update CHANGELOG" && git push', { stdio: 'inherit', windowsHide: true } );