scaffolder-backend: added built-in fetch:plain action + separate out fetch logic
Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
committed by
Johan Haals
parent
12771f5c34
commit
6706b360aa
@@ -21,6 +21,100 @@ import { TemplaterBuilder, TemplaterValues } from '../stages/templater';
|
||||
import { TemplateAction } from './types';
|
||||
import { InputError, UrlReader } from '@backstage/backend-common';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { JsonValue } from '@backstage/config';
|
||||
|
||||
async function fetchContents({
|
||||
urlReader,
|
||||
integrations,
|
||||
baseUrl,
|
||||
fetchUrl = '.',
|
||||
outputPath,
|
||||
}: {
|
||||
urlReader: UrlReader;
|
||||
integrations: ScmIntegrations;
|
||||
baseUrl?: string;
|
||||
fetchUrl?: JsonValue;
|
||||
outputPath: string;
|
||||
}) {
|
||||
if (typeof fetchUrl !== 'string') {
|
||||
throw new InputError(
|
||||
`Invalid url parameter, expected string, got ${typeof fetchUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
// We handle both file locations and url ones
|
||||
if (baseUrl?.startsWith('file://')) {
|
||||
const basePath = baseUrl.slice('file://'.length);
|
||||
if (isAbsolute(fetchUrl)) {
|
||||
throw new InputError(
|
||||
`Fetch URL may not be absolute for file locations, ${fetchUrl}`,
|
||||
);
|
||||
}
|
||||
const srcDir = resolvePath(basePath, '..', fetchUrl);
|
||||
await fs.copy(srcDir, outputPath);
|
||||
} else {
|
||||
let readUrl;
|
||||
|
||||
if (baseUrl) {
|
||||
const integration = integrations.byUrl(baseUrl);
|
||||
if (!integration) {
|
||||
throw new InputError(`No integration found for location ${baseUrl}`);
|
||||
}
|
||||
readUrl = integration.resolveUrl({
|
||||
url: fetchUrl,
|
||||
base: baseUrl,
|
||||
});
|
||||
} else {
|
||||
// If we don't have a baseUrl, check if our provided fetch url is absolute
|
||||
try {
|
||||
readUrl = new URL(fetchUrl).toString();
|
||||
} catch {
|
||||
throw new InputError(
|
||||
`Failed to fetch, template location could not be determined and the fetch URL is relative, ${fetchUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const res = await urlReader.readTree(readUrl);
|
||||
await res.dir({ targetDir: outputPath });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createFetchPlainAction(options: {
|
||||
urlReader: UrlReader;
|
||||
integrations: ScmIntegrations;
|
||||
}): TemplateAction {
|
||||
const { urlReader, integrations } = options;
|
||||
|
||||
return {
|
||||
id: 'fetch:plain',
|
||||
async handler(ctx) {
|
||||
ctx.logger.info('Fetching plain content from remote URL');
|
||||
|
||||
// Finally move the template result into the task workspace
|
||||
const targetPath = ctx.parameters.targetPath ?? './';
|
||||
if (typeof targetPath !== 'string') {
|
||||
throw new InputError(
|
||||
`Fetch action targetPath is not a string, got ${targetPath}`,
|
||||
);
|
||||
}
|
||||
const outputPath = resolvePath(ctx.workspacePath, targetPath);
|
||||
if (!outputPath.startsWith(ctx.workspacePath)) {
|
||||
throw new InputError(
|
||||
`Fetch action targetPath may not specify a path outside the working directory`,
|
||||
);
|
||||
}
|
||||
|
||||
await fetchContents({
|
||||
urlReader,
|
||||
integrations,
|
||||
baseUrl: ctx.baseUrl,
|
||||
fetchUrl: ctx.parameters.url,
|
||||
outputPath,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createFetchCookiecutterAction(options: {
|
||||
dockerClient: Docker;
|
||||
@@ -42,51 +136,13 @@ export function createFetchCookiecutterAction(options: {
|
||||
);
|
||||
const resultDir = resolvePath(workDir, 'result');
|
||||
|
||||
const fetchUrl = ctx.parameters.url ?? '.';
|
||||
if (typeof fetchUrl !== 'string') {
|
||||
throw new InputError(
|
||||
`Invalid url parameter, expected string, got ${typeof fetchUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
// We handle both file locations and url ones
|
||||
if (ctx.baseUrl?.startsWith('file://')) {
|
||||
const basePath = ctx.baseUrl.slice('file://'.length);
|
||||
if (isAbsolute(fetchUrl)) {
|
||||
throw new InputError(
|
||||
`Fetch URL may not be absolute for file locations, ${fetchUrl}`,
|
||||
);
|
||||
}
|
||||
const srcDir = resolvePath(basePath, '..', fetchUrl);
|
||||
await fs.copy(srcDir, templateContentsDir);
|
||||
} else {
|
||||
let readUrl;
|
||||
|
||||
if (ctx.baseUrl) {
|
||||
const integration = integrations.byUrl(ctx.baseUrl);
|
||||
if (!integration) {
|
||||
throw new InputError(
|
||||
`No integration found for location ${ctx.baseUrl}`,
|
||||
);
|
||||
}
|
||||
readUrl = integration.resolveUrl({
|
||||
url: fetchUrl,
|
||||
base: ctx.baseUrl,
|
||||
});
|
||||
} else {
|
||||
// If we don't have a baseUrl, check if our provided fetch url is absolute
|
||||
try {
|
||||
readUrl = new URL(fetchUrl).toString();
|
||||
} catch {
|
||||
throw new InputError(
|
||||
`Failed to fetch, template location could not be determined and the fetch URL is relative, ${fetchUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const res = await urlReader.readTree(readUrl);
|
||||
await res.dir({ targetDir: templateContentsDir });
|
||||
}
|
||||
}
|
||||
await fetchContents({
|
||||
urlReader,
|
||||
integrations,
|
||||
baseUrl: ctx.baseUrl,
|
||||
fetchUrl: ctx.parameters.url,
|
||||
outputPath: templateContentsDir,
|
||||
});
|
||||
|
||||
const cookiecutter = templaters.get('cookiecutter');
|
||||
if (!cookiecutter) {
|
||||
|
||||
@@ -54,7 +54,10 @@ import {
|
||||
TemplateEntityV1beta2,
|
||||
Entity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { createFetchCookiecutterAction } from '../scaffolder/tasks/builtin';
|
||||
import {
|
||||
createFetchPlainAction,
|
||||
createFetchCookiecutterAction,
|
||||
} from '../scaffolder/tasks/builtin';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
|
||||
export interface RouterOptions {
|
||||
@@ -128,6 +131,12 @@ export async function createRouter(
|
||||
templaters,
|
||||
catalogClient,
|
||||
});
|
||||
actionRegistry.register(
|
||||
createFetchPlainAction({
|
||||
urlReader,
|
||||
integrations,
|
||||
}),
|
||||
);
|
||||
actionRegistry.register(
|
||||
createFetchCookiecutterAction({
|
||||
urlReader,
|
||||
|
||||
Reference in New Issue
Block a user