Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/komodo-import. Import existing compose stacks into Komodo foxxmd.github.io/komodo-import
compose docker import komodo toml
Something went wrong. Try again.
14 kB · 366 lines
TypeScript
at feedback
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366import { describe, it } from 'mocha';import chai, { expect } from 'chai';import { detectGitRepo, parseGitStatus } from '../src/common/utils/git.js';import { stripIndents } from 'common-tags';import withLocalTmpDir from 'with-local-tmp-dir';import { mkdir, writeFile, chmod, constants } from 'node:fs/promises';import path from "path";import { buildGitStack, BuildGitStackOptions } from '../src/builders/stack/gitStack.js';import { loggerTest } from "@foxxmd/logging";import chaiAsPromised from 'chai-as-promised';import git from 'isomorphic-git';import fs from 'fs';import { execa, Options } from 'execa';import { faker } from '@faker-js/faker';import { readText } from '../src/common/utils/io.js';
chai.use(chaiAsPromised);
const DEFAULT_SERVER = 'test-server';
const defaultGitStandaloneConfig: BuildGitStackOptions = { server: DEFAULT_SERVER, logger: loggerTest}
describe('#Git', function () {
describe('#GitStatus Parsing', function () { it('detects no commits as no branch/remote', function () {
expect(parseGitStatus('## No commits yet on master')).to.eql({ raw: '## No commits yet on master' });
});
it('detects HEAD as branch but no remote', function () { const headOutput = stripIndents`## HEAD (no branch) ?? somefile/`;
const parsed = parseGitStatus(headOutput);
expect(parsed.branch).to.eq('HEAD'); expect(parsed.remote).is.undefined; });
it('detects branch with remote', function () {
const outputs = [ { out: '## main...origin/main', expect: { branch: 'main', remote: 'origin', remoteBranch: 'main' } }, { out: '## 3.18...origin/3.18', expect: { branch: '3.18', remote: 'origin', remoteBranch: '3.18' } } ]
for (const o of outputs) { const parsed = parseGitStatus(o.out);
expect(parsed.branch).to.eq(o.expect.branch); expect(parsed.remote).to.eq(o.expect.remote); expect(parsed.remoteBranch).to.eq(o.expect.remoteBranch); }
});
it('detects local branch with different name than remote', function () { const output = stripIndents`## main-mine...origin/main`;
const parsed = parseGitStatus(output);
expect(parsed.branch).to.eq('main-mine'); expect(parsed.remote).to.eq('origin'); expect(parsed.remoteBranch).to.eq('main'); });
it('detects local branch with no remote', function () {
const output = stripIndents`## temp-fix`;
const parsed = parseGitStatus(output);
expect(parsed.branch).to.eq('temp-fix'); expect(parsed.remote).to.be.undefined; });
it('does not add extraneous status to remote branch', function () {
const checks = [ '## main-mine...origin/main [behind 3]', '## main-mine...origin/main [ahead 1]' ]
for (const c of checks) {
const parsed = parseGitStatus(c);
expect(parsed.branch).to.eq('main-mine'); expect(parsed.remote).to.eq('origin'); expect(parsed.remoteBranch).to.eq('main'); }
}); });
describe('#GitRepo', function () {
it(`detects as not a git repo when no .git folder is present`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1');
await mkdir(dir); await expect(detectGitRepo(dir)).to.eventually.be.rejected; }, { unsafeCleanup: true }); });
it(`detects as not a git repo when .git folder is ill-defined`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir, { recursive: true }); await mkdir(path.join(dir, '.git')) await expect(detectGitRepo(dir)).to.eventually.be.rejected; }, { unsafeCleanup: true }); });
it(`detects as not a suitable git repo when no tracked branch`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir, { recursive: true }); await git.init({ fs, dir }); await expect(detectGitRepo(dir)).to.eventually.be.rejectedWith(/Could not determine tracked branch/); }, { unsafeCleanup: true }); });
it(`detects as not a suitable git repo when no remote repository`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir, { recursive: true }); await git.init({ fs, dir }); await git.commit({ fs, dir: dir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' }); await expect(detectGitRepo(dir)).to.eventually.be.rejectedWith(/Could not parse remote branch/); }, { unsafeCleanup: true }); });
it(`detects as not a suitable git repo when tracked branch does not have a remote`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); const localDir = path.join(dir, 'local'); await mkdir(localDir); const remoteDir = path.join(dir, 'remote'); await mkdir(remoteDir); await git.init({ fs, dir: localDir }); await git.commit({ fs, dir: localDir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' }); await git.init({ fs, dir: remoteDir }); await git.addRemote({ fs, remote: 'origin', url: '../remote', dir: remoteDir }); await expect(detectGitRepo(localDir)).to.eventually.be.rejectedWith(/Could not parse remote branch/); }, { unsafeCleanup: true }); });
it(`detects as a suitable git repo when tracked branch has a remote`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); const localDir = path.join(dir, 'local'); //await mkdir(localDir); const remoteDir = path.join(dir, 'remote'); await mkdir(remoteDir); await git.init({ fs, dir: remoteDir }); await git.commit({ fs, dir: remoteDir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' }); // cannot yet clone local repo with isomorphic git // https://github.com/isomorphic-git/isomorphic-git/issues/1263 await execa({ cwd: dir })`git clone remote local` await expect(detectGitRepo(localDir)).to.eventually.be.fulfilled; }, { unsafeCleanup: true }); }); });
describe('#GitStack', function () {
it(`creates a valid stack from top-dir git`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); await git.init({ fs, dir }); await git.commit({ fs, dir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' }); const rc = await readText(path.join(dir, '.git', 'config')); const [fc, fakeData] = fakeGitConfig(); await writeFile(path.join(dir, '.git', 'config'), `${rc}\n${fc}`);
const stack = await buildGitStack(dir, defaultGitStandaloneConfig); expect(stack.config.repo).eq(`${fakeData.user}/${fakeData.repo}`);
}, { unsafeCleanup: true }); });
it(`uses relative path for compose files`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); await mkdir(path.join(dir, 'nested')) await writeFile(path.join(dir, 'nested', 'compose.yml'), ''); await git.init({ fs, dir }); await git.commit({ fs, dir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' });
const rc = await readText(path.join(dir, '.git', 'config')); const [fc, fakeData] = fakeGitConfig(); await writeFile(path.join(dir, '.git', 'config'), `${rc}\n${fc}`);
const stack = await buildGitStack(dir, defaultGitStandaloneConfig); expect(stack.config.file_paths).to.not.be.undefined; expect(stack.config.file_paths[0]).eq('nested/compose.yml');
}, { unsafeCleanup: true }); });
it(`sets relative path from parent when git is above dir`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); await mkdir(path.join(dir, 'nested', 'docker'), {recursive: true}) await writeFile(path.join(dir, 'nested', 'docker', 'compose.yml'), ''); await git.init({ fs, dir }); await git.commit({ fs, dir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' });
const rc = await readText(path.join(dir, '.git', 'config')); const [fc, fakeData] = fakeGitConfig(); await writeFile(path.join(dir, '.git', 'config'), `${rc}\n${fc}`);
const stack = await buildGitStack(path.join(dir, 'nested'), defaultGitStandaloneConfig); expect(stack.config.run_directory).to.not.be.undefined; expect(stack.config.run_directory).eq('nested'); expect(stack.config.file_paths).to.not.be.undefined; expect(stack.config.file_paths[0]).eq('docker/compose.yml');
}, { unsafeCleanup: true }); });
it(`determines relative path for compose files when argument provided`, async function () { await withLocalTmpDir(async () => { const dir = path.join(process.cwd(), 'test_1'); await mkdir(dir); await mkdir(path.join(dir, 'nested', 'docker'), {recursive: true}) await writeFile(path.join(dir, 'nested', 'docker', 'compose.yml'), ''); await git.init({ fs, dir }); await git.commit({ fs, dir, author: { name: 'Mr. Test', email: 'mrtest@example.com', }, message: 'Added the a.txt file' });
const rc = await readText(path.join(dir, '.git', 'config')); const [fc, fakeData] = fakeGitConfig(); await writeFile(path.join(dir, '.git', 'config'), `${rc}\n${fc}`);
const stack = await buildGitStack(path.join(dir, 'nested'), { ...defaultGitStandaloneConfig, composeFiles: [path.join(dir, 'nested', 'docker', 'compose.yml')] }); expect(stack.config.run_directory).to.not.be.undefined; expect(stack.config.run_directory).eq('nested'); expect(stack.config.file_paths).to.not.be.undefined; expect(stack.config.file_paths[0]).eq('docker/compose.yml');
}, { unsafeCleanup: true }); });
});});
interface FakeGitConfig { user?: string repo?: string domain?: string}const fakeGitConfig = (options: FakeGitConfig = {}): [string, Required<FakeGitConfig>] => {
const { domain = 'github.com', user = faker.internet.userName(), repo = `${faker.animal.type()}-${faker.word.adjective()}` } = options;
const used: Required<FakeGitConfig> = { domain, user, repo };
const configContents = `[remote "origin"] url = https://${domain}/${user}/${repo} fetch = +refs/heads/*:refs/remotes/origin/*[branch "master"] remote = origin merge = refs/heads/master`; return [configContents, used]}