cli: add initial role types and detection
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -112,7 +112,8 @@
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"yaml": "^1.10.0",
|
||||
"yml-loader": "^2.1.0",
|
||||
"yn": "^4.0.0"
|
||||
"yn": "^4.0.0",
|
||||
"zod": "^3.11.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.10.6",
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { detectPackageRole } from './detectPackageRole';
|
||||
|
||||
describe('detectPackageRole', () => {
|
||||
it('detects explicit package roles', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
backstage: {
|
||||
role: 'web-library',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'web-library',
|
||||
platform: 'web',
|
||||
});
|
||||
|
||||
expect(
|
||||
detectPackageRole({
|
||||
backstage: {
|
||||
role: 'app',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'app',
|
||||
platform: 'web',
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
detectPackageRole({
|
||||
name: 'test',
|
||||
backstage: {},
|
||||
}),
|
||||
).toThrow('Package test must specify a role in the "backstage" field');
|
||||
|
||||
expect(() =>
|
||||
detectPackageRole({
|
||||
name: 'test',
|
||||
backstage: { role: 'invalid' },
|
||||
}),
|
||||
).toThrow(`Unknown role 'invalid' in package test`);
|
||||
});
|
||||
|
||||
it('detects the role of example-app', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: 'example-app',
|
||||
private: true,
|
||||
bundled: true,
|
||||
scripts: {
|
||||
start: 'backstage-cli app:serve',
|
||||
build: 'backstage-cli app:build',
|
||||
clean: 'backstage-cli clean',
|
||||
test: 'backstage-cli test',
|
||||
'test:e2e':
|
||||
'start-server-and-test start http://localhost:3000 cy:dev',
|
||||
'test:e2e:ci':
|
||||
'start-server-and-test start http://localhost:3000 cy:run',
|
||||
lint: 'backstage-cli lint',
|
||||
'cy:dev': 'cypress open',
|
||||
'cy:run': 'cypress run',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'app',
|
||||
platform: 'web',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of example-backend', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: 'example-backend',
|
||||
main: 'dist/index.cjs.js',
|
||||
types: 'src/index.ts',
|
||||
scripts: {
|
||||
build: 'backstage-cli backend:bundle',
|
||||
'build-image':
|
||||
'docker build ../.. -f Dockerfile --tag example-backend',
|
||||
start: 'backstage-cli backend:dev',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
clean: 'backstage-cli clean',
|
||||
'migrate:create': 'knex migrate:make -x ts',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'backend',
|
||||
platform: 'node',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-catalog', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-catalog',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.esm.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli plugin:build',
|
||||
start: 'backstage-cli plugin:serve',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
diff: 'backstage-cli plugin:diff',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'plugin-frontend',
|
||||
platform: 'web',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-catalog-backend', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-catalog-backend',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.cjs.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
start: 'backstage-cli backend:dev',
|
||||
build: 'backstage-cli backend:build',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'plugin-backend',
|
||||
platform: 'node',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-catalog-react', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-catalog-react',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.esm.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli build',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'web-library',
|
||||
platform: 'web',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-catalog-common', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-catalog-common',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.cjs.js',
|
||||
module: 'dist/index.esm.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli build',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test --passWithNoTests',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'common-library',
|
||||
platform: 'common',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-catalog-backend-module-ldap', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-catalog-backend-module-ldap',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.cjs.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli backend:build',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'plugin-backend-module',
|
||||
platform: 'node',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-permission-node', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-permission-node',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
homepage: 'https://backstage.io',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.cjs.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli backend:build',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'node-library',
|
||||
platform: 'node',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects the role of @backstage/plugin-analytics-module-ga', () => {
|
||||
expect(
|
||||
detectPackageRole({
|
||||
name: '@backstage/plugin-analytics-module-ga',
|
||||
main: 'src/index.ts',
|
||||
types: 'src/index.ts',
|
||||
publishConfig: {
|
||||
access: 'public',
|
||||
main: 'dist/index.esm.js',
|
||||
types: 'dist/index.d.ts',
|
||||
},
|
||||
scripts: {
|
||||
build: 'backstage-cli plugin:build',
|
||||
start: 'backstage-cli plugin:serve',
|
||||
lint: 'backstage-cli lint',
|
||||
test: 'backstage-cli test',
|
||||
diff: 'backstage-cli plugin:diff',
|
||||
prepack: 'backstage-cli prepack',
|
||||
postpack: 'backstage-cli postpack',
|
||||
clean: 'backstage-cli clean',
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
role: 'plugin-frontend-module',
|
||||
platform: 'web',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { PackageRoleInfo } from './types';
|
||||
|
||||
const packageRoles: PackageRoleInfo[] = [
|
||||
{ role: 'app', platform: 'web' },
|
||||
{ role: 'backend', platform: 'node' },
|
||||
{ role: 'cli', platform: 'node' },
|
||||
{ role: 'web-library', platform: 'web' },
|
||||
{ role: 'node-library', platform: 'node' },
|
||||
{ role: 'common-library', platform: 'common' },
|
||||
{ role: 'plugin-frontend', platform: 'web' },
|
||||
{ role: 'plugin-frontend-module', platform: 'web' },
|
||||
{ role: 'plugin-backend', platform: 'node' },
|
||||
{ role: 'plugin-backend-module', platform: 'node' },
|
||||
];
|
||||
const roleMap = Object.fromEntries(packageRoles.map(i => [i.role, i]));
|
||||
|
||||
const backstagePackageSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
scripts: z
|
||||
.object({
|
||||
start: z.string().optional(),
|
||||
build: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
backstage: z
|
||||
.object({
|
||||
role: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
publishConfig: z
|
||||
.object({
|
||||
main: z.string().optional(),
|
||||
types: z.string().optional(),
|
||||
module: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
main: z.string().optional(),
|
||||
types: z.string().optional(),
|
||||
module: z.string().optional(),
|
||||
});
|
||||
|
||||
export function detectPackageRole(
|
||||
pkgJson: unknown,
|
||||
): PackageRoleInfo | undefined {
|
||||
const pkg = backstagePackageSchema.parse(pkgJson);
|
||||
|
||||
// If there's an explicit role, use that.
|
||||
if (pkg.backstage) {
|
||||
const { role } = pkg.backstage;
|
||||
if (!role) {
|
||||
throw new Error(
|
||||
`Package ${pkg.name} must specify a role in the "backstage" field`,
|
||||
);
|
||||
}
|
||||
|
||||
const roleInfo = packageRoles.find(r => r.role === role);
|
||||
if (!roleInfo) {
|
||||
throw new Error(`Unknown role '${role}' in package ${pkg.name}`);
|
||||
}
|
||||
return roleInfo;
|
||||
}
|
||||
|
||||
if (pkg.scripts?.start?.includes('app:serve')) {
|
||||
return roleMap.app;
|
||||
}
|
||||
if (pkg.scripts?.build?.includes('backend:bundle')) {
|
||||
return roleMap.backend;
|
||||
}
|
||||
if (pkg.name?.includes('plugin') && pkg.name?.includes('backend-module')) {
|
||||
return roleMap['plugin-backend-module'];
|
||||
}
|
||||
if (pkg.name?.includes('plugin') && pkg.name?.includes('module')) {
|
||||
return roleMap['plugin-frontend-module'];
|
||||
}
|
||||
if (pkg.scripts?.start?.includes('plugin:serve')) {
|
||||
return roleMap['plugin-frontend'];
|
||||
}
|
||||
if (pkg.scripts?.start?.includes('backend:dev')) {
|
||||
return roleMap['plugin-backend'];
|
||||
}
|
||||
|
||||
const mainEntry = pkg.publishConfig?.main || pkg.main;
|
||||
const moduleEntry = pkg.publishConfig?.module || pkg.module;
|
||||
const typesEntry = pkg.publishConfig?.types || pkg.types;
|
||||
if (typesEntry) {
|
||||
if (mainEntry && moduleEntry) {
|
||||
return roleMap['common-library'];
|
||||
}
|
||||
if (moduleEntry || mainEntry?.endsWith('.esm.js')) {
|
||||
return roleMap['web-library'];
|
||||
}
|
||||
if (mainEntry) {
|
||||
return roleMap['node-library'];
|
||||
}
|
||||
} else if (mainEntry) {
|
||||
return roleMap.cli;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type {
|
||||
PackageRoleInfo,
|
||||
PackagePlatform,
|
||||
PackageRoleName,
|
||||
} from './types';
|
||||
export { detectPackageRole } from './detectPackageRole';
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type PackageRoleName =
|
||||
| 'app'
|
||||
| 'backend'
|
||||
| 'cli'
|
||||
| 'web-library'
|
||||
| 'node-library'
|
||||
| 'common-library'
|
||||
| 'plugin-frontend'
|
||||
| 'plugin-frontend-module'
|
||||
| 'plugin-backend'
|
||||
| 'plugin-backend-module';
|
||||
|
||||
export type PackagePlatform = 'node' | 'web' | 'common';
|
||||
|
||||
export interface PackageRoleInfo {
|
||||
role: PackageRoleName;
|
||||
platform: PackagePlatform;
|
||||
}
|
||||
Reference in New Issue
Block a user