Skip to content

Commit

Permalink
#2426 Allow import branch specification (#2432)
Browse files Browse the repository at this point in the history
  • Loading branch information
atopper authored Oct 4, 2024
1 parent 069c0f1 commit 156c803
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 17 deletions.
4 changes: 3 additions & 1 deletion src/git-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import git from 'isomorphic-git';
const cache = {};

export default class GitUtils {
static DEFAULT_BRANCH = 'main';

/**
* Determines whether the working tree directory contains uncommitted or unstaged changes.
*
Expand Down Expand Up @@ -141,7 +143,7 @@ export default class GitUtils {
* @param {string} fallback fallback value if no branch or tag is found
* @returns {Promise<string>} current branch or tag
*/
static async getBranch(dir, fallback = 'main') {
static async getBranch(dir, fallback = this.DEFAULT_BRANCH) {
// current commit sha
const rev = await git.resolveRef({ fs, dir, ref: 'HEAD' });
// reverse-lookup tag from commit sha
Expand Down
70 changes: 57 additions & 13 deletions src/import.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import path from 'path';
import chalk from 'chalk-template';
import git from 'isomorphic-git';
import http from 'isomorphic-git/http/node/index.js';
import GitUtils from './git-utils.js';
import { HelixImportProject } from './server/HelixImportProject.js';
import pkgJson from './package.cjs';
import { AbstractServerCommand } from './abstract-server.cmd.js';
Expand All @@ -36,7 +37,20 @@ export default class ImportCommand extends AbstractServerCommand {
}

withUIRepo(value) {
this._uiRepo = value;
this._uiBranch = GitUtils.DEFAULT_BRANCH;
try {
const url = new URL(value);
if (url.hash) {
this._uiBranch = url.hash.substring(1);
}
// Ensure a dangling hash does not create an invalid uiRepo.
url.hash = '';
this._uiRepo = url.href;
} catch (e) {
this.log.error(`Could not process UI Repo correctly: ${value} - ${e.message}`);
throw e;
}

return this;
}

Expand All @@ -45,6 +59,27 @@ export default class ImportCommand extends AbstractServerCommand {
return this;
}

/**
* Ensure the UI branch the caller specified (or defaulted to) is the one being used.
* @param uiFolder
* @returns {Promise<void>}
*/
async handleUIBranch(uiFolder) {
const branch = await GitUtils.getBranch(uiFolder);

if (branch !== this._uiBranch) {
this.log.info(
chalk`AEM Importer UI was on branch {yellow ${branch}}. Switching to {green ${this._uiBranch}}.`,
);
await git.checkout({
fs: fse,
http,
dir: uiFolder,
ref: this._uiBranch,
});
}
}

async setupImporterUI() {
const importerFolder = path.join(this.directory, this._importerSubPath);
await fse.ensureDir(importerFolder);
Expand All @@ -54,32 +89,41 @@ export default class ImportCommand extends AbstractServerCommand {
const exists = await fse.pathExists(uiFolder);
if (!exists) {
this.log.info('AEM Importer UI needs to be installed.');
this.log.info(`Cloning ${this._uiRepo} in ${importerFolder}.`);
this.log.info(`Cloning ${this._uiRepo} (branch: ${this._uiBranch}) in ${importerFolder}.`);
// clone the ui project
await git.clone({
fs: fse,
http,
dir: uiFolder,
url: this._uiRepo,
depth: 1,
singleBranch: true,
});
this.log.info(`AEM Importer UI is ready. v${await getUIVersion()}`);
try {
await git.clone({
fs: fse,
http,
dir: uiFolder,
url: this._uiRepo,
ref: this._uiBranch,
depth: 1,
});

this.log.info(`AEM Importer UI is ready. v${await getUIVersion()} (branch: ${this._uiBranch})`);
} catch (e) {
this.log.error(`AEM Importer UI clone failed: ${e.message}`);
throw e;
}
} else {
this.log.info('Fetching latest version of the AEM Importer UI...');
// clone the ui project
await this.handleUIBranch(uiFolder);

// Pull the ui project
await git.pull({
fs: fse,
http,
dir: uiFolder,
url: this._uiRepo,
ref: this._uiBranch,
depth: 1,
singleBranch: true,
author: {
name: 'hlx import',
},
});
this.log.info(`AEM Importer UI is up-to-date. v${await getUIVersion()}`);
this.log.info(`AEM Importer UI is up-to-date. v${await getUIVersion()} (branch: ${this._uiBranch})`);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function up() {
})
.option('ui-repo', {
alias: 'uiRepo',
describe: 'Git repository for the AEM Importer UI',
describe: 'Git repository for the AEM Importer UI. A fragment may be used to indicated a branch other than main.',
type: 'string',
default: 'https://github.com/adobe/helix-importer-ui',
})
Expand Down
149 changes: 147 additions & 2 deletions test/import-cmd.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import assert from 'assert';
import path from 'path';
import fse from 'fs-extra';
import { h1NoCache as fetchContext } from '@adobe/fetch';
import { Nock, assertHttp, createTestRoot } from './utils.js';
import GitUtils from '../src/git-utils.js';
import {
Nock,
assertHttp,
createTestRoot,
getBranch,
} from './utils.js';
import ImportCommand from '../src/import.cmd.js';

const { fetch } = fetchContext({ rejectUnauthorized: false });
Expand Down Expand Up @@ -558,7 +564,7 @@ describe('Import command - importer ui', function suite() {

this.timeout(240000);

it('import command installs the importer ui', (done) => {
it('import command installs the main importer ui', (done) => {
let error = null;
const cmd = new ImportCommand()
.withDirectory(testDir)
Expand All @@ -576,6 +582,7 @@ describe('Import command - importer ui', function suite() {
try {
assert.ok(await fse.pathExists(`${testDir}/tools/importer/helix-importer-ui/index.html`), 'helix-importer-ui project has been cloned');
await assertHttp(`http://127.0.0.1:${cmd.project.server.port}/tools/importer/helix-importer-ui/index.html`, 200);
assert.equal(getBranch(`${testDir}/tools/importer/helix-importer-ui`), 'main');

await myDone();
} catch (e) {
Expand All @@ -588,4 +595,142 @@ describe('Import command - importer ui', function suite() {
.run()
.catch(done);
});

it('import command installs a importer ui test branch', (done) => {
// This assumes the branch 'origin/test' will exist and be available.
let error = null;
const cmd = new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('https://github.com/adobe/helix-importer-ui#test')
.withHttpPort(0);

const myDone = (err) => {
error = err;
return cmd.stop();
};

cmd
.on('started', async () => {
try {
assert.ok(await fse.pathExists(`${testDir}/tools/importer/helix-importer-ui/index.html`), 'helix-importer-ui project has been cloned');
await assertHttp(`http://127.0.0.1:${cmd.project.server.port}/tools/importer/helix-importer-ui/index.html`, 200);
assert.equal(getBranch(`${testDir}/tools/importer/helix-importer-ui`), 'test');

await myDone();
} catch (e) {
await myDone(e);
}
})
.on('stopped', () => {
done(error);
})
.run()
.catch(done);
});

it('import command fails to install a bad branch', (done) => {
// This assumes the branch 'origin/bad' does not exist.
let error = null;
const cmd = new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('https://github.com/adobe/helix-importer-ui#bad')
.withHttpPort(0);

const myDone = (err) => {
error = err;
return cmd.stop();
};

cmd
.on('started', async () => {
try {
assert.fail('Should not have been able to clone the importer ui');
await myDone();
} catch (e) {
await myDone(e);
}
})
.on('stopped', () => {
done(error);
})
.run()
.catch(() => {
done();
});
});

it('import command fails to install an invalid ui-repo', () => {
try {
new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('not a url#hash')
.withHttpPort(0);
assert.fail('Importer UI repo URL was invalid - exception expected.');
} catch (e) {
assert.equal(e.message, 'Invalid URL');
}
});

/**
* Testing starting the importer on the default (main) branch, and switching it to a test
* branch.
*/
it('import command handles an empty branch', () => {
try {
const cmd = new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('https://github.com/adobe/helix-importer-ui#')
.withHttpPort(0);
// eslint-disable-next-line no-underscore-dangle
assert.equal(cmd._uiRepo, 'https://github.com/adobe/helix-importer-ui');
// eslint-disable-next-line no-underscore-dangle
assert.equal(cmd._uiBranch, GitUtils.DEFAULT_BRANCH);
} catch (e) {
assert.fail('Importer UI repo URL was valid - exception not expected.');
}
});

it('import command switches branch', (done) => {
const cmd = new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('https://github.com/adobe/helix-importer-ui#')
.withHttpPort(0);
cmd
.on('started', async () => {
const mainBranch = await getBranch(`${testDir}/tools/importer/helix-importer-ui`);
assert.equal(mainBranch, 'main');

await cmd.stop();
})
.on('stopped', async () => {
// Main branch server has stopped. Now switch it to 'test'.
const cmd2 = new ImportCommand()
.withDirectory(testDir)
.withOpen(false)
.withUIRepo('https://github.com/adobe/helix-importer-ui#test')
.withHttpPort(0);
cmd2
.on('started', async () => {
const testBranch = await getBranch(`${testDir}/tools/importer/helix-importer-ui`);
assert.equal(testBranch, 'test');
await cmd2.stop();
})
.on('stopped', () => {
done();
})
.run()
.catch((e) => {
done(e);
});
})
.run()
.catch((e) => {
done(e);
});
});
});
12 changes: 12 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,21 @@ export function switchBranch(dir, branch) {
shell.cd(dir);
shell.exec(`git checkout -b ${branch}`);
shell.cd(pwd);
// eslint-disable-next-line no-console
console.log(`switched to branch ${branch} in ${dir}`);
}

export function getBranch(dir) {
const pwd = shell.pwd();
shell.cd(dir);
const { stdout } = shell.exec('git rev-parse --abbrev-ref HEAD');
shell.cd(pwd);
// eslint-disable-next-line no-console
console.log(`The current branch is ${stdout.trim()} in ${dir}`);

return stdout.trim();
}

export function clearHelixEnv() {
const deleted = {};
Object.keys(process.env).filter((key) => key.startsWith(('AEM_'))).forEach((key) => {
Expand Down

0 comments on commit 156c803

Please sign in to comment.