@cloud-cli/vm 1.3.0 → 1.3.2

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.
@@ -1,13 +1,10 @@
1
1
  name: CICD
2
2
  on:
3
- push:
4
- branches:
5
- - main
3
+ - push
4
+
6
5
  jobs:
7
6
  release:
8
- uses: homebots/workflows/.github/workflows/node-build-release.yml@master
9
- with:
10
- branches: main
7
+ uses: cloud-cli/workflows/.github/workflows/npm-build-release.yml@main
11
8
  secrets:
12
9
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
13
10
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ async function ls(options) {
23
23
  const { name, path = '' } = options;
24
24
  const root = await getVolumeMountpoint(name);
25
25
  const files = await exec('ls', ['-1', join(root, path)]);
26
- return files.stdout.trim().split('\n');
26
+ return files.stdout.trim().split('\n').filter(Boolean);
27
27
  }
28
28
  async function fixPermissions(options) {
29
29
  const { name } = options;
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@cloud-cli/vm",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Docker volume management",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
7
  "type": "module",
8
8
  "prettier": "@cloud-cli/prettier-config",
9
9
  "scripts": {
10
- "test": "jest",
11
- "tdd": "jest --watchAll",
10
+ "test": "vitest",
12
11
  "build": "tsc"
13
12
  },
14
13
  "keywords": [
@@ -24,11 +23,15 @@
24
23
  "author": "Darlan Alves <me@darlanalv.es>",
25
24
  "license": "MIT",
26
25
  "devDependencies": {
27
- "@cloud-cli/jest-config": "^1.0.0",
28
26
  "@cloud-cli/prettier-config": "^1.0.0",
29
- "@cloud-cli/typescript-config": "^1.0.1"
27
+ "@cloud-cli/typescript-config": "^1.0.1",
28
+ "typescript": "^5.4.5",
29
+ "vitest": "^1.6.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@cloud-cli/exec": "^1.0.1"
33
+ },
34
+ "peerDependencies": {
35
+ "@cloud-cli/cli": "^2.0.0"
33
36
  }
34
37
  }
package/src/index.spec.ts CHANGED
@@ -1,17 +1,17 @@
1
- const execMocks = {
2
- exec: jest.fn(),
3
- };
1
+ import { describe, expect, it, vi, beforeEach } from 'vitest';
4
2
 
5
- const fsMocks = {
6
- readFile: jest.fn(),
7
- };
3
+ const execMocks = vi.hoisted(() => ({
4
+ exec: vi.fn(),
5
+ readFile: vi.fn(),
6
+ }));
8
7
 
9
- jest.mock('@cloud-cli/exec', () => execMocks);
10
- jest.mock('node:fs/promises', () => fsMocks);
8
+ vi.mock('get-port', () => ({ default: vi.fn().mockReturnValue(1234) }));
9
+ vi.mock('@cloud-cli/exec', () => ({ exec: execMocks.exec }));
10
+
11
+ vi.mock('@cloud-cli/exec', () => ({ exec: execMocks.exec }));
12
+ vi.mock('node:fs/promises', () => ({ readFile: execMocks.readFile }));
11
13
 
12
14
  import vm from './index';
13
- import * as exec from '@cloud-cli/exec';
14
- import * as fs from 'node:fs/promises';
15
15
 
16
16
  const inspectOutput = `[{
17
17
  "CreatedAt": "2023-03-10T10:15:10Z",
@@ -27,6 +27,11 @@ const inspectOutput = `[{
27
27
  }]`;
28
28
 
29
29
  describe('volume manager', () => {
30
+ beforeEach(() => {
31
+ execMocks.exec.mockReset();
32
+ execMocks.readFile.mockReset();
33
+ });
34
+
30
35
  describe('list', () => {
31
36
  it('should list the available volumes', async () => {
32
37
  const execOutput = {
@@ -34,15 +39,15 @@ describe('volume manager', () => {
34
39
  stdout: `test\ntest2\n`,
35
40
  };
36
41
 
37
- execMocks.exec = jest.fn().mockResolvedValue(execOutput);
42
+ execMocks.exec.mockResolvedValue(execOutput);
38
43
 
39
44
  await expect(vm.list()).resolves.toEqual(['test', 'test2']);
40
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'ls', '--format={{.Name}}']);
45
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'ls', '--format={{.Name}}']);
41
46
  });
42
47
 
43
48
  it('should reject in case of error', async () => {
44
49
  const output = { ok: false };
45
- execMocks.exec = jest.fn().mockResolvedValue(output);
50
+ execMocks.exec.mockResolvedValue(output);
46
51
 
47
52
  await expect(vm.list()).rejects.toThrowError('Unable to list volumes');
48
53
  });
@@ -51,29 +56,29 @@ describe('volume manager', () => {
51
56
  describe('fixPermissions', () => {
52
57
  it('should fix write permissions on volume folder', async () => {
53
58
  const outputs = [inspectOutput, ''];
54
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
59
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
55
60
 
56
61
  await expect(vm.fixPermissions({ name: 'test' })).resolves.toEqual(true);
57
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
58
- expect(exec.exec).toHaveBeenCalledWith('chmod', ['-R', 'a+w', '/var/lib/docker/volumes/test/_data']);
62
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
63
+ expect(execMocks.exec).toHaveBeenCalledWith('chmod', ['-R', 'a+w', '/var/lib/docker/volumes/test/_data']);
59
64
  });
60
65
  });
61
66
 
62
67
  describe('ls', () => {
63
68
  it('should list files of a volume and path', async () => {
64
69
  const outputs = [inspectOutput, 'dir', inspectOutput, 'a.txt\nb.txt'];
65
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
70
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
66
71
 
67
72
  await expect(vm.ls({ name: 'test' })).resolves.toEqual(['dir']);
68
73
  await expect(vm.ls({ name: 'test', path: 'dir' })).resolves.toEqual(['a.txt', 'b.txt']);
69
74
 
70
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
71
- expect(exec.exec).toHaveBeenCalledWith('ls', ['-1', '/var/lib/docker/volumes/test/_data']);
72
- expect(exec.exec).toHaveBeenCalledWith('ls', ['-1', '/var/lib/docker/volumes/test/_data/dir']);
75
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
76
+ expect(execMocks.exec).toHaveBeenCalledWith('ls', ['-1', '/var/lib/docker/volumes/test/_data']);
77
+ expect(execMocks.exec).toHaveBeenCalledWith('ls', ['-1', '/var/lib/docker/volumes/test/_data/dir']);
73
78
  });
74
79
 
75
80
  it('should throw an error if volume name is invalid', async () => {
76
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: '[]' }));
81
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: '[]' }));
77
82
  await expect(vm.ls({ name: 'notfound' })).rejects.toThrowError('Volume not found');
78
83
  });
79
84
  });
@@ -81,21 +86,21 @@ describe('volume manager', () => {
81
86
  describe('rm', () => {
82
87
  it('should remove files of a volume by path', async () => {
83
88
  const outputs = [inspectOutput, ''];
84
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
89
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: outputs.shift() }));
85
90
 
86
91
  await expect(vm.rm({ name: 'test', path: 'file.txt' })).resolves.toEqual(true);
87
92
 
88
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
89
- expect(exec.exec).toHaveBeenCalledWith('rm', ['-r', '/var/lib/docker/volumes/test/_data/file.txt']);
93
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
94
+ expect(execMocks.exec).toHaveBeenCalledWith('rm', ['-r', '/var/lib/docker/volumes/test/_data/file.txt']);
90
95
  });
91
96
 
92
97
  it('should throw an error if volume name is invalid', async () => {
93
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: '[]' }));
98
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: '[]' }));
94
99
  await expect(vm.rm({ name: 'notfound', path: 'file.txt' })).rejects.toThrowError('Volume not found');
95
100
  });
96
101
 
97
102
  it('should throw an error if path is invalid', async () => {
98
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
103
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
99
104
  await expect(vm.rm({ name: 'nopath' })).rejects.toThrowError('Path not specified');
100
105
  expect(execMocks.exec).not.toHaveBeenCalled();
101
106
  });
@@ -103,22 +108,22 @@ describe('volume manager', () => {
103
108
 
104
109
  describe('cat', () => {
105
110
  it('should read the content of a file in a volume by path', async () => {
106
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
107
- fsMocks.readFile = jest.fn().mockImplementation(() => 'test file');
111
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
112
+ execMocks.readFile.mockImplementation(() => 'test file');
108
113
 
109
114
  await expect(vm.cat({ name: 'test', path: 'file.txt' })).resolves.toEqual('test file');
110
115
 
111
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
112
- expect(fs.readFile).toHaveBeenCalledWith('/var/lib/docker/volumes/test/_data/file.txt', 'utf8');
116
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
117
+ expect(execMocks.readFile).toHaveBeenCalledWith('/var/lib/docker/volumes/test/_data/file.txt', 'utf8');
113
118
  });
114
119
 
115
120
  it('should throw an error if volume name is invalid', async () => {
116
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: '[]' }));
121
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: '[]' }));
117
122
  await expect(vm.cat({ name: 'notfound', path: 'file.txt' })).rejects.toThrowError('Volume not found');
118
123
  });
119
124
 
120
125
  it('should throw an error if path is invalid', async () => {
121
- execMocks.exec = jest.fn().mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
126
+ execMocks.exec.mockImplementation(() => ({ ok: true, stdout: inspectOutput }));
122
127
  await expect(vm.cat({ name: 'nopath' })).rejects.toThrowError('Path not specified');
123
128
  expect(execMocks.exec).not.toHaveBeenCalled();
124
129
  });
@@ -127,7 +132,7 @@ describe('volume manager', () => {
127
132
  describe('show', () => {
128
133
  it('should show details of a volume', async () => {
129
134
  const execOutput = { ok: true, stdout: inspectOutput };
130
- execMocks.exec = jest.fn().mockResolvedValue(execOutput);
135
+ execMocks.exec.mockResolvedValue(execOutput);
131
136
 
132
137
  await expect(vm.show({ name: 'test' })).resolves.toEqual({
133
138
  name: 'test',
@@ -138,18 +143,17 @@ describe('volume manager', () => {
138
143
  },
139
144
  });
140
145
 
141
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
146
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'inspect', 'test']);
142
147
  });
143
148
 
144
149
  it('should throw an error if volume name is invalid', async () => {
145
- execMocks.exec = jest.fn();
146
150
  await expect(vm.show({ name: 'In$%valid' })).rejects.toThrowError('Invalid name');
147
- expect(exec.exec).not.toHaveBeenCalled();
151
+ expect(execMocks.exec).not.toHaveBeenCalled();
148
152
  });
149
153
 
150
154
  it('should throw error if volume does not exist', async () => {
151
155
  const output = { ok: true, stdout: '[]' };
152
- execMocks.exec = jest.fn().mockResolvedValue(output);
156
+ execMocks.exec.mockResolvedValue(output);
153
157
 
154
158
  await expect(vm.show({ name: 'invalid' })).rejects.toThrowError('Volume not found');
155
159
  });
@@ -158,22 +162,21 @@ describe('volume manager', () => {
158
162
  describe('add', () => {
159
163
  it('should add a volume', async () => {
160
164
  const execOutput = { ok: true, stdout: 'test' };
161
- execMocks.exec = jest.fn().mockResolvedValue(execOutput);
165
+ execMocks.exec.mockResolvedValue(execOutput);
162
166
 
163
167
  await expect(vm.add({ name: 'test' })).resolves.toEqual(true);
164
168
 
165
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'create', 'test']);
169
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'create', 'test']);
166
170
  });
167
171
 
168
172
  it('should throw error if volume name is invalid', async () => {
169
- execMocks.exec = jest.fn();
170
173
  await expect(vm.add({ name: 'In$%valid' })).rejects.toThrowError('Invalid name');
171
- expect(exec.exec).not.toHaveBeenCalled();
174
+ expect(execMocks.exec).not.toHaveBeenCalled();
172
175
  });
173
176
 
174
177
  it('should throw error if volume creation fails', async () => {
175
178
  const output = { ok: false, stdout: '' };
176
- execMocks.exec = jest.fn().mockResolvedValue(output);
179
+ execMocks.exec.mockResolvedValue(output);
177
180
 
178
181
  await expect(vm.add({ name: 'failed' })).rejects.toThrowError('Unable to create volume');
179
182
  });
@@ -182,22 +185,21 @@ describe('volume manager', () => {
182
185
  describe('remove', () => {
183
186
  it('should add a volume', async () => {
184
187
  const execOutput = { ok: true, stdout: '' };
185
- execMocks.exec = jest.fn().mockResolvedValue(execOutput);
188
+ execMocks.exec.mockResolvedValue(execOutput);
186
189
 
187
190
  await expect(vm.remove({ name: 'test' })).resolves.toEqual(true);
188
191
 
189
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'rm', 'test']);
192
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'rm', 'test']);
190
193
  });
191
194
 
192
195
  it('should throw error if volume name is invalid', async () => {
193
- execMocks.exec = jest.fn();
194
196
  await expect(vm.remove({ name: 'In$%valid' })).rejects.toThrowError('Invalid name');
195
- expect(exec.exec).not.toHaveBeenCalled();
197
+ expect(execMocks.exec).not.toHaveBeenCalled();
196
198
  });
197
199
 
198
200
  it('should throw error if volume removal fails', async () => {
199
201
  const output = { ok: false, stdout: '' };
200
- execMocks.exec = jest.fn().mockResolvedValue(output);
202
+ execMocks.exec.mockResolvedValue(output);
201
203
 
202
204
  await expect(vm.remove({ name: 'failed' })).rejects.toThrowError('Unable to remove volume');
203
205
  });
@@ -206,11 +208,11 @@ describe('volume manager', () => {
206
208
  describe('prune', () => {
207
209
  it('should remove ununsed volumes', async () => {
208
210
  const execOutput = { ok: true, stdout: '' };
209
- execMocks.exec = jest.fn().mockResolvedValue(execOutput);
211
+ execMocks.exec.mockResolvedValue(execOutput);
210
212
 
211
213
  await expect(vm.prune()).resolves.toEqual('');
212
214
 
213
- expect(exec.exec).toHaveBeenCalledWith('docker', ['volume', 'prune']);
215
+ expect(execMocks.exec).toHaveBeenCalledWith('docker', ['volume', 'prune']);
214
216
  });
215
217
  });
216
218
  });
package/src/index.ts CHANGED
@@ -41,7 +41,7 @@ async function ls(options: NameAndPathOption) {
41
41
  const root = await getVolumeMountpoint(name);
42
42
  const files = await exec('ls', ['-1', join(root, path)]);
43
43
 
44
- return files.stdout.trim().split('\n');
44
+ return files.stdout.trim().split('\n').filter(Boolean);
45
45
  }
46
46
 
47
47
  async function fixPermissions(options: NameOption) {