Moved git clone to a helper functions and added tests for preparers (#2112)

This commit is contained in:
Sebastian Qvarfordt
2020-08-25 16:21:53 +02:00
committed by GitHub
parent 5f9f41b789
commit f6826d9107
4 changed files with 66 additions and 69 deletions
@@ -15,6 +15,12 @@
*/
import { DirectoryPreparer } from './dir';
import { getVoidLogger } from '@backstage/backend-common';
import { checkoutGitRepository } from './helpers';
jest.mock('./helpers', () => ({
...jest.requireActual<{}>('./helpers'),
checkoutGitRepository: jest.fn(() => '/tmp/backstage-repo/org/name/branch/'),
}));
const logger = getVoidLogger();
@@ -59,4 +65,19 @@ describe('directory preparer', () => {
'/our-documentation/techdocs',
);
});
it('should merge managed-by-location and techdocs-ref when managed-by-location is a git repository', async () => {
const directoryPreparer = new DirectoryPreparer(logger);
const mockEntity = createMockEntity({
'backstage.io/managed-by-location':
'github:https://github.com/spotify/backstage/blob/master/catalog-info.yaml',
'backstage.io/techdocs-ref': 'dir:./docs',
});
expect(await directoryPreparer.prepare(mockEntity)).toEqual(
'/tmp/backstage-repo/org/name/branch/docs',
);
expect(checkoutGitRepository).toHaveBeenCalledTimes(1);
});
});
@@ -13,14 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs-extra';
import os from 'os';
import { PreparerBase } from './types';
import { Entity } from '@backstage/catalog-model';
import path from 'path';
import { parseReferenceAnnotation } from './helpers';
import { parseReferenceAnnotation, checkoutGitRepository } from './helpers';
import { InputError } from '@backstage/backend-common';
import { Clone } from 'nodegit';
import parseGitUrl from 'git-url-parse';
import { Logger } from 'winston';
@@ -31,41 +28,6 @@ export class DirectoryPreparer implements PreparerBase {
this.logger = logger;
}
private async cloneGithubRepo(entity: Entity) {
const { type, target } = parseReferenceAnnotation(
'backstage.io/managed-by-location',
entity,
);
if (type !== 'github') {
throw new InputError(`Wrong target type: ${type}, should be 'github'`);
}
const parsedGitLocation = parseGitUrl(target);
const repositoryTmpPath = path.join(
// fs.realpathSync fixes a problem with macOS returning a path that is a symlink
fs.realpathSync(os.tmpdir()),
'backstage-repo',
parsedGitLocation.source,
parsedGitLocation.owner,
parsedGitLocation.name,
parsedGitLocation.ref,
);
if (fs.existsSync(repositoryTmpPath)) {
return repositoryTmpPath;
}
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
this.logger.debug(
`[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`,
);
fs.mkdirSync(repositoryTmpPath, { recursive: true });
await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {});
return repositoryTmpPath;
}
private async resolveManagedByLocationToDir(entity: Entity) {
const { type, target } = parseReferenceAnnotation(
'backstage.io/managed-by-location',
@@ -78,7 +40,7 @@ export class DirectoryPreparer implements PreparerBase {
switch (type) {
case 'github': {
const parsedGitLocation = parseGitUrl(target);
const repoLocation = await this.cloneGithubRepo(entity);
const repoLocation = await checkoutGitRepository(target);
return path.dirname(
path.join(repoLocation, parsedGitLocation.filepath),
@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs-extra';
import path from 'path';
import os from 'os';
import { Entity } from '@backstage/catalog-model';
import { InputError } from '@backstage/backend-common';
import { PreparerBase } from './types';
import parseGitUrl from 'git-url-parse';
import { Clone } from 'nodegit';
import { parseReferenceAnnotation } from './helpers';
import { parseReferenceAnnotation, checkoutGitRepository } from './helpers';
import { Logger } from 'winston';
export class GithubPreparer implements PreparerBase {
@@ -41,32 +38,14 @@ export class GithubPreparer implements PreparerBase {
throw new InputError(`Wrong target type: ${type}, should be 'github'`);
}
const parsedGitLocation = parseGitUrl(target);
const repositoryTmpPath = path.join(
// fs.realpathSync fixes a problem with macOS returning a path that is a symlink
fs.realpathSync(os.tmpdir()),
'backstage-repo',
parsedGitLocation.source,
parsedGitLocation.owner,
parsedGitLocation.name,
parsedGitLocation.ref,
);
try {
const repoPath = await checkoutGitRepository(target);
if (fs.existsSync(repositoryTmpPath)) {
this.logger.debug(
`[TechDocs] Found repository already checked out at ${repositoryTmpPath}`,
);
return path.join(repositoryTmpPath, parsedGitLocation.filepath);
const parsedGitLocation = parseGitUrl(target);
return path.join(repoPath, parsedGitLocation.filepath);
} catch (error) {
this.logger.debug(`Repo checkout failed with error ${error.message}`);
throw error;
}
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
this.logger.debug(
`[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`,
);
fs.mkdirSync(repositoryTmpPath, { recursive: true });
await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {});
return path.join(repositoryTmpPath, parsedGitLocation.filepath);
}
}
@@ -16,6 +16,11 @@
import { Entity } from '@backstage/catalog-model';
import { InputError } from '@backstage/backend-common';
import { RemoteProtocol } from './types';
import parseGitUrl from 'git-url-parse';
import { Clone } from 'nodegit';
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
export type ParsedLocationAnnotation = {
type: RemoteProtocol;
@@ -52,3 +57,33 @@ export const parseReferenceAnnotation = (
target,
};
};
export const clearGithubRepositoryCache = () => {
fs.removeSync(path.join(fs.realpathSync(os.tmpdir()), 'backstage-repo'));
};
export const checkoutGitRepository = async (
repoUrl: string,
): Promise<string> => {
const parsedGitLocation = parseGitUrl(repoUrl);
const repositoryTmpPath = path.join(
// fs.realpathSync fixes a problem with macOS returning a path that is a symlink
fs.realpathSync(os.tmpdir()),
'backstage-repo',
parsedGitLocation.source,
parsedGitLocation.owner,
parsedGitLocation.name,
parsedGitLocation.ref,
);
if (fs.existsSync(repositoryTmpPath)) {
return repositoryTmpPath;
}
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
fs.mkdirSync(repositoryTmpPath, { recursive: true });
await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {});
return repositoryTmpPath;
};