diff --git a/.changeset/warm-beds-flow.md b/.changeset/warm-beds-flow.md
new file mode 100644
index 0000000000..7d34336739
--- /dev/null
+++ b/.changeset/warm-beds-flow.md
@@ -0,0 +1,5 @@
+---
+'@techdocs/cli': patch
+---
+
+Updated the HTTP server to allow for simplification of the development of the CLI itself.
diff --git a/packages/techdocs-cli-embedded-app/app-config.dev.yaml b/packages/techdocs-cli-embedded-app/app-config.dev.yaml
deleted file mode 100644
index 02d68c940e..0000000000
--- a/packages/techdocs-cli-embedded-app/app-config.dev.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-# NOTE: This file is used for testing techdocs-cli locally
-
-app:
- title: Techdocs Preview App
- baseUrl: http://localhost:3000
-
-backend:
- baseUrl: http://localhost:7007
-
-techdocs:
- builder: 'external'
- requestUrl: http://localhost:7007/api
diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json
index 6eb3119b06..2df050ef86 100644
--- a/packages/techdocs-cli-embedded-app/package.json
+++ b/packages/techdocs-cli-embedded-app/package.json
@@ -39,9 +39,8 @@
"start-server-and-test": "^1.10.11"
},
"scripts": {
- "start": "backstage-cli app:serve --config ./app-config.yaml --config ./app-config.dev.yaml",
+ "start": "backstage-cli app:serve --config ./app-config.yaml",
"build": "backstage-cli app:build --config ./app-config.yaml",
- "build:dev": "backstage-cli app:build --config ./app-config.dev.yaml",
"clean": "backstage-cli clean",
"test": "backstage-cli test",
"lint": "backstage-cli lint",
diff --git a/packages/techdocs-cli-embedded-app/src/App.test.tsx b/packages/techdocs-cli-embedded-app/src/App.test.tsx
index f177433d95..75658d271e 100644
--- a/packages/techdocs-cli-embedded-app/src/App.test.tsx
+++ b/packages/techdocs-cli-embedded-app/src/App.test.tsx
@@ -18,25 +18,24 @@ import React from 'react';
import { renderWithEffects } from '@backstage/test-utils';
import App from './App';
+jest.mock('./config', () => ({
+ configLoader: async () => [
+ {
+ data: {
+ app: { title: 'Test' },
+ backend: { baseUrl: 'http://localhost:7007' },
+ techdocs: {
+ storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
+ },
+ },
+ context: 'test',
+ },
+ ],
+}));
+
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 = await renderWithEffects();
- expect(rendered.baseElement).toBeInTheDocument();
+ expect(rendered.getByText('Docs Preview')).toBeInTheDocument();
});
});
diff --git a/packages/techdocs-cli-embedded-app/src/App.tsx b/packages/techdocs-cli-embedded-app/src/App.tsx
index 00b232d8e1..51bdfbbb11 100644
--- a/packages/techdocs-cli-embedded-app/src/App.tsx
+++ b/packages/techdocs-cli-embedded-app/src/App.tsx
@@ -29,9 +29,11 @@ import { apis } from './apis';
import { Root } from './components/Root';
import { techDocsPage } from './components/TechDocsPage';
import * as plugins from './plugins';
+import { configLoader } from './config';
const app = createApp({
apis,
+ configLoader,
plugins: Object.values(plugins),
});
diff --git a/packages/techdocs-cli-embedded-app/src/config.ts b/packages/techdocs-cli-embedded-app/src/config.ts
new file mode 100644
index 0000000000..482ceb41ec
--- /dev/null
+++ b/packages/techdocs-cli-embedded-app/src/config.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { defaultConfigLoader } from '@backstage/core-app-api';
+
+const PRODUCTION_CONFIG = {
+ backend: {
+ baseUrl: 'http://localhost:3000',
+ },
+ techdocs: {
+ builder: 'external',
+ requestUrl: 'http://localhost:3000/api',
+ },
+};
+
+const DEVELOPMENT_CONFIG = {
+ backend: {
+ baseUrl: 'http://localhost:7007',
+ },
+ techdocs: {
+ builder: 'external',
+ requestUrl: 'http://localhost:7007/api',
+ },
+};
+
+async function isProductionServe() {
+ const res = await fetch('/.detect');
+ if (!res.ok) {
+ return false;
+ }
+ const text = await res.text();
+ return text.trim() === 'techdocs-cli-server';
+}
+
+export async function configLoader() {
+ const defaultConfigs = await defaultConfigLoader();
+ const isProduction = await isProductionServe();
+
+ return [
+ ...defaultConfigs,
+ {
+ context: 'detected',
+ data: isProduction ? PRODUCTION_CONFIG : DEVELOPMENT_CONFIG,
+ },
+ ];
+}
diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json
index 8d2d952e84..2076e25bed 100644
--- a/packages/techdocs-cli/package.json
+++ b/packages/techdocs-cli/package.json
@@ -21,12 +21,13 @@
"types": "",
"scripts": {
"start": "nodemon --",
- "build": "./scripts/build.sh",
+ "build": "backstage-cli build --outputs cjs",
"clean": "backstage-cli clean",
"lint": "backstage-cli lint",
"test": "backstage-cli test --testPathIgnorePatterns=src/e2e.test.ts",
"test:e2e": "backstage-cli test src/e2e.test.ts",
- "test:e2e:ci": "backstage-cli test --watchAll=false --ci src/e2e.test.ts"
+ "test:e2e:ci": "backstage-cli test --watchAll=false --ci src/e2e.test.ts",
+ "prepack": "./scripts/prepack.sh"
},
"bin": {
"techdocs-cli": "bin/techdocs-cli"
@@ -57,6 +58,7 @@
"dependencies": {
"@backstage/backend-common": "^0.10.6",
"@backstage/catalog-model": "^0.9.10",
+ "@backstage/cli-common": "^0.1.6",
"@backstage/config": "^0.1.13",
"@backstage/techdocs-common": "^0.11.6",
"@types/dockerode": "^3.3.0",
diff --git a/packages/techdocs-cli/scripts/build.sh b/packages/techdocs-cli/scripts/prepack.sh
similarity index 54%
rename from packages/techdocs-cli/scripts/build.sh
rename to packages/techdocs-cli/scripts/prepack.sh
index 31c2eae6c3..fa24b10167 100755
--- a/packages/techdocs-cli/scripts/build.sh
+++ b/packages/techdocs-cli/scripts/prepack.sh
@@ -20,25 +20,7 @@ SCRIPT_DIR=$(dirname $0)
TECHDOCS_CLI_DIR="$SCRIPT_DIR"/..
TECHDOCS_CLI_EMBEDDED_APP_DIR="$TECHDOCS_CLI_DIR"/../techdocs-cli-embedded-app
-compile_and_build_cli() {
- echo "📄 Compiling..."
- yarn workspace @techdocs/cli tsc > /dev/null
- echo "📦️ Building..."
- pushd $TECHDOCS_CLI_DIR > /dev/null
- npx backstage-cli build --outputs cjs > /dev/null
- popd > /dev/null
-}
-
-build_and_embed_app() {
- echo "🚚 Embedding app..."
- if [ "$TECHDOCS_CLI_DEV_MODE" = "true" ] ; then
- yarn workspace techdocs-cli-embedded-app build:dev > /dev/null
- else
- yarn workspace techdocs-cli-embedded-app build > /dev/null
- fi
- cp -r "$TECHDOCS_CLI_EMBEDDED_APP_DIR"/dist "$TECHDOCS_CLI_DIR"/dist/techdocs-preview-bundle > /dev/null
-}
-
-compile_and_build_cli
-build_and_embed_app
+echo "🚚 Copying embedded app into dist/embedded-app"
+rm -r "$TECHDOCS_CLI_DIR"/dist/embedded-app
+cp -r "$TECHDOCS_CLI_EMBEDDED_APP_DIR"/dist "$TECHDOCS_CLI_DIR"/dist/embedded-app
echo "🏁 Ready!"
diff --git a/packages/techdocs-cli/src/commands/serve/serve.ts b/packages/techdocs-cli/src/commands/serve/serve.ts
index 4d71de3e52..a6a671c2df 100644
--- a/packages/techdocs-cli/src/commands/serve/serve.ts
+++ b/packages/techdocs-cli/src/commands/serve/serve.ts
@@ -17,11 +17,30 @@
import { Command } from 'commander';
import path from 'path';
import openBrowser from 'react-dev-utils/openBrowser';
+import { findPaths } from '@backstage/cli-common';
import HTTPServer from '../../lib/httpServer';
import { runMkdocsServer } from '../../lib/mkdocsServer';
import { LogFunc, waitForSignal } from '../../lib/run';
import { createLogger } from '../../lib/utility';
+function findPreviewBundlePath(): string {
+ try {
+ return path.join(
+ path.dirname(require.resolve('techdocs-cli-embedded-app/package.json')),
+ 'dist',
+ );
+ } catch {
+ // If the techdocs-cli-embedded-app package is not available it means we're
+ // running a published package. For published packages the preview bundle is
+ // copied to dist/embedded-app be the prepack script.
+ //
+ // This can be tested by running `yarn pack` and extracting the resulting tarball into a directory.
+ // Within the extracted directory, run `npm install --only=prod`.
+ // Once that's done you can test the CLI in any directory using `node /package `.
+ return findPaths(__dirname).resolveOwn('dist/embedded-app');
+ }
+}
+
export default async function serve(cmd: Command) {
const logger = createLogger({ verbose: cmd.verbose });
@@ -91,16 +110,9 @@ export default async function serve(cmd: Command) {
);
}
- // Run the embedded-techdocs Backstage app
- const techdocsPreviewBundlePath = path.join(
- path.dirname(require.resolve('@techdocs/cli/package.json')),
- 'dist',
- 'techdocs-preview-bundle',
- );
-
const port = isDevMode ? backstageBackendPort : backstagePort;
const httpServer = new HTTPServer(
- techdocsPreviewBundlePath,
+ findPreviewBundlePath(),
port,
cmd.mkdocsPort,
cmd.verbose,
diff --git a/packages/techdocs-cli/src/lib/httpServer.ts b/packages/techdocs-cli/src/lib/httpServer.ts
index 0402ce4e69..92fc5abc87 100644
--- a/packages/techdocs-cli/src/lib/httpServer.ts
+++ b/packages/techdocs-cli/src/lib/httpServer.ts
@@ -71,10 +71,18 @@ export default class HTTPServer {
response.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
request.url = forwardPath;
- return proxy.web(request, response);
+ proxy.web(request, response);
+ return;
}
- return serveHandler(request, response, {
+ // This endpoint is used by the frontend to detect where the backend is running.
+ if (request.url === '/.detect') {
+ response.setHeader('Content-Type', 'text/plain');
+ response.end('techdocs-cli-server');
+ return;
+ }
+
+ serveHandler(request, response, {
public: this.backstageBundleDir,
trailingSlash: true,
rewrites: [{ source: '**', destination: 'index.html' }],