Merge pull request #30595 from backstage/blam/experimental-nfs

`create-app`: Add support for `--next` flag
This commit is contained in:
Ben Lambert
2025-07-29 13:44:01 +02:00
committed by GitHub
58 changed files with 2171 additions and 11 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Created a flag for scaffolding apps using the new frontend system.
+1
View File
@@ -8,6 +8,7 @@
Usage: backstage-create-app [options]
Options:
--next
--path [directory]
--skip-install
--template-path [directory]
+27 -6
View File
@@ -57,7 +57,7 @@ describe('command entrypoint', () => {
jest.resetAllMocks();
});
test('should call expected tasks with no path option', async () => {
it('should call expected tasks with no `--path` option', async () => {
const cmd = {} as unknown as Command;
await createApp(cmd);
expect(checkAppExistsMock).toHaveBeenCalled();
@@ -78,7 +78,7 @@ describe('command entrypoint', () => {
expect(buildAppMock).toHaveBeenCalled();
});
it('should call expected tasks with path option', async () => {
it('should call expected tasks with `--path` option', async () => {
const cmd = { path: 'myDirectory' } as unknown as Command;
await createApp(cmd);
expect(checkPathExistsMock).toHaveBeenCalled();
@@ -96,7 +96,28 @@ describe('command entrypoint', () => {
expect(buildAppMock).toHaveBeenCalled();
});
it('should call expected tasks with relative --template-path option', async () => {
it('should call expected tasks when `--next` is supplied', async () => {
const cmd = { next: true } as unknown as Command;
await createApp(cmd);
expect(checkAppExistsMock).toHaveBeenCalled();
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
expect(templatingMock.mock.lastCall?.[0]).toEqual(
findPaths(__dirname).resolveTarget(
'packages',
'create-app',
'templates',
'next-app',
),
);
expect(templatingMock.mock.lastCall?.[1]).toContain(
path.join(tmpdir(), 'MyApp'),
);
expect(moveAppMock).toHaveBeenCalled();
expect(buildAppMock).toHaveBeenCalled();
});
it('should call expected tasks with relative `--template-path` option', async () => {
const cmd = {
path: 'myDirectory',
templatePath: 'templateDirectory',
@@ -112,7 +133,7 @@ describe('command entrypoint', () => {
expect(buildAppMock).toHaveBeenCalled();
});
it('should call expected tasks with absolute --template-path option', async () => {
it('should call expected tasks with absolute `--template-path` option', async () => {
const cmd = {
path: 'myDirectory',
templatePath: path.resolve('somewhere', 'templateDirectory'),
@@ -128,13 +149,13 @@ describe('command entrypoint', () => {
expect(buildAppMock).toHaveBeenCalled();
});
it('should not call `buildAppTask` when `skipInstall` is supplied', async () => {
it('should not call `buildAppTask()` when `--skip-install` is supplied', async () => {
const cmd = { skipInstall: true } as unknown as Command;
await createApp(cmd);
expect(buildAppMock).not.toHaveBeenCalled();
});
it('should not call `initGitRepository` when `gitConfig` is undefined', async () => {
it('should not call `initGitRepository()` when `gitConfig` is undefined', async () => {
const cmd = {} as unknown as Command;
readGitConfig.mockResolvedValue(undefined);
await createApp(cmd);
+7 -1
View File
@@ -65,9 +65,15 @@ export default async (opts: OptionValues): Promise<void> => {
},
]);
// Pick the built-in template based on the --next flag
const builtInTemplate = opts.next
? paths.resolveOwn('templates/next-app')
: paths.resolveOwn('templates/default-app');
// Use `--template-path` argument as template when specified. Otherwise, use the default template.
const templateDir = opts.templatePath
? paths.resolveTarget(opts.templatePath)
: paths.resolveOwn('templates/default-app');
: builtInTemplate;
// Use `--path` argument as application directory when specified, otherwise
// create a directory using `answers.name`
+1
View File
@@ -31,6 +31,7 @@ const main = (argv: string[]) => {
.name('backstage-create-app')
.version(version)
.description('Creates a new app in a new directory or specified path')
.option('--next', 'Use the next generation of the app template')
.option(
'--path [directory]',
'Location to store the app defaulting to a new folder with the app name',
+4 -4
View File
@@ -85,6 +85,7 @@ export class Task {
* @param templateDir - location containing template files
* @param destinationDir - location to save templated project
* @param context - template parameters
* @param excludedDirs - template files to exclude
*/
export async function templatingTask(
templateDir: string,
@@ -96,10 +97,9 @@ export async function templatingTask(
});
for (const file of files) {
const destinationFile = resolvePath(
destinationDir,
relativePath(templateDir, file),
);
const filePath = relativePath(templateDir, file);
const destinationFile = resolvePath(destinationDir, filePath);
await fs.ensureDir(dirname(destinationFile));
if (file.endsWith('.hbs')) {
+8
View File
@@ -32,12 +32,16 @@ leaving any imports in place.
import { version as root } from '../../../../package.json';
import { version as appDefaults } from '../../../app-defaults/package.json';
import { version as frontendDefaults } from '../../../frontend-defaults/package.json';
import { version as frontendPluginApi } from '../../../frontend-plugin-api/package.json';
import { version as backendDefaults } from '../../../backend-defaults/package.json';
import { version as catalogClient } from '../../../catalog-client/package.json';
import { version as catalogModel } from '../../../catalog-model/package.json';
import { version as cli } from '../../../cli/package.json';
import { version as config } from '../../../config/package.json';
import { version as coreAppApi } from '../../../core-app-api/package.json';
import { version as coreCompatApi } from '../../../core-compat-api/package.json';
import { version as frontendTestUtils } from '../../../frontend-test-utils/package.json';
import { version as coreComponents } from '../../../core-components/package.json';
import { version as corePluginApi } from '../../../core-plugin-api/package.json';
import { version as e2eTestUtils } from '../../../e2e-test-utils/package.json';
@@ -90,14 +94,18 @@ import { version as pluginUserSettings } from '../../../../plugins/user-settings
export const packageVersions = {
root,
'@backstage/app-defaults': appDefaults,
'@backstage/frontend-defaults': frontendDefaults,
'@backstage/frontend-plugin-api': frontendPluginApi,
'@backstage/backend-defaults': backendDefaults,
'@backstage/catalog-client': catalogClient,
'@backstage/catalog-model': catalogModel,
'@backstage/cli': cli,
'@backstage/config': config,
'@backstage/core-app-api': coreAppApi,
'@backstage/core-compat-api': coreCompatApi,
'@backstage/core-components': coreComponents,
'@backstage/core-plugin-api': corePluginApi,
'@backstage/frontend-test-utils': frontendTestUtils,
'@backstage/e2e-test-utils': e2eTestUtils,
'@backstage/errors': errors,
'@backstage/integration-react': integrationReact,
@@ -0,0 +1,8 @@
.git
.yarn/cache
.yarn/install-state.gz
node_modules
packages/*/src
packages/*/node_modules
plugins
*.local.yaml
@@ -0,0 +1 @@
playwright.config.ts
@@ -0,0 +1,3 @@
module.exports = {
root: true,
};
@@ -0,0 +1,56 @@
# macOS
.DS_Store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Coverage directory generated when running tests with coverage
coverage
# Dependencies
node_modules/
# Yarn files
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Node version directives
.nvmrc
# dotenv environment variables file
.env
.env.test
# Build output
dist
dist-types
# Temporary change files created by Vim
*.swp
# MkDocs build output
site
# Local configuration files
*.local.yaml
# Sensitive credentials
*-credentials.yaml
# vscode database functionality support files
*.session.sql
# E2E test reports
e2e-test-report/
# Cache
.cache/
@@ -0,0 +1,4 @@
dist
dist-types
coverage
.vscode
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.4.1.cjs
@@ -0,0 +1,10 @@
# [Backstage](https://backstage.io)
This is your newly scaffolded Backstage App, Good Luck!
To start the app, run:
```sh
yarn install
yarn start
```
@@ -0,0 +1,55 @@
app:
# Should be the same as backend.baseUrl when using the `app-backend` plugin.
baseUrl: http://localhost:7007
backend:
# Note that the baseUrl should be the URL that the browser and other clients
# should use when communicating with the backend, i.e. it needs to be
# reachable not just from within the backend host, but from all of your
# callers. When its value is "http://localhost:7007", it's strictly private
# and can't be reached by others.
baseUrl: http://localhost:7007
# The listener can also be expressed as a single <host>:<port> string. In this case we bind to
# all interfaces, the most permissive setting. The right value depends on your specific deployment.
listen: ':7007'
# config options: https://node-postgres.com/apis/client
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
# https://node-postgres.com/features/ssl
# you can set the sslmode configuration option via the `PGSSLMODE` environment variable
# see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require)
# ssl:
# ca: # if you have a CA file and want to verify it you can uncomment this section
# $file: <file-path>/ca/server.crt
auth:
providers:
guest: {}
catalog:
# Overrides the default list locations from app-config.yaml as these contain example data.
# See https://backstage.io/docs/features/software-catalog/#adding-components-to-the-catalog for more details
# on how to get entities into the catalog.
locations:
# Local example data, replace this with your production config, these are intended for demo use only.
# File locations are relative to the backend process, typically in a deployed context, such as in a Docker container, this will be the root
- type: file
target: ./examples/entities.yaml
# Local example template
- type: file
target: ./examples/template/template.yaml
rules:
- allow: [Template]
# Local example organizational data
- type: file
target: ./examples/org.yaml
rules:
- allow: [User, Group]
@@ -0,0 +1,124 @@
app:
title: Scaffolded Backstage App
baseUrl: http://localhost:3000
experimental:
packages: all
extensions:
# Disable the nav items that we're manually rendering in packages/app/src/modules/nav/Sidebar.tsx
- nav-item:search: false
- nav-item:user-settings: false
- nav-item:catalog: false
- nav-item:scaffolder: false
organization:
name: My Company
backend:
# Used for enabling authentication, secret is shared by all backend plugins
# See https://backstage.io/docs/auth/service-to-service-auth for
# information on the format
# auth:
# keys:
# - secret: ${BACKEND_SECRET}
baseUrl: http://localhost:7007
listen:
port: 7007
# Uncomment the following host directive to bind to specific interfaces
# host: 127.0.0.1
csp:
connect-src: ["'self'", 'http:', 'https:']
# Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
# Default Helmet Content-Security-Policy values can be removed by setting the key to false
cors:
origin: http://localhost:3000
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
credentials: true
# This is for local development only, it is not recommended to use this in production
# The production database configuration is stored in app-config.production.yaml
database:
client: better-sqlite3
connection: ':memory:'
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
integrations:
github:
- host: github.com
# This is a Personal Access Token or PAT from GitHub. You can find out how to generate this token, and more information
# about setting up the GitHub integration here: https://backstage.io/docs/integrations/github/locations#configuration
token: ${GITHUB_TOKEN}
### Example for how to add your GitHub Enterprise instance using the API:
# - host: ghe.example.net
# apiBaseUrl: https://ghe.example.net/api/v3
# token: ${GHE_TOKEN}
proxy:
### Example for how to add a proxy endpoint for the frontend.
### A typical reason to do this is to handle HTTPS and CORS for internal services.
# endpoints:
# '/test':
# target: 'https://example.com'
# changeOrigin: true
# Reference documentation http://backstage.io/docs/features/techdocs/configuration
# Note: After experimenting with basic setup, use CI/CD to generate docs
# and an external cloud storage when deploying TechDocs for production use-case.
# https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-basic-to-recommended-deployment-approach
techdocs:
builder: 'local' # Alternatives - 'external'
generator:
runIn: 'docker' # Alternatives - 'local'
publisher:
type: 'local' # Alternatives - 'googleGcs' or 'awsS3'. Read documentation for using alternatives.
auth:
# see https://backstage.io/docs/auth/ to learn about auth providers
providers:
# See https://backstage.io/docs/auth/guest/provider
guest: {}
scaffolder:
# see https://backstage.io/docs/features/software-templates/configuration for software template options
catalog:
import:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location]
locations:
# Local example data, file locations are relative to the backend process, typically `packages/backend`
- type: file
target: ../../examples/entities.yaml
# Local example template
- type: file
target: ../../examples/template/template.yaml
rules:
- allow: [Template]
# Local example organizational data
- type: file
target: ../../examples/org.yaml
rules:
- allow: [User, Group]
## Uncomment these lines to add more example data
# - type: url
# target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all.yaml
## Uncomment these lines to add an example org
# - type: url
# target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml
# rules:
# - allow: [User, Group]
kubernetes:
# see https://backstage.io/docs/features/kubernetes/configuration for kubernetes configuration options
# see https://backstage.io/docs/permissions/getting-started for more on the permission framework
permission:
# setting this to `false` will disable permissions
enabled: true
@@ -0,0 +1,3 @@
{
"version": "{{version 'root'}}"
}
@@ -0,0 +1,13 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: {{name}}
description: An example of a Backstage application.
# Example for optional annotations
# annotations:
# github.com/project-slug: backstage/backstage
# backstage.io/techdocs-ref: dir:.
spec:
type: website
owner: john@example.com
lifecycle: experimental
@@ -0,0 +1,41 @@
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: examples
spec:
owner: guests
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: example-website
spec:
type: website
lifecycle: experimental
owner: guests
system: examples
providesApis: [example-grpc-api]
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-api
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: example-grpc-api
spec:
type: grpc
lifecycle: experimental
owner: guests
system: examples
definition: |
syntax = "proto3";
service Exampler {
rpc Example (ExampleMessage) returns (ExampleMessage) {};
}
message ExampleMessage {
string example = 1;
};
@@ -0,0 +1,17 @@
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-user
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: guest
spec:
memberOf: [guests]
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-group
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: guests
spec:
type: team
children: []
@@ -0,0 +1,8 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{ values.name | dump }}
spec:
type: service
owner: user:guest
lifecycle: experimental
@@ -0,0 +1 @@
console.log('Hello from ${{ values.name }}!');
@@ -0,0 +1,5 @@
{
"name": "${{ values.name }}",
"private": true,
"dependencies": {}
}
@@ -0,0 +1,76 @@
apiVersion: scaffolder.backstage.io/v1beta3
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-template
kind: Template
metadata:
name: example-nodejs-template
title: Example Node.js Template
description: An example template for the scaffolder that creates a simple Node.js service
spec:
owner: user:guest
type: service
# These parameters are used to generate the input form in the frontend, and are
# used to gather input data for the execution of the template.
parameters:
- title: Fill in some steps
required:
- name
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
# These steps are executed in the scaffolder backend, using data that we gathered
# via the parameters above.
steps:
# Each step executes an action, in this case one templates files into the working directory.
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./content
values:
name: ${{ parameters.name }}
# This step publishes the contents of the working directory to GitHub.
# If you or your organization prefer another default branch name over 'main'
# you can change that here.
- id: publish
name: Publish
action: publish:github
input:
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
defaultBranch: 'main'
# The final step is to register our new component in the catalog.
- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
# Outputs are displayed to the user after a successful execution of the template.
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
@@ -0,0 +1,54 @@
{
"name": "root",
"version": "1.0.0",
"private": true,
"engines": {
"node": "20 || 22"
},
"scripts": {
"start": "backstage-cli repo start",
"build:backend": "yarn workspace backend build",
"build:all": "backstage-cli repo build --all",
"build-image": "yarn workspace backend build-image",
"tsc": "tsc",
"tsc:full": "tsc --skipLibCheck false --incremental false",
"clean": "backstage-cli repo clean",
"test": "backstage-cli repo test",
"test:all": "backstage-cli repo test --coverage",
"test:e2e": "playwright test",
"fix": "backstage-cli repo fix",
"lint": "backstage-cli repo lint --since origin/{{defaultBranch}}",
"lint:all": "backstage-cli repo lint",
"prettier:check": "prettier --check .",
"new": "backstage-cli new"
},
"workspaces": {
"packages": [
"packages/*",
"plugins/*"
]
},
"devDependencies": {
"@backstage/cli": "^{{version '@backstage/cli'}}",
"@backstage/e2e-test-utils": "^{{version '@backstage/e2e-test-utils'}}",
"@playwright/test": "^1.32.3",
"node-gyp": "^10.0.0",
"prettier": "^2.3.2",
"typescript": "~5.8.0"
},
"resolutions": {
"@types/react": "^18",
"@types/react-dom": "^18"
},
"prettier": "@backstage/cli/config/prettier",
"lint-staged": {
"*.{js,jsx,ts,tsx,mjs,cjs}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
},
"packageManager": "yarn@4.4.1"
}
@@ -0,0 +1,9 @@
# The Packages Folder
This is where your own applications and centrally managed libraries live, each
in a separate folder of its own.
From the start there's an `app` folder (for the frontend) and a `backend` folder
(for the Node backend), but you can also add more modules in here that house
your core additions and adaptations, such as themes, common React component
libraries, utilities, and similar.
@@ -0,0 +1 @@
public
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,27 @@
/*
* Copyright 2020 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 { test, expect } from '@playwright/test';
test('App should render the welcome page', async ({ page }) => {
await page.goto('/');
const enterButton = page.getByRole('button', { name: 'Enter' });
await expect(enterButton).toBeVisible();
await enterButton.click();
await expect(page.getByText('My Company Catalog')).toBeVisible();
});
@@ -0,0 +1,59 @@
{
"name": "app",
"version": "0.0.0",
"private": true,
"bundled": true,
"backstage": {
"role": "frontend"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/core-compat-api": "^{{ version '@backstage/core-compat-api'}}",
"@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}",
"@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}",
"@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}",
"@backstage/plugin-org": "^{{ version '@backstage/plugin-org'}}",
"@backstage/plugin-scaffolder": "^{{ version '@backstage/plugin-scaffolder'}}",
"@backstage/plugin-search": "^{{ version '@backstage/plugin-search'}}",
"@backstage/plugin-user-settings": "^{{ version '@backstage/plugin-user-settings'}}",
"@backstage/core-components": "^{{ version '@backstage/core-components'}}",
"@backstage/ui": "^{{ version '@backstage/ui'}}",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0"
},
"devDependencies": {
"@backstage/frontend-test-utils": "^{{ version '@backstage/frontend-test-utils'}}",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"@types/react-dom": "*",
"cross-env": "^7.0.0"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"files": [
"dist"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Backstage is an open source framework for building developer portals"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link
rel="manifest"
href="<%= publicPath %>/manifest.json"
crossorigin="use-credentials"
/>
<link rel="icon" href="<%= publicPath %>/favicon.ico" />
<link rel="shortcut icon" href="<%= publicPath %>/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="<%= publicPath %>/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="<%= publicPath %>/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="<%= publicPath %>/favicon-16x16.png"
/>
<link
rel="mask-icon"
href="<%= publicPath %>/safari-pinned-tab.svg"
color="#5bbad5"
/>
<title><%= config.getOptionalString('app.title') ?? 'Backstage' %></title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `yarn start`.
To create a production bundle, use `yarn build`.
-->
</body>
</html>
@@ -0,0 +1,15 @@
{
"short_name": "Backstage",
"name": "Backstage",
"icons": [
{
"src": "favicon.ico",
"sizes": "48x48",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
@@ -0,0 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="682.667" height="682.667" preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="0 0 512 512"><metadata>Created by potrace 1.11, written by Peter Selinger 2001-2013</metadata><g fill="#000" stroke="none"><path d="M492 4610 c-4 -3 -8 -882 -7 -1953 l0 -1948 850 2 c898 1 945 3 1118 49 505 134 823 531 829 1037 2 136 -9 212 -44 323 -40 125 -89 218 -163 310 -35 43 -126 128 -169 157 -22 15 -43 30 -46 33 -12 13 -131 70 -188 91 l-64 22 60 28 c171 77 317 224 403 404 64 136 92 266 91 425 -5 424 -245 770 -642 923 -79 30 -105 39 -155 50 -11 3 -38 10 -60 15 -22 6 -60 13 -85 17 -25 3 -58 9 -75 12 -36 8 -1643 11 -1653 3z m1497 -743 c236 -68 352 -254 305 -486 -26 -124 -110 -224 -232 -277 -92 -40 -151 -46 -439 -49 l-283 -3 -1 27 c-1 36 -1 760 0 790 l1 23 298 -5 c226 -4 310 -9 351 -20z m-82 -1538 c98 -3 174 -19 247 -52 169 -78 257 -212 258 -395 0 -116 -36 -221 -100 -293 -64 -72 -192 -135 -314 -155 -23 -3 -181 -7 -350 -8 l-308 -2 -1 26 c-6 210 1 874 9 879 9 5 366 6 559 0z" transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"/><path d="M4160 1789 c-275 -24 -499 -263 -503 -536 -1 -115 21 -212 66 -292 210 -369 697 -402 950 -65 77 103 110 199 111 329 0 50 -6 113 -13 140 -16 58 -62 155 -91 193 -33 43 -122 132 -132 132 -5 0 -26 11 -46 25 -85 56 -219 85 -342 74z" transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,28 @@
import { render, waitFor } from '@testing-library/react';
import App from './App';
describe('App', () => {
it('should render', async () => {
process.env = {
NODE_ENV: 'test',
APP_CONFIG: [
{
data: {
app: { title: 'Test' },
backend: { baseUrl: 'http://localhost:7007' },
techdocs: {
storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
},
},
context: 'test',
},
] as any,
};
const rendered = render(App.createRoot());
await waitFor(() => {
expect(rendered.baseElement).toBeInTheDocument();
});
});
});
@@ -0,0 +1,7 @@
import { createApp } from '@backstage/frontend-defaults';
import catalogPlugin from '@backstage/plugin-catalog/alpha';
import { navModule } from './modules/nav';
export default createApp({
features: [catalogPlugin, navModule],
});
@@ -0,0 +1,6 @@
import '@backstage/cli/asset-types';
import ReactDOM from 'react-dom/client';
import App from './App';
import '@backstage/ui/css/styles.css';
ReactDOM.createRoot(document.getElementById('root')!).render(App.createRoot());
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles({
svg: {
width: 'auto',
height: 28,
},
path: {
fill: '#7df3e1',
},
});
export const LogoIcon = () => {
const classes = useStyles();
return (
<svg
className={classes.svg}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 337.46 428.5"
>
<path
className={classes.path}
d="M303,166.05a80.69,80.69,0,0,0,13.45-10.37c.79-.77,1.55-1.53,2.3-2.3a83.12,83.12,0,0,0,7.93-9.38A63.69,63.69,0,0,0,333,133.23a48.58,48.58,0,0,0,4.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56,0,78.3,115.23,0,190.25l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.69,19.16-18.36,25.52-42.12,13.7-61.87a49.22,49.22,0,0,0-6.8-8.87A89.17,89.17,0,0,0,259,178.29h.15a85.08,85.08,0,0,0,31-5.79A80.88,80.88,0,0,0,303,166.05ZM202.45,225.86c-19.32,18.51-50.4,21.23-75.7,5.9L51.61,186.15l67.45-64.64,76.41,46.38C223,184.58,221.49,207.61,202.45,225.86Zm8.93-82.22-70.65-42.89L205.14,39,274.51,81.1c25.94,15.72,29.31,37,10.55,55A60.69,60.69,0,0,1,211.38,143.64Zm29.86,190c-19.57,18.75-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,282.52v24.67L108.6,373.1a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A87.27,87.27,0,0,1,241.24,333.68Zm0-39c-19.57,18.75-46.17,29.08-74.88,29.08a123.81,123.81,0,0,1-64.1-18.19L0,243.53v24.68l108.6,65.91a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.5v-1.78A87.27,87.27,0,0,1,241.24,294.7Zm0-39c-19.57,18.76-46.17,29.09-74.88,29.09a123.81,123.81,0,0,1-64.1-18.19L0,204.55v24.68l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.68,12.88-12.35,20-27.13,19.68-41.5v-1.82A86.09,86.09,0,0,1,241.24,255.71Zm83.7,25.74a94.15,94.15,0,0,1-60.2,25.86h0V334a81.6,81.6,0,0,0,51.74-22.37c14-13.38,21.14-28.11,21-42.64v-2.19A94.92,94.92,0,0,1,324.94,281.45Zm-83.7,91.21c-19.57,18.76-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,321.5v24.68l108.6,65.9a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.8,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A86.29,86.29,0,0,1,241.24,372.66ZM327,162.45c-.68.69-1.35,1.38-2.05,2.06a94.37,94.37,0,0,1-10.64,8.65,91.35,91.35,0,0,1-11.6,7,94.53,94.53,0,0,1-26.24,8.71,97.69,97.69,0,0,1-14.16,1.57c.5,1.61.9,3.25,1.25,4.9a53.27,53.27,0,0,1,1.14,12V217h.05a84.41,84.41,0,0,0,25.35-5.55,81,81,0,0,0,26.39-16.82c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,172.17a48.55,48.55,0,0,0,4.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08,1.54-2.25,3.09A96.73,96.73,0,0,1,327,162.45Zm0,77.92c-.69.7-1.31,1.41-2,2.1a94.2,94.2,0,0,1-60.2,25.86h0l0,26.67h0a81.6,81.6,0,0,0,51.74-22.37A73.51,73.51,0,0,0,333,250.13a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74,1.07-1.46,2.15-2.27,3.21A95.68,95.68,0,0,1,327,240.37Zm0-39c-.69.7-1.31,1.41-2,2.1a93.18,93.18,0,0,1-10.63,8.65,91.63,91.63,0,0,1-11.63,7,95.47,95.47,0,0,1-37.94,10.18h0V256h0a81.65,81.65,0,0,0,51.74-22.37c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,211.15a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74,1.08-1.46,2.16-2.27,3.22A95.68,95.68,0,0,1,327,201.39Z"
/>
</svg>
);
};
@@ -0,0 +1,58 @@
import {
SidebarDivider,
SidebarGroup,
SidebarItem,
SidebarScrollWrapper,
SidebarSpace,
} from '@backstage/core-components';
import { compatWrapper } from '@backstage/core-compat-api';
import { Sidebar } from '@backstage/core-components';
import { NavContentBlueprint } from '@backstage/frontend-plugin-api';
import { SidebarLogo } from './SidebarLogo';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
import HomeIcon from '@material-ui/icons/Home';
import MenuIcon from '@material-ui/icons/Menu';
import SearchIcon from '@material-ui/icons/Search';
import { SidebarSearchModal } from '@backstage/plugin-search';
import { UserSettingsSignInAvatar, Settings as SidebarSettings } from '@backstage/plugin-user-settings';
export const SidebarContent = NavContentBlueprint.make({
params: {
component: ({ items }) =>
compatWrapper(
<Sidebar>
<SidebarLogo />
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
<SidebarSearchModal />
</SidebarGroup>
<SidebarDivider />
<SidebarGroup label="Menu" icon={<MenuIcon />}>
{/* Global nav, not org-specific */}
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
<SidebarItem
icon={CreateComponentIcon}
to="create"
text="Create..."
/>
{/* End global nav */}
<SidebarDivider />
<SidebarScrollWrapper>
{/* Items in this group will be scrollable if they run out of space */}
{items.map((item, index) => (
<SidebarItem {...item} key={index} />
))}
</SidebarScrollWrapper>
</SidebarGroup>
<SidebarSpace />
<SidebarDivider />
<SidebarGroup
label="Settings"
icon={<UserSettingsSignInAvatar />}
to="/settings"
>
<SidebarSettings />
</SidebarGroup>
</Sidebar>,
),
},
});
@@ -0,0 +1,36 @@
import {
Link,
sidebarConfig,
useSidebarOpenState,
} from '@backstage/core-components';
import { makeStyles } from '@material-ui/core';
import { LogoFull } from './LogoFull';
import { LogoIcon } from './LogoIcon';
const useSidebarLogoStyles = makeStyles({
root: {
width: sidebarConfig.drawerWidthClosed,
height: 3 * sidebarConfig.logoHeight,
display: 'flex',
flexFlow: 'row nowrap',
alignItems: 'center',
marginBottom: -14,
},
link: {
width: sidebarConfig.drawerWidthClosed,
marginLeft: 24,
},
});
export const SidebarLogo = () => {
const classes = useSidebarLogoStyles();
const { isOpen } = useSidebarOpenState();
return (
<div className={classes.root}>
<Link to="/" underline="none" className={classes.link} aria-label="Home">
{isOpen ? <LogoFull /> : <LogoIcon />}
</Link>
</div>
);
};
@@ -0,0 +1,7 @@
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { SidebarContent } from './Sidebar';
export const navModule = createFrontendModule({
pluginId: 'app',
extensions: [SidebarContent],
});
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,70 @@
# This dockerfile builds an image for the backend package.
# It should be executed with the root of the repo as docker context.
#
# Before building this image, be sure to have run the following commands in the repo root:
#
# yarn install --immutable
# yarn tsc
# yarn build:backend
#
# Once the commands have been run, you can build the image using `yarn build-image`
#
# Alternatively, there is also a multi-stage Dockerfile documented here:
# https://backstage.io/docs/deployment/docker#multi-stage-build
FROM node:20-bookworm-slim
# Set Python interpreter for `node-gyp` to use
ENV PYTHON=/usr/bin/python3
# Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend.
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends python3 g++ build-essential && \
rm -rf /var/lib/apt/lists/*
# Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image,
# in which case you should also move better-sqlite3 to "devDependencies" in package.json.
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends libsqlite3-dev && \
rm -rf /var/lib/apt/lists/*
# From here on we use the least-privileged `node` user to run the backend.
USER node
# This should create the app dir as `node`.
# If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`.
# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`.
WORKDIR /app
# Copy files needed by Yarn
COPY --chown=node:node .yarn ./.yarn
COPY --chown=node:node .yarnrc.yml ./
COPY --chown=node:node backstage.json ./
# This switches many Node.js dependencies to production mode.
ENV NODE_ENV=production
# This disables node snapshot for Node 20 to work with the Scaffolder
ENV NODE_OPTIONS="--no-node-snapshot"
# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
# The skeleton contains the package.json of each package in the monorepo,
# and along with yarn.lock and the root package.json, that's enough to run yarn install.
COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
yarn workspaces focus --all --production && rm -rf "$(yarn cache clean)"
# This will include the examples, if you don't need these simply remove this line
COPY --chown=node:node examples ./examples
# Then copy the rest of the backend bundle, along with any other files we might want.
COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
@@ -0,0 +1,59 @@
# example-backend
This package is an EXAMPLE of a Backstage backend.
The main purpose of this package is to provide a test bed for Backstage plugins
that have a backend part. Feel free to experiment locally or within your fork by
adding dependencies and routes to this backend, to try things out.
Our goal is to eventually amend the create-app flow of the CLI, such that a
production ready version of a backend skeleton is made alongside the frontend
app. Until then, feel free to experiment here!
## Development
To run the example backend, first go to the project root and run
```bash
yarn install
```
You should only need to do this once.
After that, go to the `packages/backend` directory and run
```bash
yarn start
```
If you want to override any configuration locally, for example adding any secrets,
you can do so in `app-config.local.yaml`.
The backend starts up on port 7007 per default.
## Populating The Catalog
If you want to use the catalog functionality, you need to add so called
locations to the backend. These are places where the backend can find some
entity descriptor data to consume and serve. For more information, see
[Software Catalog Overview - Adding Components to the Catalog](https://backstage.io/docs/features/software-catalog/#adding-components-to-the-catalog).
To get started quickly, this template already includes some statically configured example locations
in `app-config.yaml` under `catalog.locations`. You can remove and replace these locations as you
like, and also override them for local development in `app-config.local.yaml`.
## Authentication
We chose [Passport](http://www.passportjs.org/) as authentication platform due
to its comprehensive set of supported authentication
[strategies](http://www.passportjs.org/packages/).
Read more about the
[auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md)
and
[how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md)
## Documentation
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
- [Backstage Documentation](https://backstage.io/docs)
@@ -0,0 +1,54 @@
{
"name": "backend",
"version": "0.0.0",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"private": true,
"backstage": {
"role": "backend"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
},
"dependencies": {
"@backstage/backend-defaults": "^{{version '@backstage/backend-defaults'}}",
"@backstage/config": "^{{version '@backstage/config'}}",
"@backstage/plugin-app-backend": "^{{version '@backstage/plugin-app-backend'}}",
"@backstage/plugin-auth-backend": "^{{version '@backstage/plugin-auth-backend'}}",
"@backstage/plugin-auth-backend-module-github-provider": "^{{version '@backstage/plugin-auth-backend-module-github-provider'}}",
"@backstage/plugin-auth-backend-module-guest-provider": "^{{version '@backstage/plugin-auth-backend-module-guest-provider'}}",
"@backstage/plugin-auth-node": "^{{version '@backstage/plugin-auth-node'}}",
"@backstage/plugin-catalog-backend": "^{{version '@backstage/plugin-catalog-backend'}}",
"@backstage/plugin-catalog-backend-module-logs": "^{{version '@backstage/plugin-catalog-backend-module-logs'}}",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^{{version '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'}}",
"@backstage/plugin-kubernetes-backend": "^{{version '@backstage/plugin-kubernetes-backend'}}",
"@backstage/plugin-permission-backend": "^{{version '@backstage/plugin-permission-backend'}}",
"@backstage/plugin-permission-backend-module-allow-all-policy": "^{{version '@backstage/plugin-permission-backend-module-allow-all-policy'}}",
"@backstage/plugin-permission-common": "^{{version '@backstage/plugin-permission-common'}}",
"@backstage/plugin-permission-node": "^{{version '@backstage/plugin-permission-node'}}",
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
"@backstage/plugin-scaffolder-backend-module-github": "^{{version '@backstage/plugin-scaffolder-backend-module-github'}}",
"@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
"@backstage/plugin-search-backend-module-catalog": "^{{version '@backstage/plugin-search-backend-module-catalog'}}",
"@backstage/plugin-search-backend-module-pg": "^{{version '@backstage/plugin-search-backend-module-pg'}}",
"@backstage/plugin-search-backend-module-techdocs": "^{{version '@backstage/plugin-search-backend-module-techdocs'}}",
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
"app": "link:../app",
"better-sqlite3": "^9.0.0",
"node-gyp": "^10.0.0",
"pg": "^8.11.3"
},
"devDependencies": {
"@backstage/cli": "^{{version '@backstage/cli'}}"
},
"files": [
"dist"
]
}
@@ -0,0 +1,55 @@
/*
* Hi!
*
* Note that this is an EXAMPLE Backstage backend. Please check the README.
*
* Happy hacking!
*/
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-proxy-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-techdocs-backend'));
// auth plugin
backend.add(import('@backstage/plugin-auth-backend'));
// See https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
// See https://backstage.io/docs/auth/guest/provider
// catalog plugin
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
// See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors
backend.add(import('@backstage/plugin-catalog-backend-module-logs'));
// permission plugin
backend.add(import('@backstage/plugin-permission-backend'));
// See https://backstage.io/docs/permissions/getting-started for how to create your own permission policy
backend.add(
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
// search plugin
backend.add(import('@backstage/plugin-search-backend'));
// search engine
// See https://backstage.io/docs/features/search/search-engines
backend.add(import('@backstage/plugin-search-backend-module-pg'));
// search collators
backend.add(import('@backstage/plugin-search-backend-module-catalog'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs'));
// kubernetes
backend.add(import('@backstage/plugin-kubernetes-backend'));
backend.start();
@@ -0,0 +1,60 @@
/*
* Copyright 2023 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 { defineConfig } from '@playwright/test';
import { generateProjects } from '@backstage/e2e-test-utils/playwright';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
timeout: 60_000,
expect: {
timeout: 5_000,
},
// Run your local dev server before starting the tests
webServer: process.env.CI
? []
: [
{
command: 'yarn start',
port: 3000,
reuseExistingServer: true,
timeout: 60_000,
},
],
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['html', { open: 'never', outputFolder: 'e2e-test-report' }]],
use: {
actionTimeout: 0,
baseURL:
process.env.PLAYWRIGHT_URL ??
(process.env.CI ? 'http://localhost:7007' : 'http://localhost:3000'),
screenshot: 'only-on-failure',
trace: 'on-first-retry',
},
outputDir: 'node_modules/.cache/e2e-test-results',
projects: generateProjects(), // Find all packages with e2e-test folders
});
@@ -0,0 +1,9 @@
# The Plugins Folder
This is where your own plugins and their associated modules live, each in a
separate folder of its own.
If you want to create a new plugin here, go to your project root directory, run
the command `yarn new`, and follow the on-screen instructions.
You can also check out existing plugins on [the plugin marketplace](https://backstage.io/plugins)!
@@ -0,0 +1,17 @@
{
"extends": "@backstage/cli/config/tsconfig.json",
"include": [
"packages/*/src",
"packages/*/config.d.ts",
"plugins/*/src",
"plugins/*/config.d.ts",
"plugins/*/dev",
"plugins/*/migrations"
],
"exclude": ["node_modules"],
"compilerOptions": {
"outDir": "dist-types",
"rootDir": ".",
"jsx": "react-jsx"
}
}
@@ -0,0 +1,12 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10
"root-workspace-0b6124@workspace:.":
version: 0.0.0-use.local
resolution: "root-workspace-0b6124@workspace:."
languageName: unknown
linkType: soft