Project: Backstage (App Definition and Development / Application Definition & Image Build)
File: template-testing-dry-run.md

```js /* Copyright 2022 The Backstage Authors * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ / A CLI that helps you test Backstage software templates * @packageDocumentation */ import { dirname, join, relative } from 'node:path'; import { readFile, writeFile } from 'node:fs/promises'; import { gzipSync } from 'node:zlib'; import { ensureDir } from 'fs-extra'; import { program } from 'commander'; import fetch from 'node-fetch'; import ora from 'ora'; import readdir from 'recursive-readdir'; import { parse } from 'yaml'; import { version } from '../../../package.json'; import type { ScaffolderDryRunResponse } from '@backstage/plugin-scaffolder'; const loadDirectoryContents = async (template_path: string) => { const spinner = ora('Loading template').start(); const files = await await readdir(template_path, ['.git']); const contents = await Promise.all( files.map(async p => { return { path: relative(template_path, p), base64Content: (await readFile(p)).toString('base64'), }; }), ); spinner.succeed(); return contents; }; const writeResultContents = async ( root: string, directoryContents: ScaffolderDryRunResponse['directoryContents'], ) => { const directories = new Set( directoryContents.map(({ path }) => dirname(join(root, path))), ); await Promise.all(Array.from(directories).map(d => ensureDir(d))); await Promise.all( directoryContents.map(async ({ path, base64Content, executable }) => writeFile(join(root, path), Buffer.from(base64Content, 'base64'), { mode: executable ? 0o755 : 0o644, }), ), ); }; const api = async ( bodyObj: Record<string, any>, { baseURL, token }: { baseURL: string; token: string | false }, ) => { const body = gzipSync(JSON.stringify(bodyObj)); return fetch(new URL('/api/scaffolder/v2/dry-run', baseURL), { method: 'POST', headers: { Authorization: `Bearer ${token || ''}`, 'Content-Type': 'application/json', 'Content-Encoding': 'gzip', }, body, }); }; const handle = async ( baseURL: string, template_path: string, data: string, target: string, { token }: { token: string | false }, ) => { const directoryContents = await loadDirectoryContents(template_path); const values = parse(await readFile(data, 'utf-8')); const secrets = {}; const template = parse( await readFile(`${template_path}/template.yaml`, 'utf-8'), ); const response = await api( { directoryContents, values, secrets, template, }, { baseURL, token }, ); if (!response.ok) { const contentType = response.headers.get('content-type'); if (contentType?.startsWith('application/json')) { const responseData = await response.json(); if (responseData.error) { throw responseData.error; } if (responseData.errors) { throw responseData.errors; } throw responseData; } throw await response.text(); } const { log, directoryContents: resultContents } = (await response.json()) as ScaffolderDryRunResponse; for (const logEntry of log) { console.log(logEntry.body.message); } await writeResultContents(target, resultContents); }; const main = async (argv: string[]) => { program .name('backstage-scaffolder') .version(version) .description('Creates a dry-run output of a given template') .option('-t, --token <value>', 'JWT to use for auth') .argument('url', 'URL of your Backstage instance') .argument('template-path', 'Source directory of template') .argument('data-path', 'YAML file with input to render') .argument('target', 'Output directory') .action(handle); await program.parseAsync(argv); process.exit(); }; process.on('unhandledRejection', rejection => { console.error(rejection); process.exit(1); }); main(process.argv); ```