Adds a number of examples for builtin functions
This patch also proposes: - changing the pattern by which the examples are imported - adds tests for the examples Signed-off-by: Brian Fletcher <brian@roadie.io>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Adds examples to a few scaffolder actions.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Export `TemplateExample` from the `createTemplateAction` type.
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { PassThrough } from 'stream';
|
||||
import os from 'os';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { createFetchCatalogEntityAction } from './fetch';
|
||||
import { examples } from './fetch.examples';
|
||||
import yaml from 'yaml';
|
||||
|
||||
describe('catalog:fetch examples', () => {
|
||||
const getEntityByRef = jest.fn();
|
||||
const getEntitiesByRefs = jest.fn();
|
||||
|
||||
const catalogClient = {
|
||||
getEntityByRef: getEntityByRef,
|
||||
getEntitiesByRefs: getEntitiesByRefs,
|
||||
};
|
||||
|
||||
const action = createFetchCatalogEntityAction({
|
||||
catalogClient: catalogClient as unknown as CatalogApi,
|
||||
});
|
||||
|
||||
const mockContext = {
|
||||
workspacePath: os.tmpdir(),
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
secrets: { backstageToken: 'secret' },
|
||||
};
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('fetch single entity', () => {
|
||||
it('should return entity from catalog', async () => {
|
||||
getEntityByRef.mockReturnValueOnce({
|
||||
metadata: {
|
||||
namespace: 'default',
|
||||
name: 'name',
|
||||
},
|
||||
kind: 'Component',
|
||||
} as Entity);
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
});
|
||||
|
||||
expect(getEntityByRef).toHaveBeenCalledWith('component:default/name', {
|
||||
token: 'secret',
|
||||
});
|
||||
expect(mockContext.output).toHaveBeenCalledWith('entity', {
|
||||
metadata: {
|
||||
namespace: 'default',
|
||||
name: 'name',
|
||||
},
|
||||
kind: 'Component',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Fetch entity by reference',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'catalog:fetch',
|
||||
id: 'fetch',
|
||||
name: 'Fetch catalog entity',
|
||||
input: {
|
||||
entityRef: 'component:default/name',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Fetch multiple entities by referencse',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'catalog:fetch',
|
||||
id: 'fetchMultiple',
|
||||
name: 'Fetch catalog entities',
|
||||
input: {
|
||||
entityRefs: ['component:default/name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -16,45 +16,12 @@
|
||||
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
import { z } from 'zod';
|
||||
import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { examples } from './fetch.examples';
|
||||
|
||||
const id = 'catalog:fetch';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
description: 'Fetch entity by reference',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'fetch',
|
||||
name: 'Fetch catalog entity',
|
||||
input: {
|
||||
entityRef: 'component:default/name',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Fetch multiple entities by referencse',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'fetchMultiple',
|
||||
name: 'Fetch catalog entities',
|
||||
input: {
|
||||
entityRefs: ['component:default/name'],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns entity or entities from the catalog by entity reference(s).
|
||||
*
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { PassThrough } from 'stream';
|
||||
import os from 'os';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createCatalogRegisterAction } from './register';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { examples } from './register.examples';
|
||||
import yaml from 'yaml';
|
||||
|
||||
describe('catalog:register', () => {
|
||||
const integrations = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ host: 'github.com', token: 'token' }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const addLocation = jest.fn();
|
||||
const catalogClient = {
|
||||
addLocation: addLocation,
|
||||
};
|
||||
|
||||
const action = createCatalogRegisterAction({
|
||||
integrations,
|
||||
catalogClient: catalogClient as unknown as CatalogApi,
|
||||
});
|
||||
|
||||
const mockContext = {
|
||||
workspacePath: os.tmpdir(),
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should register location in catalog', async () => {
|
||||
addLocation
|
||||
.mockResolvedValueOnce({
|
||||
entities: [],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
entities: [
|
||||
{
|
||||
metadata: {
|
||||
namespace: 'default',
|
||||
name: 'test',
|
||||
},
|
||||
kind: 'Component',
|
||||
} as Entity,
|
||||
],
|
||||
});
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
});
|
||||
|
||||
expect(addLocation).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{
|
||||
type: 'url',
|
||||
target:
|
||||
'http://github.com/backstage/backstage/blob/master/catalog-info.yaml',
|
||||
},
|
||||
{},
|
||||
);
|
||||
expect(addLocation).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{
|
||||
dryRun: true,
|
||||
type: 'url',
|
||||
target:
|
||||
'http://github.com/backstage/backstage/blob/master/catalog-info.yaml',
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'entityRef',
|
||||
'component:default/test',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'catalogInfoUrl',
|
||||
'http://github.com/backstage/backstage/blob/master/catalog-info.yaml',
|
||||
);
|
||||
});
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Register with the catalog',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'catalog:register',
|
||||
id: 'register-with-catalog',
|
||||
name: 'Register with the catalog',
|
||||
input: {
|
||||
catalogInfoUrl:
|
||||
'http://github.com/backstage/backstage/blob/master/catalog-info.yaml',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -19,29 +19,10 @@ import { ScmIntegrations } from '@backstage/integration';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { stringifyEntityRef, Entity } from '@backstage/catalog-model';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './register.examples';
|
||||
|
||||
const id = 'catalog:register';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
description: 'Register with the catalog',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'register-with-catalog',
|
||||
name: 'Register with the catalog',
|
||||
input: {
|
||||
catalogInfoUrl:
|
||||
'http://github.com/backstage/backstage/blob/master/catalog-info.yaml',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers entities from a catalog descriptor file in the workspace into the software catalog.
|
||||
* @public
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
|
||||
jest.mock('fs-extra');
|
||||
|
||||
const fsMock = fs as jest.Mocked<typeof fs>;
|
||||
|
||||
import { PassThrough } from 'stream';
|
||||
import os from 'os';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { createCatalogWriteAction } from './write';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import * as yaml from 'yaml';
|
||||
import { examples } from './write.examples';
|
||||
|
||||
describe('catalog:write', () => {
|
||||
const action = createCatalogWriteAction();
|
||||
|
||||
const mockContext = {
|
||||
workspacePath: os.tmpdir(),
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should write the catalog-info.yml in the workspace', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
annotations: {},
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
owner: 'default/owner',
|
||||
},
|
||||
};
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
});
|
||||
|
||||
expect(fsMock.writeFile).toHaveBeenCalledTimes(1);
|
||||
expect(fsMock.writeFile).toHaveBeenCalledWith(
|
||||
resolvePath(mockContext.workspacePath, 'catalog-info.yaml'),
|
||||
yaml.stringify(entity),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import * as yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Write a catalog yaml file',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'catalog:write',
|
||||
id: 'create-catalog-info-file',
|
||||
name: 'Create catalog file',
|
||||
input: {
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
annotations: {},
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
owner: 'default/owner',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -19,39 +19,10 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import * as yaml from 'yaml';
|
||||
import { resolveSafeChildPath } from '@backstage/backend-common';
|
||||
import { z } from 'zod';
|
||||
import { examples } from './write.examples';
|
||||
|
||||
const id = 'catalog:write';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
description: 'Write a catalog yaml file',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'create-catalog-info-file',
|
||||
name: 'Create catalog file',
|
||||
input: {
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
annotations: {},
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
owner: 'default/owner',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Writes a catalog descriptor file containing the provided entity to a path in the workspace.
|
||||
* @public
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import { Writable } from 'stream';
|
||||
import { createDebugLogAction } from './log';
|
||||
import { join } from 'path';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './log.examples';
|
||||
|
||||
describe('debug:log examples', () => {
|
||||
const logStream = {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
};
|
||||
|
||||
const action = createDebugLogAction();
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
[`${mockContext.workspacePath}/README.md`]: '',
|
||||
[`${mockContext.workspacePath}/a-directory/index.md`]: '',
|
||||
});
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should log message', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
};
|
||||
|
||||
await action.handler(context);
|
||||
|
||||
expect(logStream.write).toHaveBeenCalledTimes(1);
|
||||
expect(logStream.write).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Hello Backstage!'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should log the workspace content, if active', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[1].example).steps[0].input,
|
||||
};
|
||||
|
||||
await action.handler(context);
|
||||
|
||||
expect(logStream.write).toHaveBeenCalledTimes(1);
|
||||
expect(logStream.write).toHaveBeenCalledWith(
|
||||
expect.stringContaining('README.md'),
|
||||
);
|
||||
expect(logStream.write).toHaveBeenCalledWith(
|
||||
expect.stringContaining(join('a-directory', 'index.md')),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Write a debug message',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'debug:log',
|
||||
id: 'write-debug-line',
|
||||
name: 'Write "Hello Backstage!" log line',
|
||||
input: {
|
||||
message: 'Hello Backstage!',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'List the workspace directory',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'debug:log',
|
||||
id: 'write-workspace-directory',
|
||||
name: 'List the workspace directory',
|
||||
input: {
|
||||
listWorkspace: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -17,43 +17,10 @@
|
||||
import { readdir, stat } from 'fs-extra';
|
||||
import { relative, join } from 'path';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './log.examples';
|
||||
|
||||
const id = 'debug:log';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
description: 'Write a debug message',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'write-debug-line',
|
||||
name: 'Write "Hello Backstage!" log line',
|
||||
input: {
|
||||
message: 'Hello Backstage!',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'List the workspace directory',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'write-workspace-directory',
|
||||
name: 'List the workspace directory',
|
||||
input: {
|
||||
listWorkspace: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Writes a message into the log or lists all files in the workspace
|
||||
*
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import { createWaitAction } from './wait';
|
||||
import { Writable } from 'stream';
|
||||
import os from 'os';
|
||||
import { examples } from './wait.examples';
|
||||
import yaml from 'yaml';
|
||||
|
||||
describe('debug:wait examples', () => {
|
||||
const action = createWaitAction();
|
||||
|
||||
const logStream = {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should wait for specified period of seconds', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
};
|
||||
const start = new Date().getTime();
|
||||
await action.handler(context);
|
||||
const end = new Date().getTime();
|
||||
expect(end - start).toBeGreaterThanOrEqual(50);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Waiting for 50 milliseconds',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'debug:wait',
|
||||
id: 'wait-milliseconds',
|
||||
name: 'Waiting for 50 milliseconds',
|
||||
input: {
|
||||
milliseconds: 50,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Waiting for 5 seconds',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'debug:wait',
|
||||
id: 'wait-5sec',
|
||||
name: 'Waiting for 5 seconds',
|
||||
input: {
|
||||
seconds: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Waiting for 1 minutes',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'debug:wait',
|
||||
id: 'wait-1min',
|
||||
name: 'Waiting for 1 minutes',
|
||||
input: {
|
||||
minutes: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -16,46 +16,13 @@
|
||||
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
import yaml from 'yaml';
|
||||
import { Duration } from 'luxon';
|
||||
import { examples } from './wait.examples';
|
||||
|
||||
const id = 'debug:wait';
|
||||
|
||||
const MAX_WAIT_TIME_IN_ISO = 'T00:00:30';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
description: 'Waiting for 5 seconds',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'wait-5sec',
|
||||
name: 'Waiting for 5 seconds',
|
||||
input: {
|
||||
seconds: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Waiting for 5 minutes',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: id,
|
||||
id: 'wait-5min',
|
||||
name: 'Waiting for 5 minutes',
|
||||
input: {
|
||||
minutes: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Waits for a certain period of time.
|
||||
*
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import yaml from 'yaml';
|
||||
|
||||
jest.mock('./helpers');
|
||||
|
||||
import os from 'os';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { getVoidLogger, UrlReader } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createFetchPlainAction } from './plain';
|
||||
import { PassThrough } from 'stream';
|
||||
import { fetchContents } from './helpers';
|
||||
import { examples } from './plain.examples';
|
||||
|
||||
describe('fetch:plain examples', () => {
|
||||
const integrations = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ host: 'github.com', token: 'token' }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
const reader: UrlReader = {
|
||||
readUrl: jest.fn(),
|
||||
readTree: jest.fn(),
|
||||
search: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const action = createFetchPlainAction({ integrations, reader });
|
||||
const mockContext = {
|
||||
workspacePath: os.tmpdir(),
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
it('should fetch plain', async () => {
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
});
|
||||
expect(fetchContents).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
outputPath: resolvePath(mockContext.workspacePath),
|
||||
fetchUrl:
|
||||
'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch plain to a specified directory', async () => {
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[1].example).steps[0].input,
|
||||
});
|
||||
expect(fetchContents).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
outputPath: resolvePath(mockContext.workspacePath, 'fetched-data'),
|
||||
fetchUrl:
|
||||
'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Downloads content and places it in the workspace.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'fetch:plain',
|
||||
id: 'fetch-plain',
|
||||
name: 'Fetch plain',
|
||||
input: {
|
||||
url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Optionally, if you would prefer the data to be downloaded to a subdirectory in the workspace you may specify the ‘targetPath’ input option.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'fetch:plain',
|
||||
id: 'fetch-plain',
|
||||
name: 'Fetch plain',
|
||||
input: {
|
||||
url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets',
|
||||
targetPath: 'fetched-data',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -18,6 +18,9 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { fetchContents } from './helpers';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { examples } from './plain.examples';
|
||||
|
||||
export const ACTION_ID = 'fetch:plain';
|
||||
|
||||
/**
|
||||
* Downloads content and places it in the workspace, or optionally
|
||||
@@ -31,7 +34,8 @@ export function createFetchPlainAction(options: {
|
||||
const { reader, integrations } = options;
|
||||
|
||||
return createTemplateAction<{ url: string; targetPath?: string }>({
|
||||
id: 'fetch:plain',
|
||||
id: ACTION_ID,
|
||||
examples,
|
||||
description:
|
||||
'Downloads content and places it in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.',
|
||||
schema: {
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import yaml from 'yaml';
|
||||
|
||||
jest.mock('./helpers');
|
||||
|
||||
import os from 'os';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { getVoidLogger, UrlReader } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createFetchPlainFileAction } from './plainFile';
|
||||
import { PassThrough } from 'stream';
|
||||
import { fetchFile } from './helpers';
|
||||
import { examples } from './plainFile.examples';
|
||||
|
||||
describe('fetch:plain:file examples', () => {
|
||||
const integrations = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ host: 'github.com', token: 'token' }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
const reader: UrlReader = {
|
||||
readUrl: jest.fn(),
|
||||
readTree: jest.fn(),
|
||||
search: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const action = createFetchPlainFileAction({ integrations, reader });
|
||||
const mockContext = {
|
||||
workspacePath: os.tmpdir(),
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
it('should fetch plain', async () => {
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: yaml.parse(examples[0].example).steps[0].input,
|
||||
});
|
||||
expect(fetchFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
outputPath: resolvePath(mockContext.workspacePath, 'target-path'),
|
||||
fetchUrl:
|
||||
'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets/Backstage%20Community%20Sessions.png',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Downloads a file and places it in the workspace.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'fetch:plain:file',
|
||||
id: 'fetch-plain-file',
|
||||
name: 'Fetch plain file',
|
||||
input: {
|
||||
url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets/Backstage%20Community%20Sessions.png',
|
||||
targetPath: 'target-path',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -18,6 +18,7 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { fetchFile } from './helpers';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { examples } from './plainFile.examples';
|
||||
|
||||
/**
|
||||
* Downloads content and places it in the workspace, or optionally
|
||||
@@ -33,6 +34,7 @@ export function createFetchPlainFileAction(options: {
|
||||
return createTemplateAction<{ url: string; targetPath: string }>({
|
||||
id: 'fetch:plain:file',
|
||||
description: 'Downloads single file and places it in the workspace.',
|
||||
examples,
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import os from 'os';
|
||||
import { join as joinPath, sep as pathSep } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import {
|
||||
getVoidLogger,
|
||||
resolvePackagePath,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { PassThrough } from 'stream';
|
||||
import { fetchContents } from './helpers';
|
||||
import { createFetchTemplateAction } from './template';
|
||||
import {
|
||||
ActionContext,
|
||||
TemplateAction,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { examples } from './template.examples';
|
||||
import yaml from 'yaml';
|
||||
|
||||
jest.mock('./helpers', () => ({
|
||||
fetchContents: jest.fn(),
|
||||
}));
|
||||
|
||||
type FetchTemplateInput = ReturnType<
|
||||
typeof createFetchTemplateAction
|
||||
> extends TemplateAction<infer U>
|
||||
? U
|
||||
: never;
|
||||
|
||||
const realFiles = Object.fromEntries(
|
||||
[
|
||||
resolvePackagePath(
|
||||
'@backstage/plugin-scaffolder-backend',
|
||||
'assets',
|
||||
'nunjucks.js.txt',
|
||||
),
|
||||
].map(k => [k, mockFs.load(k)]),
|
||||
);
|
||||
|
||||
const aBinaryFile = fs.readFileSync(
|
||||
resolvePackagePath(
|
||||
'@backstage/plugin-scaffolder-backend',
|
||||
'fixtures/test-nested-template/public/react-logo192.png',
|
||||
),
|
||||
);
|
||||
|
||||
const mockFetchContents = fetchContents as jest.MockedFunction<
|
||||
typeof fetchContents
|
||||
>;
|
||||
|
||||
describe('fetch:template examples', () => {
|
||||
let action: TemplateAction<any>;
|
||||
|
||||
const workspacePath = os.tmpdir();
|
||||
const createTemporaryDirectory: jest.MockedFunction<
|
||||
ActionContext<FetchTemplateInput>['createTemporaryDirectory']
|
||||
> = jest.fn(() =>
|
||||
Promise.resolve(
|
||||
joinPath(workspacePath, `${createTemporaryDirectory.mock.calls.length}`),
|
||||
),
|
||||
);
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
const mockContext = (input: any) => ({
|
||||
templateInfo: {
|
||||
baseUrl: 'base-url',
|
||||
entityRef: 'template:default/test-template',
|
||||
},
|
||||
input: input,
|
||||
output: jest.fn(),
|
||||
logStream: new PassThrough(),
|
||||
logger,
|
||||
workspacePath,
|
||||
createTemporaryDirectory,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
});
|
||||
|
||||
action = createFetchTemplateAction({
|
||||
reader: Symbol('UrlReader') as unknown as UrlReader,
|
||||
integrations: Symbol('Integrations') as unknown as ScmIntegrations,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe('handler', () => {
|
||||
describe('with valid input', () => {
|
||||
let context: ActionContext<FetchTemplateInput>;
|
||||
|
||||
beforeEach(async () => {
|
||||
context = mockContext(yaml.parse(examples[0].example).steps[0].input);
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
[outputPath]: {
|
||||
'an-executable.sh': mockFs.file({
|
||||
content: '#!/usr/bin/env bash',
|
||||
mode: parseInt('100755', 8),
|
||||
}),
|
||||
'empty-dir-${{ values.count }}': {},
|
||||
'static.txt': 'static content',
|
||||
'${{ values.name }}.txt': 'static content',
|
||||
subdir: {
|
||||
'templated-content.txt':
|
||||
'${{ values.name }}: ${{ values.count }}',
|
||||
},
|
||||
'.${{ values.name }}': '${{ values.itemList | dump }}',
|
||||
'a-binary-file.png': aBinaryFile,
|
||||
symlink: mockFs.symlink({
|
||||
path: 'a-binary-file.png',
|
||||
}),
|
||||
brokenSymlink: mockFs.symlink({
|
||||
path: './not-a-real-file.txt',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
await action.handler(context);
|
||||
});
|
||||
|
||||
it('uses fetchContents to retrieve the template content', () => {
|
||||
expect(mockFetchContents).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseUrl: context.templateInfo?.baseUrl,
|
||||
fetchUrl: context.input.url,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('copies files with no templating in names or content successfully', async () => {
|
||||
await expect(
|
||||
fs.readFile(`${workspacePath}/target/static.txt`, 'utf-8'),
|
||||
).resolves.toEqual('static content');
|
||||
});
|
||||
|
||||
it('copies files with templated names successfully', async () => {
|
||||
await expect(
|
||||
fs.readFile(`${workspacePath}/target/test-project.txt`, 'utf-8'),
|
||||
).resolves.toEqual('static content');
|
||||
});
|
||||
|
||||
it('copies files with templated content successfully', async () => {
|
||||
await expect(
|
||||
fs.readFile(
|
||||
`${workspacePath}/target/subdir/templated-content.txt`,
|
||||
'utf-8',
|
||||
),
|
||||
).resolves.toEqual('test-project: 1234');
|
||||
});
|
||||
|
||||
it('processes dotfiles', async () => {
|
||||
await expect(
|
||||
fs.readFile(`${workspacePath}/target/.test-project`, 'utf-8'),
|
||||
).resolves.toEqual('["first","second","third"]');
|
||||
});
|
||||
|
||||
it('copies empty directories', async () => {
|
||||
await expect(
|
||||
fs.readdir(`${workspacePath}/target/empty-dir-1234`, 'utf-8'),
|
||||
).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it('copies binary files as-is without processing them', async () => {
|
||||
await expect(
|
||||
fs.readFile(`${workspacePath}/target/a-binary-file.png`),
|
||||
).resolves.toEqual(aBinaryFile);
|
||||
});
|
||||
|
||||
it('copies files and maintains the original file permissions', async () => {
|
||||
await expect(
|
||||
fs
|
||||
.stat(`${workspacePath}/target/an-executable.sh`)
|
||||
.then(fObj => fObj.mode),
|
||||
).resolves.toEqual(parseInt('100755', 8));
|
||||
});
|
||||
|
||||
it('copies file symlinks as-is without processing them', async () => {
|
||||
await expect(
|
||||
fs
|
||||
.lstat(`${workspacePath}/target/symlink`)
|
||||
.then(i => i.isSymbolicLink()),
|
||||
).resolves.toBe(true);
|
||||
|
||||
await expect(
|
||||
fs.realpath(`${workspacePath}/target/symlink`),
|
||||
).resolves.toBe(joinPath(workspacePath, 'target', 'a-binary-file.png'));
|
||||
});
|
||||
|
||||
it('copies broken symlinks as-is without processing them', async () => {
|
||||
await expect(
|
||||
fs
|
||||
.lstat(`${workspacePath}/target/brokenSymlink`)
|
||||
.then(i => i.isSymbolicLink()),
|
||||
).resolves.toBe(true);
|
||||
|
||||
await expect(
|
||||
fs.readlink(`${workspacePath}/target/brokenSymlink`),
|
||||
).resolves.toEqual(`.${pathSep}not-a-real-file.txt`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description:
|
||||
'Downloads a skelaton directory that lives alongside the template file and fill it out with values.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'fetch:template',
|
||||
id: 'fetch-template',
|
||||
name: 'Fetch template',
|
||||
input: {
|
||||
url: './skeleton',
|
||||
targetPath: './target',
|
||||
values: {
|
||||
name: 'test-project',
|
||||
count: 1234,
|
||||
itemList: ['first', 'second', 'third'],
|
||||
showDummyFile: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
TemplateGlobal,
|
||||
} from '../../../../lib/templating/SecureTemplater';
|
||||
import { createDefaultFilters } from '../../../../lib/templating/filters';
|
||||
import { examples } from './template.examples';
|
||||
|
||||
/**
|
||||
* Downloads a skeleton, templates variables into file and directory names and content.
|
||||
@@ -70,6 +71,7 @@ export function createFetchTemplateAction(options: {
|
||||
id: 'fetch:template',
|
||||
description:
|
||||
'Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.',
|
||||
examples,
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
|
||||
@@ -20,6 +20,12 @@ import { Schema } from 'jsonschema';
|
||||
import zodToJsonSchema from 'zod-to-json-schema';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export type TemplateExample = {
|
||||
description: string;
|
||||
example: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type TemplateActionOptions<
|
||||
TActionInput extends JsonObject = {},
|
||||
@@ -29,7 +35,7 @@ export type TemplateActionOptions<
|
||||
> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: { description: string; example: string }[];
|
||||
examples?: TemplateExample[];
|
||||
supportsDryRun?: boolean;
|
||||
schema?: {
|
||||
input?: TInputSchema;
|
||||
|
||||
@@ -17,5 +17,6 @@
|
||||
export {
|
||||
createTemplateAction,
|
||||
type TemplateActionOptions,
|
||||
type TemplateExample,
|
||||
} from './createTemplateAction';
|
||||
export { type ActionContext, type TemplateAction } from './types';
|
||||
|
||||
Reference in New Issue
Block a user