mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-20 22:27:24 -04:00
Git clone fails on Windows. This fixes it. (#2194)
Co-authored-by: Gregory Schier <gschier1990@gmail.com>
This commit is contained in:
12
packages/insomnia-app/app/sync/git/__mocks__/path.js
Normal file
12
packages/insomnia-app/app/sync/git/__mocks__/path.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// eslint-disable-next-line filenames/match-exported
|
||||
const path = jest.requireActual('path');
|
||||
|
||||
const exportObj = { __mockPath, ...path };
|
||||
|
||||
function __mockPath(type) {
|
||||
const mock = type === 'win32' ? path.win32 : path.posix;
|
||||
|
||||
Object.keys(mock).forEach(k => (exportObj[k] = mock[k]));
|
||||
}
|
||||
|
||||
module.exports = exportObj;
|
||||
@@ -1,8 +1,12 @@
|
||||
import GitVCS, { GIT_NAMESPACE_DIR } from '../git-vcs';
|
||||
import GitVCS, { GIT_CLONE_DIR, GIT_INSOMNIA_DIR } from '../git-vcs';
|
||||
import { setupDateMocks } from './util';
|
||||
import { MemPlugin } from '../mem-plugin';
|
||||
import path from 'path';
|
||||
jest.mock('path');
|
||||
|
||||
describe('Git-VCS', () => {
|
||||
describe.each(['win32', 'posix'])('Git-VCS using path.%s', type => {
|
||||
beforeAll(() => path.__mockPath(type));
|
||||
afterAll(() => jest.restoreAllMocks());
|
||||
beforeEach(setupDateMocks);
|
||||
|
||||
describe('common operations', () => {
|
||||
@@ -10,7 +14,7 @@ describe('Git-VCS', () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
|
||||
// No files exist yet
|
||||
@@ -24,33 +28,33 @@ describe('Git-VCS', () => {
|
||||
|
||||
it('stage and unstage file', async () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
await fs.promises.mkdir(`/${GIT_NAMESPACE_DIR}`);
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/bar.txt`, 'bar');
|
||||
await fs.promises.mkdir(GIT_INSOMNIA_DIR);
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/bar.txt`, 'bar');
|
||||
|
||||
// Files outside namespace should be ignored
|
||||
await fs.promises.writeFile('/other.txt', 'other');
|
||||
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('*added');
|
||||
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('added');
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('added');
|
||||
|
||||
await vcs.remove(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('*added');
|
||||
await vcs.remove(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('*added');
|
||||
});
|
||||
|
||||
it('Returns empty log without first commit', async () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
|
||||
expect(await vcs.log()).toEqual([]);
|
||||
@@ -58,20 +62,20 @@ describe('Git-VCS', () => {
|
||||
|
||||
it('commit file', async () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
await fs.promises.mkdir(`/${GIT_NAMESPACE_DIR}`);
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/bar.txt`, 'bar');
|
||||
await fs.promises.mkdir(`${GIT_INSOMNIA_DIR}`);
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/bar.txt`, 'bar');
|
||||
|
||||
await fs.promises.writeFile('/other.txt', 'should be ignored');
|
||||
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
await vcs.commit('First commit!');
|
||||
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('unmodified');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('unmodified');
|
||||
|
||||
expect(await vcs.log()).toEqual([
|
||||
{
|
||||
@@ -94,36 +98,36 @@ describe('Git-VCS', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await fs.promises.unlink(`/${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('*deleted');
|
||||
await fs.promises.unlink(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('*deleted');
|
||||
|
||||
await vcs.remove(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('deleted');
|
||||
await vcs.remove(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('deleted');
|
||||
|
||||
await vcs.remove(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_NAMESPACE_DIR}/foo.txt`)).toBe('deleted');
|
||||
await vcs.remove(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/bar.txt`)).toBe('*added');
|
||||
expect(await vcs.status(`${GIT_INSOMNIA_DIR}/foo.txt`)).toBe('deleted');
|
||||
});
|
||||
|
||||
it('create branch', async () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
await fs.promises.mkdir(`/${GIT_NAMESPACE_DIR}`);
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/bar.txt`, 'bar');
|
||||
await fs.promises.mkdir(`${GIT_INSOMNIA_DIR}`);
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/foo.txt`, 'foo');
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/bar.txt`, 'bar');
|
||||
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/foo.txt`);
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/foo.txt`);
|
||||
await vcs.commit('First commit!');
|
||||
|
||||
expect((await vcs.log()).length).toBe(1);
|
||||
|
||||
await vcs.checkout('new-branch');
|
||||
expect((await vcs.log()).length).toBe(1);
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/bar.txt`);
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/bar.txt`);
|
||||
await vcs.commit('Second commit!');
|
||||
expect((await vcs.log()).length).toBe(2);
|
||||
|
||||
@@ -135,28 +139,26 @@ describe('Git-VCS', () => {
|
||||
describe('readObjectFromTree()', () => {
|
||||
it('reads an object from tree', async () => {
|
||||
const fs = MemPlugin.createPlugin();
|
||||
await fs.promises.mkdir(`/${GIT_NAMESPACE_DIR}`);
|
||||
await fs.promises.mkdir(`/${GIT_NAMESPACE_DIR}/dir`);
|
||||
await fs.promises.writeFile(`/${GIT_NAMESPACE_DIR}/dir/foo.txt`, 'foo');
|
||||
await fs.promises.mkdir(`${GIT_INSOMNIA_DIR}`);
|
||||
await fs.promises.mkdir(`${GIT_INSOMNIA_DIR}/dir`);
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/dir/foo.txt`, 'foo');
|
||||
|
||||
const vcs = new GitVCS();
|
||||
await vcs.init('/', fs);
|
||||
await vcs.init(GIT_CLONE_DIR, fs);
|
||||
await vcs.setAuthor('Karen Brown', 'karen@example.com');
|
||||
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/dir/foo.txt`);
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/dir/foo.txt`);
|
||||
await vcs.commit('First');
|
||||
|
||||
await fs.promises.writeFile(`${GIT_NAMESPACE_DIR}//dir/foo.txt`, 'foo bar');
|
||||
await vcs.add(`${GIT_NAMESPACE_DIR}/dir/foo.txt`);
|
||||
await fs.promises.writeFile(`${GIT_INSOMNIA_DIR}/dir/foo.txt`, 'foo bar');
|
||||
await vcs.add(`${GIT_INSOMNIA_DIR}/dir/foo.txt`);
|
||||
await vcs.commit('Second');
|
||||
|
||||
const log = await vcs.log();
|
||||
expect(await vcs.readObjFromTree(log[0].tree, `${GIT_NAMESPACE_DIR}/dir/foo.txt`)).toBe(
|
||||
expect(await vcs.readObjFromTree(log[0].tree, `${GIT_INSOMNIA_DIR}/dir/foo.txt`)).toBe(
|
||||
'foo bar',
|
||||
);
|
||||
expect(await vcs.readObjFromTree(log[1].tree, `${GIT_NAMESPACE_DIR}/dir/foo.txt`)).toBe(
|
||||
'foo',
|
||||
);
|
||||
expect(await vcs.readObjFromTree(log[1].tree, `${GIT_INSOMNIA_DIR}/dir/foo.txt`)).toBe('foo');
|
||||
|
||||
// Some extra checks
|
||||
expect(await vcs.readObjFromTree(log[1].tree, 'missing')).toBe(null);
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { assertAsyncError, setupDateMocks } from './util';
|
||||
import { MemPlugin } from '../mem-plugin';
|
||||
import path from 'path';
|
||||
import { GIT_CLONE_DIR } from '../git-vcs';
|
||||
jest.mock('path');
|
||||
|
||||
describe('MemPlugin', () => {
|
||||
describe.each(['win32', 'posix'])('Memlugin using path.%s', type => {
|
||||
beforeAll(() => path.__mockPath(type));
|
||||
afterAll(() => jest.restoreAllMocks());
|
||||
beforeEach(setupDateMocks);
|
||||
|
||||
describe('readfile()', () => {
|
||||
@@ -90,12 +95,12 @@ describe('MemPlugin', () => {
|
||||
const p = new MemPlugin();
|
||||
|
||||
// Root dir should always exist
|
||||
expect(await p.readdir('/')).toEqual([]);
|
||||
expect(await p.readdir(GIT_CLONE_DIR)).toEqual([]);
|
||||
|
||||
// Write a file and list it again
|
||||
await p.writeFile('/foo.txt', 'Hello World!');
|
||||
await p.writeFile('/bar.txt', 'Bar!');
|
||||
expect(await p.readdir('/')).toEqual(['bar.txt', 'foo.txt']);
|
||||
expect(await p.readdir(GIT_CLONE_DIR)).toEqual(['bar.txt', 'foo.txt']);
|
||||
});
|
||||
|
||||
it('errors on file', async () => {
|
||||
@@ -117,21 +122,29 @@ describe('MemPlugin', () => {
|
||||
await p.mkdir('/foo');
|
||||
await p.mkdir('/foo/bar');
|
||||
|
||||
expect(await p.readdir('/')).toEqual(['foo']);
|
||||
expect(await p.readdir('/foo')).toEqual(['bar']);
|
||||
expect(await p.readdir(GIT_CLONE_DIR)).toEqual(['foo']);
|
||||
expect(await p.readdir(`${GIT_CLONE_DIR}/foo`)).toEqual(['bar']);
|
||||
});
|
||||
|
||||
it('creates directory non-recursively', async () => {
|
||||
const p = new MemPlugin();
|
||||
|
||||
await p.mkdir(`${GIT_CLONE_DIR}/foo`, { recursive: true });
|
||||
await p.mkdir(`${GIT_CLONE_DIR}/foo/bar`);
|
||||
expect(await p.readdir(`${GIT_CLONE_DIR}/foo/bar`)).toEqual([]);
|
||||
});
|
||||
|
||||
it('creates directory recursively', async () => {
|
||||
const p = new MemPlugin();
|
||||
|
||||
await p.mkdir('/foo/bar/baz', { recursive: true });
|
||||
expect(await p.readdir('/foo/bar/baz')).toEqual([]);
|
||||
await p.mkdir(`${GIT_CLONE_DIR}/foo/bar/baz`, { recursive: true });
|
||||
expect(await p.readdir(`${GIT_CLONE_DIR}/foo/bar/baz`)).toEqual([]);
|
||||
});
|
||||
|
||||
it('fails to create if no parent', async () => {
|
||||
const p = new MemPlugin();
|
||||
|
||||
await assertAsyncError(p.mkdir('/foo/bar/baz'), 'ENOENT');
|
||||
await assertAsyncError(p.mkdir(`${GIT_CLONE_DIR}/foo/bar/baz`), 'ENOENT');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,7 +180,7 @@ describe('MemPlugin', () => {
|
||||
it('stats root dir', async () => {
|
||||
const p = new MemPlugin();
|
||||
|
||||
const stat = await p.stat('/');
|
||||
const stat = await p.stat(GIT_CLONE_DIR);
|
||||
|
||||
expect(stat).toEqual({
|
||||
ctimeMs: 1000000000000,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
// @flow
|
||||
import YAML from 'yaml';
|
||||
import { globalBeforeEach } from '../../../__jest__/before-each';
|
||||
import * as models from '../../../models';
|
||||
import * as db from '../../../common/database';
|
||||
import { assertAsyncError, setupDateMocks } from './util';
|
||||
import NeDBPlugin from '../ne-db-plugin';
|
||||
import { GIT_NAMESPACE_DIR } from '../git-vcs';
|
||||
import path from 'path';
|
||||
import { GIT_CLONE_DIR, GIT_INSOMNIA_DIR, GIT_INSOMNIA_DIR_NAME } from '../git-vcs';
|
||||
jest.mock('path');
|
||||
|
||||
describe.each(['win32', 'posix'])('NeDBPlugin using path.%s', type => {
|
||||
beforeAll(() => path.__mockPath(type));
|
||||
afterAll(() => jest.restoreAllMocks());
|
||||
|
||||
describe('NeDBPlugin', () => {
|
||||
beforeEach(async () => {
|
||||
await globalBeforeEach();
|
||||
|
||||
@@ -24,19 +31,19 @@ describe('NeDBPlugin', () => {
|
||||
it('reads model IDs from model type folders', async () => {
|
||||
const pNeDB = new NeDBPlugin('wrk_1');
|
||||
|
||||
expect(await pNeDB.readdir('/')).toEqual([GIT_NAMESPACE_DIR]);
|
||||
expect(await pNeDB.readdir(`/${GIT_NAMESPACE_DIR}`)).toEqual([
|
||||
expect(await pNeDB.readdir(GIT_CLONE_DIR)).toEqual([GIT_INSOMNIA_DIR_NAME]);
|
||||
expect(await pNeDB.readdir(GIT_INSOMNIA_DIR)).toEqual([
|
||||
'ApiSpec',
|
||||
'Environment',
|
||||
'Request',
|
||||
'RequestGroup',
|
||||
'Workspace',
|
||||
]);
|
||||
expect(await pNeDB.readdir(`/${GIT_NAMESPACE_DIR}/Request`)).toEqual([
|
||||
expect(await pNeDB.readdir(`${GIT_INSOMNIA_DIR}/Request`)).toEqual([
|
||||
'req_1.yml',
|
||||
'req_2.yml',
|
||||
]);
|
||||
expect(await pNeDB.readdir(`/${GIT_NAMESPACE_DIR}/Workspace`)).toEqual(['wrk_1.yml']);
|
||||
expect(await pNeDB.readdir(`${GIT_INSOMNIA_DIR}/Workspace`)).toEqual(['wrk_1.yml']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,14 +52,14 @@ describe('NeDBPlugin', () => {
|
||||
const pNeDB = new NeDBPlugin('wrk_1');
|
||||
|
||||
expect(
|
||||
YAML.parse(await pNeDB.readFile(`/${GIT_NAMESPACE_DIR}/Workspace/wrk_1.yml`, 'utf8')),
|
||||
YAML.parse(await pNeDB.readFile(`${GIT_INSOMNIA_DIR}/Workspace/wrk_1.yml`, 'utf8')),
|
||||
).toEqual(expect.objectContaining({ _id: 'wrk_1', parentId: null }));
|
||||
|
||||
expect(
|
||||
YAML.parse(await pNeDB.readFile(`/${GIT_NAMESPACE_DIR}/Request/req_1.yml`, 'utf8')),
|
||||
YAML.parse(await pNeDB.readFile(`${GIT_INSOMNIA_DIR}/Request/req_1.yml`, 'utf8')),
|
||||
).toEqual(expect.objectContaining({ _id: 'req_1', parentId: 'wrk_1' }));
|
||||
|
||||
await assertAsyncError(pNeDB.readFile(`/${GIT_NAMESPACE_DIR}/Request/req_x.yml`));
|
||||
await assertAsyncError(pNeDB.readFile(`${GIT_INSOMNIA_DIR}/Request/req_x.yml`));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,19 +67,106 @@ describe('NeDBPlugin', () => {
|
||||
it('stats a dir', async () => {
|
||||
const pNeDB = new NeDBPlugin('wrk_1');
|
||||
|
||||
expect(await pNeDB.stat('/')).toEqual(expect.objectContaining({ type: 'dir' }));
|
||||
expect(await pNeDB.stat(`/${GIT_NAMESPACE_DIR}`)).toEqual(
|
||||
expect(await pNeDB.stat(GIT_CLONE_DIR)).toEqual(expect.objectContaining({ type: 'dir' }));
|
||||
expect(await pNeDB.stat(`${GIT_INSOMNIA_DIR}`)).toEqual(
|
||||
expect.objectContaining({ type: 'dir' }),
|
||||
);
|
||||
expect(await pNeDB.stat(`/${GIT_NAMESPACE_DIR}/Workspace/wrk_1.yml`)).toEqual(
|
||||
expect(await pNeDB.stat(`${GIT_INSOMNIA_DIR}/Workspace/wrk_1.yml`)).toEqual(
|
||||
expect.objectContaining({ type: 'file' }),
|
||||
);
|
||||
expect(await pNeDB.stat(`/${GIT_NAMESPACE_DIR}/Request`)).toEqual(
|
||||
expect(await pNeDB.stat(`${GIT_INSOMNIA_DIR}/Request`)).toEqual(
|
||||
expect.objectContaining({ type: 'dir' }),
|
||||
);
|
||||
expect(await pNeDB.stat(`/${GIT_NAMESPACE_DIR}/Request/req_2.yml`)).toEqual(
|
||||
expect(await pNeDB.stat(`${GIT_INSOMNIA_DIR}/Request/req_2.yml`)).toEqual(
|
||||
expect.objectContaining({ type: 'file' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeFile()', () => {
|
||||
it('should ignore files not in GIT_INSOMNIA_DIR directory', async () => {
|
||||
// Assemble
|
||||
const upsertSpy = jest.spyOn(db, 'upsert');
|
||||
const workspaceId = 'wrk_1';
|
||||
const pNeDB = new NeDBPlugin(workspaceId);
|
||||
|
||||
const env = { _id: 'env_1', type: 'Environment', parentId: workspaceId };
|
||||
const filePath = `anotherDir/${env.type}/${env._id}.yml`;
|
||||
|
||||
// Act
|
||||
await pNeDB.writeFile(filePath, YAML.stringify(env));
|
||||
|
||||
// Assert
|
||||
expect(upsertSpy).not.toBeCalled();
|
||||
|
||||
// Cleanup
|
||||
upsertSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should write files in GIT_INSOMNIA_DIR directory to db', async () => {
|
||||
// Assemble
|
||||
const workspaceId = 'wrk_1';
|
||||
const pNeDB = new NeDBPlugin(workspaceId);
|
||||
const upsertSpy = jest.spyOn(db, 'upsert');
|
||||
|
||||
const env = { _id: 'env_1', type: 'Environment', parentId: workspaceId };
|
||||
const filePath = `${GIT_INSOMNIA_DIR}/${env.type}/${env._id}.yml`;
|
||||
|
||||
// Act
|
||||
await pNeDB.writeFile(filePath, YAML.stringify(env));
|
||||
|
||||
// Assert
|
||||
expect(upsertSpy).toHaveBeenCalledTimes(1);
|
||||
expect(upsertSpy).toHaveBeenCalledWith(env, true);
|
||||
|
||||
// Cleanup
|
||||
upsertSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should throw error if id does not match', async () => {
|
||||
// Assemble
|
||||
const workspaceId = 'wrk_1';
|
||||
const pNeDB = new NeDBPlugin(workspaceId);
|
||||
|
||||
const env = { _id: 'env_1', type: 'Environment', parentId: workspaceId };
|
||||
const filePath = `${GIT_INSOMNIA_DIR}/${env.type}/env_2.yml`;
|
||||
|
||||
// Act
|
||||
const promiseResult = pNeDB.writeFile(filePath, YAML.stringify(env));
|
||||
|
||||
// Assert
|
||||
await expect(promiseResult).rejects.toThrowError(
|
||||
`Doc _id does not match file path [env_1 != env_2]`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if type does not match', async () => {
|
||||
// Assemble
|
||||
const workspaceId = 'wrk_1';
|
||||
const pNeDB = new NeDBPlugin(workspaceId);
|
||||
|
||||
const env = { _id: 'env_1', type: 'Environment', parentId: workspaceId };
|
||||
const filePath = `${GIT_INSOMNIA_DIR}/Request/${env._id}.yml`;
|
||||
|
||||
// Act
|
||||
const promiseResult = pNeDB.writeFile(filePath, YAML.stringify(env));
|
||||
|
||||
// Assert
|
||||
await expect(promiseResult).rejects.toThrowError(
|
||||
`Doc type does not match file path [Environment != Request]`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mkdir()', () => {
|
||||
it('should throw error', async () => {
|
||||
const workspaceId = 'wrk_1';
|
||||
const pNeDB = new NeDBPlugin(workspaceId);
|
||||
|
||||
const promiseResult = pNeDB.mkdir('', '');
|
||||
|
||||
// Assert
|
||||
await expect(promiseResult).rejects.toThrowError('NeDBPlugin is not writable');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { MemPlugin } from '../mem-plugin';
|
||||
import { routableFSPlugin } from '../routable-fs-plugin';
|
||||
import { GIT_CLONE_DIR } from '../git-vcs';
|
||||
import path from 'path';
|
||||
jest.mock('path');
|
||||
|
||||
describe('routableFSPlugin', () => {
|
||||
describe.each(['win32', 'posix'])('routableFSPlugin using path.%s', type => {
|
||||
beforeAll(() => path.__mockPath(type));
|
||||
afterAll(() => jest.restoreAllMocks());
|
||||
it('routes .git and other files to separate places', async () => {
|
||||
const pGit = MemPlugin.createPlugin();
|
||||
const pDir = MemPlugin.createPlugin();
|
||||
@@ -18,7 +23,7 @@ describe('routableFSPlugin', () => {
|
||||
expect(await pDir.promises.readdir('/other')).toEqual(['a.txt']);
|
||||
|
||||
// Kind of an edge case, but reading the root dir will not list the .git folder
|
||||
expect(await pDir.promises.readdir('/')).toEqual(['other']);
|
||||
expect(await pDir.promises.readdir(GIT_CLONE_DIR)).toEqual(['other']);
|
||||
|
||||
expect((await p.readFile('/other/a.txt')).toString()).toBe('a');
|
||||
expect((await p.readFile('/.git/b.txt')).toString()).toBe('b');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import * as git from 'isomorphic-git';
|
||||
import { trackEvent } from '../../common/analytics';
|
||||
import { httpPlugin } from './http';
|
||||
import path from 'path';
|
||||
|
||||
export type GitAuthor = {|
|
||||
name: string,
|
||||
@@ -34,7 +35,16 @@ export type GitLogEntry = {|
|
||||
},
|
||||
|};
|
||||
|
||||
export const GIT_NAMESPACE_DIR = '.insomnia';
|
||||
// isomorphic-git internally will default an empty ('') clone directory to '.'
|
||||
// Ref: https://github.com/isomorphic-git/isomorphic-git/blob/4e66704d05042624bbc78b85ee5110d5ee7ec3e2/src/utils/normalizePath.js#L10
|
||||
// We should set this explicitly (even if set to an empty string), because we have other code (such as fs plugins
|
||||
// and unit tests) that depend on the clone directory.
|
||||
export const GIT_CLONE_DIR = '.';
|
||||
const _gitInternalDirName = 'git';
|
||||
export const GIT_INSOMNIA_DIR_NAME = '.insomnia';
|
||||
|
||||
export const GIT_INTERNAL_DIR = path.join(GIT_CLONE_DIR, _gitInternalDirName);
|
||||
export const GIT_INSOMNIA_DIR = path.join(GIT_CLONE_DIR, GIT_INSOMNIA_DIR_NAME);
|
||||
|
||||
export default class GitVCS {
|
||||
_git: Object;
|
||||
|
||||
@@ -278,7 +278,9 @@ export class MemPlugin {
|
||||
filePath = path.normalize(filePath);
|
||||
|
||||
let current = this.__fs;
|
||||
const pathSegments = filePath.split(path.sep).filter(s => s !== '');
|
||||
|
||||
// Ignore empty and current directory '.' segments
|
||||
const pathSegments = filePath.split(path.sep).filter(s => s !== '' && s !== '.');
|
||||
for (const expectedName of pathSegments) {
|
||||
const e = (current.children || []).find(c => c.name === expectedName);
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@ import * as db from '../../common/database';
|
||||
import * as models from '../../models';
|
||||
import YAML from 'yaml';
|
||||
import Stat from './stat';
|
||||
import { GIT_NAMESPACE_DIR } from './git-vcs';
|
||||
import { GIT_CLONE_DIR, GIT_INSOMNIA_DIR_NAME } from './git-vcs';
|
||||
|
||||
export default class NeDBPlugin {
|
||||
_workspaceId: string;
|
||||
_cloneDirRegExp: RegExp;
|
||||
|
||||
constructor(workspaceId: string) {
|
||||
if (!workspaceId) {
|
||||
throw new Error('Cannot use NeDBPlugin without workspace ID');
|
||||
}
|
||||
this._workspaceId = workspaceId;
|
||||
|
||||
// The win32 separator is a single backslash (\), but we have to escape both the JS string and RegExp.
|
||||
const pathSep = path.sep === path.win32.sep ? '\\\\' : '/';
|
||||
this._cloneDirRegExp = new RegExp(`^${GIT_CLONE_DIR}${pathSep}`);
|
||||
}
|
||||
|
||||
static createPlugin(workspaceId: string) {
|
||||
@@ -69,7 +74,7 @@ export default class NeDBPlugin {
|
||||
filePath = path.normalize(filePath);
|
||||
const { root, id, type } = this._parsePath(filePath);
|
||||
|
||||
if (root !== GIT_NAMESPACE_DIR) {
|
||||
if (root !== GIT_INSOMNIA_DIR_NAME) {
|
||||
console.log(`[git] Ignoring external file ${filePath}`);
|
||||
return;
|
||||
}
|
||||
@@ -77,11 +82,11 @@ export default class NeDBPlugin {
|
||||
const doc = YAML.parse(data.toString());
|
||||
|
||||
if (id !== doc._id) {
|
||||
throw new Error(`Doc _id does not match file path ${doc._id} != ${id || 'null'}`);
|
||||
throw new Error(`Doc _id does not match file path [${doc._id} != ${id || 'null'}]`);
|
||||
}
|
||||
|
||||
if (type !== doc.type) {
|
||||
throw new Error(`Doc type does not match file path ${doc.type} != ${type || 'null'}`);
|
||||
throw new Error(`Doc type does not match file path [${doc.type} != ${type || 'null'}]`);
|
||||
}
|
||||
|
||||
await db.upsert(doc, true);
|
||||
@@ -112,7 +117,7 @@ export default class NeDBPlugin {
|
||||
let docs = [];
|
||||
let otherFolders = [];
|
||||
if (root === null && id === null && type === null) {
|
||||
otherFolders = [GIT_NAMESPACE_DIR];
|
||||
otherFolders = [GIT_INSOMNIA_DIR_NAME];
|
||||
} else if (id === null && type === null) {
|
||||
otherFolders = [
|
||||
models.workspace.type,
|
||||
@@ -201,7 +206,12 @@ export default class NeDBPlugin {
|
||||
_parsePath(filePath: string): { root: string | null, type: string | null, id: string | null } {
|
||||
filePath = path.normalize(filePath);
|
||||
|
||||
const [root, type, idRaw] = filePath.split(path.sep).filter(s => s !== '');
|
||||
// FilePath will start with the clone directory. We want to remove the clone dir, so that the
|
||||
// segments can be extracted correctly.
|
||||
filePath = filePath.replace(this._cloneDirRegExp, '');
|
||||
|
||||
// Ignore empty and current directory '.' segments
|
||||
const [root, type, idRaw] = filePath.split(path.sep).filter(s => s !== '' && s !== '.');
|
||||
|
||||
const id = typeof idRaw === 'string' ? idRaw.replace(/\.(json|yml)$/, '') : idRaw;
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ class GitRepositorySettingsModal extends React.PureComponent<Props, State> {
|
||||
inputs.token = typeof creds.token === 'string' ? creds.token : '';
|
||||
inputs.authorEmail = gitRepository.author.email;
|
||||
inputs.authorName = gitRepository.author.name;
|
||||
inputs.username = gitRepository.credentials?.username || '';
|
||||
inputs.uri = gitRepository.uri;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import Modal from '../base/modal';
|
||||
import ModalBody from '../base/modal-body';
|
||||
import ModalHeader from '../base/modal-header';
|
||||
import type { Workspace } from '../../../models/workspace';
|
||||
import GitVCS, { GIT_NAMESPACE_DIR } from '../../../sync/git/git-vcs';
|
||||
import GitVCS, { GIT_INSOMNIA_DIR, GIT_INSOMNIA_DIR_NAME } from '../../../sync/git/git-vcs';
|
||||
import { withDescendants } from '../../../common/database';
|
||||
import IndeterminateCheckbox from '../base/indeterminate-checkbox';
|
||||
import ModalFooter from '../base/modal-footer';
|
||||
@@ -129,15 +129,14 @@ class GitStagingModal extends React.PureComponent<Props, State> {
|
||||
const { vcs } = this.props;
|
||||
|
||||
const f = vcs.getFs().promises;
|
||||
const rootDir = path.join('/', GIT_NAMESPACE_DIR);
|
||||
|
||||
const fsPaths = [];
|
||||
for (const type of await f.readdir(rootDir)) {
|
||||
const typeDir = path.join(rootDir, type);
|
||||
for (const type of await f.readdir(GIT_INSOMNIA_DIR)) {
|
||||
const typeDir = path.join(GIT_INSOMNIA_DIR, type);
|
||||
for (const name of await f.readdir(typeDir)) {
|
||||
// NOTE: git paths don't start with '/' so we're omitting
|
||||
// it here too.
|
||||
const gitPath = path.join(`${GIT_NAMESPACE_DIR}/`, type, name);
|
||||
const gitPath = path.join(GIT_INSOMNIA_DIR_NAME, type, name);
|
||||
fsPaths.push(path.join(gitPath));
|
||||
}
|
||||
}
|
||||
@@ -167,9 +166,9 @@ class GitStagingModal extends React.PureComponent<Props, State> {
|
||||
|
||||
this.statusNames = {};
|
||||
for (const doc of docs) {
|
||||
this.statusNames[path.join(GIT_NAMESPACE_DIR, doc.type, `${doc._id}.json`)] =
|
||||
this.statusNames[path.join(GIT_INSOMNIA_DIR_NAME, doc.type, `${doc._id}.json`)] =
|
||||
(doc: any).name || '';
|
||||
this.statusNames[path.join(GIT_NAMESPACE_DIR, doc.type, `${doc._id}.yml`)] =
|
||||
this.statusNames[path.join(GIT_INSOMNIA_DIR_NAME, doc.type, `${doc._id}.yml`)] =
|
||||
(doc: any).name || '';
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,12 @@ import type { ForceToWorkspace } from '../redux/modules/helpers';
|
||||
import { ForceToWorkspaceKeys } from '../redux/modules/helpers';
|
||||
import designerLogo from '../images/insomnia-designer-logo.svg';
|
||||
import { MemPlugin } from '../../sync/git/mem-plugin';
|
||||
import GitVCS, { GIT_NAMESPACE_DIR } from '../../sync/git/git-vcs';
|
||||
import GitVCS, {
|
||||
GIT_CLONE_DIR,
|
||||
GIT_INSOMNIA_DIR,
|
||||
GIT_INSOMNIA_DIR_NAME,
|
||||
GIT_INTERNAL_DIR,
|
||||
} from '../../sync/git/git-vcs';
|
||||
import { parseApiSpec } from '../../common/api-specs';
|
||||
|
||||
type Props = {|
|
||||
@@ -115,7 +120,6 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
trackEvent('Git', 'Clone');
|
||||
|
||||
const core = Math.random() + '';
|
||||
const rootDir = path.join('/', GIT_NAMESPACE_DIR);
|
||||
|
||||
// Create in-memory filesystem to perform clone
|
||||
const plugins = git.cores.create(core);
|
||||
@@ -127,7 +131,8 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
try {
|
||||
await git.clone({
|
||||
core,
|
||||
dir: '/',
|
||||
dir: GIT_CLONE_DIR,
|
||||
gitdir: GIT_INTERNAL_DIR,
|
||||
singleBranch: true,
|
||||
url,
|
||||
...credentials,
|
||||
@@ -161,15 +166,15 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!(await ensureDir('/', GIT_NAMESPACE_DIR))) {
|
||||
if (!(await ensureDir(GIT_CLONE_DIR, GIT_INSOMNIA_DIR_NAME))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await ensureDir(rootDir, models.workspace.type))) {
|
||||
if (!(await ensureDir(GIT_INSOMNIA_DIR, models.workspace.type))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceBase = path.join(rootDir, models.workspace.type);
|
||||
const workspaceBase = path.join(GIT_INSOMNIA_DIR, models.workspace.type);
|
||||
const workspaceDirs = await f.readdir(workspaceBase);
|
||||
|
||||
if (workspaceDirs.length > 1) {
|
||||
@@ -219,14 +224,16 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
||||
|
||||
// Import all docs to the DB
|
||||
onConfirm: async () => {
|
||||
const { handleSetActiveWorkspace } = this.props;
|
||||
const {
|
||||
wrapperProps: { handleSetActiveWorkspace },
|
||||
} = this.props;
|
||||
|
||||
// Stop the DB from pushing updates to the UI temporarily
|
||||
const bufferId = await db.bufferChanges();
|
||||
|
||||
// Loop over all model folders in root
|
||||
for (const modelType of await f.readdir(rootDir)) {
|
||||
const modelDir = path.join(rootDir, modelType);
|
||||
for (const modelType of await f.readdir(GIT_INSOMNIA_DIR)) {
|
||||
const modelDir = path.join(GIT_INSOMNIA_DIR, modelType);
|
||||
|
||||
// Loop over all documents in model folder and save them
|
||||
for (const docFileName of await f.readdir(modelDir)) {
|
||||
|
||||
@@ -78,7 +78,7 @@ import ExportRequestsModal from '../components/modals/export-requests-modal';
|
||||
import FileSystemDriver from '../../sync/store/drivers/file-system-driver';
|
||||
import VCS from '../../sync/vcs';
|
||||
import SyncMergeModal from '../components/modals/sync-merge-modal';
|
||||
import GitVCS, { GIT_NAMESPACE_DIR } from '../../sync/git/git-vcs';
|
||||
import GitVCS, { GIT_CLONE_DIR, GIT_INSOMNIA_DIR, GIT_INTERNAL_DIR } from '../../sync/git/git-vcs';
|
||||
import NeDBPlugin from '../../sync/git/ne-db-plugin';
|
||||
import FSPlugin from '../../sync/git/fs-plugin';
|
||||
import { routableFSPlugin } from '../../sync/git/routable-fs-plugin';
|
||||
@@ -998,7 +998,6 @@ class App extends PureComponent {
|
||||
const pNeDb = NeDBPlugin.createPlugin(activeWorkspace._id);
|
||||
const pGitData = FSPlugin.createPlugin(baseDir);
|
||||
const pOtherData = FSPlugin.createPlugin(path.join(baseDir, 'other'));
|
||||
const gitSubDir = '/git';
|
||||
|
||||
const fsPlugin = routableFSPlugin(
|
||||
// All data outside the directories listed below will be stored in an 'other'
|
||||
@@ -1008,10 +1007,10 @@ class App extends PureComponent {
|
||||
{
|
||||
// All app data is stored within the a namespaced directory at the root of the
|
||||
// repository and is read/written from the local NeDB database
|
||||
[`/${GIT_NAMESPACE_DIR}`]: pNeDb,
|
||||
[GIT_INSOMNIA_DIR]: pNeDb,
|
||||
|
||||
// All git metadata is stored in a git/ directory on the filesystem
|
||||
[gitSubDir]: pGitData,
|
||||
[GIT_INTERNAL_DIR]: pGitData,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1019,9 +1018,9 @@ class App extends PureComponent {
|
||||
if (activeGitRepository.needsFullClone) {
|
||||
await models.gitRepository.update(activeGitRepository, { needsFullClone: false });
|
||||
const { credentials, uri } = activeGitRepository;
|
||||
await gitVCS.initFromClone(uri, credentials, '/', fsPlugin, gitSubDir);
|
||||
await gitVCS.initFromClone(uri, credentials, GIT_CLONE_DIR, fsPlugin, GIT_INTERNAL_DIR);
|
||||
} else {
|
||||
await gitVCS.init('/', fsPlugin, gitSubDir);
|
||||
await gitVCS.init(GIT_CLONE_DIR, fsPlugin, GIT_INTERNAL_DIR);
|
||||
}
|
||||
|
||||
// Configure basic info
|
||||
|
||||
Reference in New Issue
Block a user