Git clone fails on Windows. This fixes it. (#2194)

Co-authored-by: Gregory Schier <gschier1990@gmail.com>
This commit is contained in:
Opender Singh
2020-05-29 06:13:13 +12:00
committed by GitHub
parent 4387381fd1
commit 5cf2f59e46
12 changed files with 258 additions and 104 deletions

View 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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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');
});
});
});

View File

@@ -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');

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 || '';
}

View File

@@ -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)) {

View File

@@ -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