From 33ebb28942f0f582ec0d51f30d505cb75a3ce0cc Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 14 Aug 2024 08:34:34 +0200 Subject: [PATCH] refactor(techdocs-node): remove dependency on backend-common Signed-off-by: Camila Belo --- .changeset/spicy-poems-hammer.md | 5 ++ plugins/techdocs-node/api-report.md | 55 ++++++++++++++----- plugins/techdocs-node/src/helpers.ts | 10 ++-- .../src/stages/generate/generators.test.ts | 3 +- .../src/stages/generate/generators.ts | 8 +-- .../src/stages/generate/helpers.test.ts | 3 +- .../src/stages/generate/helpers.ts | 7 +-- .../src/stages/generate/mkdocsPatchers.ts | 8 +-- .../src/stages/generate/techdocs.ts | 14 ++--- .../src/stages/generate/types.ts | 10 ++-- .../src/stages/prepare/dir.test.ts | 3 +- .../techdocs-node/src/stages/prepare/dir.ts | 5 +- .../techdocs-node/src/stages/prepare/types.ts | 7 +-- .../techdocs-node/src/stages/prepare/url.ts | 7 +-- .../src/stages/publish/awsS3.test.ts | 3 +- .../techdocs-node/src/stages/publish/awsS3.ts | 10 ++-- .../stages/publish/azureBlobStorage.test.ts | 5 +- .../src/stages/publish/azureBlobStorage.ts | 10 ++-- .../src/stages/publish/googleStorage.test.ts | 7 +-- .../src/stages/publish/googleStorage.ts | 8 +-- .../techdocs-node/src/stages/publish/index.ts | 3 +- .../src/stages/publish/local.test.ts | 9 +-- .../techdocs-node/src/stages/publish/local.ts | 19 ++++--- .../publish/migrations/GoogleMigration.ts | 12 ++-- .../src/stages/publish/openStackSwift.test.ts | 3 +- .../src/stages/publish/openStackSwift.ts | 11 ++-- .../src/stages/publish/publish.test.ts | 9 +-- .../techdocs-node/src/stages/publish/types.ts | 43 +++++++++++++-- 28 files changed, 176 insertions(+), 121 deletions(-) create mode 100644 .changeset/spicy-poems-hammer.md diff --git a/.changeset/spicy-poems-hammer.md b/.changeset/spicy-poems-hammer.md new file mode 100644 index 0000000000..23e86e178e --- /dev/null +++ b/.changeset/spicy-poems-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +As the `@backstage/backend-common` package is deprecated, we have updated the `techdocs-node` package to stop depending on it. diff --git a/plugins/techdocs-node/api-report.md b/plugins/techdocs-node/api-report.md index 6c7f05e900..77be81a9f6 100644 --- a/plugins/techdocs-node/api-report.md +++ b/plugins/techdocs-node/api-report.md @@ -7,13 +7,12 @@ import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import { ContainerRunner } from '@backstage/backend-common'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; -import { Logger } from 'winston'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import * as winston from 'winston'; @@ -48,8 +47,8 @@ export type GeneratorBuilder = { // @public export type GeneratorOptions = { - logger: Logger; - containerRunner?: ContainerRunner; + logger: LoggerService; + containerRunner?: TechDocsContainerRunner; }; // @public @@ -58,7 +57,7 @@ export type GeneratorRunOptions = { outputDir: string; parsedLocationAnnotation?: ParsedLocationAnnotation; etag?: string; - logger: Logger; + logger: LoggerService; logStream?: Writable; siteOptions?: { name?: string; @@ -71,8 +70,8 @@ export class Generators implements GeneratorBuilder { static fromConfig( config: Config, options: { - logger: Logger; - containerRunner?: ContainerRunner; + logger: LoggerService; + containerRunner?: TechDocsContainerRunner; customGenerator?: TechdocsGenerator; }, ): Promise; @@ -86,7 +85,7 @@ export const getDocFilesFromRepository: ( entity: Entity, opts?: { etag?: string; - logger?: Logger; + logger?: LoggerService; }, ) => Promise; @@ -156,13 +155,13 @@ export type PreparerBuilder = { // @public export type PreparerConfig = { - logger: Logger; + logger: LoggerService; reader: UrlReaderService; }; // @public export type PreparerOptions = { - logger?: Logger; + logger?: LoggerService; etag?: ETag; }; @@ -214,8 +213,8 @@ export type PublisherBuilder = { // @public export type PublisherFactory = { - logger: Logger; - discovery: PluginEndpointDiscovery; + logger: LoggerService; + discovery: DiscoveryService; customPublisher?: PublisherBase | undefined; }; @@ -261,6 +260,32 @@ export interface TechdocsBuildsExtensionPoint { // @public export const techdocsBuildsExtensionPoint: ExtensionPoint; +// @public +export interface TechDocsContainerRunner { + runContainer(opts: { + imageName: string; + command?: string | string[]; + args: string[]; + logStream?: Writable; + mountDirs?: Record; + workingDir?: string; + envVars?: Record; + pullImage?: boolean; + defaultUser?: boolean; + pullOptions?: { + authconfig?: { + username?: string; + password?: string; + auth?: string; + email?: string; + serveraddress?: string; + [key: string]: unknown; + }; + [key: string]: unknown; + }; + }): Promise; +} + // @public export interface TechDocsDocument extends IndexableDocument { kind: string; @@ -274,8 +299,8 @@ export interface TechDocsDocument extends IndexableDocument { // @public export class TechdocsGenerator implements GeneratorBase { constructor(options: { - logger: Logger; - containerRunner?: ContainerRunner; + logger: LoggerService; + containerRunner?: TechDocsContainerRunner; config: Config; scmIntegrations: ScmIntegrationRegistry; }); diff --git a/plugins/techdocs-node/src/helpers.ts b/plugins/techdocs-node/src/helpers.ts index d4816e1f72..67bb862c62 100644 --- a/plugins/techdocs-node/src/helpers.ts +++ b/plugins/techdocs-node/src/helpers.ts @@ -14,8 +14,11 @@ * limitations under the License. */ -import { UrlReaderService } from '@backstage/backend-plugin-api'; -import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; +import { + LoggerService, + UrlReaderService, + resolveSafeChildPath, +} from '@backstage/backend-plugin-api'; import { Entity, getEntitySourceLocation, @@ -25,7 +28,6 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common'; import path from 'path'; -import { Logger } from 'winston'; import { PreparerResponse, RemoteProtocol } from './stages/prepare/types'; /** @@ -145,7 +147,7 @@ export const getLocationForEntity = ( export const getDocFilesFromRepository = async ( reader: UrlReaderService, entity: Entity, - opts?: { etag?: string; logger?: Logger }, + opts?: { etag?: string; logger?: LoggerService }, ): Promise => { const { target } = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity); diff --git a/plugins/techdocs-node/src/stages/generate/generators.test.ts b/plugins/techdocs-node/src/stages/generate/generators.test.ts index f80f070796..3e46eb9f9d 100644 --- a/plugins/techdocs-node/src/stages/generate/generators.test.ts +++ b/plugins/techdocs-node/src/stages/generate/generators.test.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { Generators } from './generators'; import { TechdocsGenerator } from './techdocs'; import { mockServices } from '@backstage/backend-test-utils'; -const logger = loggerToWinstonLogger(mockServices.logger.mock()); +const logger = mockServices.logger.mock(); const mockEntity = { apiVersion: 'version', diff --git a/plugins/techdocs-node/src/stages/generate/generators.ts b/plugins/techdocs-node/src/stages/generate/generators.ts index 0c20ac1588..7c8d169d1e 100644 --- a/plugins/techdocs-node/src/stages/generate/generators.ts +++ b/plugins/techdocs-node/src/stages/generate/generators.ts @@ -14,10 +14,8 @@ * limitations under the License. */ -import { ContainerRunner } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import { Logger } from 'winston'; import { getGeneratorKey } from './helpers'; import { TechdocsGenerator } from './techdocs'; import { @@ -25,6 +23,8 @@ import { GeneratorBuilder, SupportedGeneratorKey, } from './types'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { TechDocsContainerRunner } from '../publish/types'; /** * Collection of docs generators @@ -41,8 +41,8 @@ export class Generators implements GeneratorBuilder { static async fromConfig( config: Config, options: { - logger: Logger; - containerRunner?: ContainerRunner; + logger: LoggerService; + containerRunner?: TechDocsContainerRunner; customGenerator?: TechdocsGenerator; }, ): Promise { diff --git a/plugins/techdocs-node/src/stages/generate/helpers.test.ts b/plugins/techdocs-node/src/stages/generate/helpers.test.ts index 695f5b8f2c..755cd9d1a6 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.test.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.test.ts @@ -37,7 +37,6 @@ import { patchMkdocsYmlWithPlugins, } from './mkdocsPatchers'; import yaml from 'js-yaml'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; const mockEntity = { apiVersion: 'version', @@ -93,7 +92,7 @@ const mkdocsYmlWithAdditionalPluginsWithConfig = fs.readFileSync( const mkdocsYmlWithEnvTag = fs.readFileSync( resolvePath(__filename, '../__fixtures__/mkdocs_with_env_tag.yml'), ); -const mockLogger = loggerToWinstonLogger(mockServices.logger.mock()); +const mockLogger = mockServices.logger.mock(); const warn = jest.spyOn(mockLogger, 'warn'); const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({})); diff --git a/plugins/techdocs-node/src/stages/generate/helpers.ts b/plugins/techdocs-node/src/stages/generate/helpers.ts index 9c1996a772..d62b6dcccd 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { isChildPath } from '@backstage/backend-plugin-api'; +import { isChildPath, LoggerService } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { assertError, ForwardedError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -24,7 +24,6 @@ import gitUrlParse from 'git-url-parse'; import yaml, { DEFAULT_SCHEMA, Type } from 'js-yaml'; import path, { resolve as resolvePath } from 'path'; import { PassThrough, Writable } from 'stream'; -import { Logger } from 'winston'; import { ParsedLocationAnnotation } from '../../helpers'; import { DefaultMkdocsContent, SupportedGeneratorKey } from './types'; import { getFileTreeRecursively } from '../publish/helpers'; @@ -298,7 +297,7 @@ export const patchIndexPreBuild = async ({ docsDir = 'docs', }: { inputDir: string; - logger: Logger; + logger: LoggerService; docsDir?: string; }) => { const docsPath = path.join(inputDir, docsDir); @@ -342,7 +341,7 @@ export const patchIndexPreBuild = async ({ */ export const createOrUpdateMetadata = async ( techdocsMetadataPath: string, - logger: Logger, + logger: LoggerService, ): Promise => { const techdocsMetadataDir = techdocsMetadataPath .split(path.sep) diff --git a/plugins/techdocs-node/src/stages/generate/mkdocsPatchers.ts b/plugins/techdocs-node/src/stages/generate/mkdocsPatchers.ts index fb95c19a9c..804c06f129 100644 --- a/plugins/techdocs-node/src/stages/generate/mkdocsPatchers.ts +++ b/plugins/techdocs-node/src/stages/generate/mkdocsPatchers.ts @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Logger } from 'winston'; import fs from 'fs-extra'; import yaml from 'js-yaml'; import { ParsedLocationAnnotation } from '../../helpers'; import { getRepoUrlFromLocationAnnotation, MKDOCS_SCHEMA } from './helpers'; import { assertError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { LoggerService } from '@backstage/backend-plugin-api'; type MkDocsObject = { plugins?: string[]; @@ -30,7 +30,7 @@ type MkDocsObject = { const patchMkdocsFile = async ( mkdocsYmlPath: string, - logger: Logger, + logger: LoggerService, updateAction: (mkdocsYml: MkDocsObject) => boolean, ) => { // We only want to override the mkdocs.yml if it has actually changed. This is relevant if @@ -103,7 +103,7 @@ const patchMkdocsFile = async ( */ export const patchMkdocsYmlPreBuild = async ( mkdocsYmlPath: string, - logger: Logger, + logger: LoggerService, parsedLocationAnnotation: ParsedLocationAnnotation, scmIntegrations: ScmIntegrationRegistry, ) => { @@ -149,7 +149,7 @@ export const patchMkdocsYmlPreBuild = async ( */ export const patchMkdocsYmlWithPlugins = async ( mkdocsYmlPath: string, - logger: Logger, + logger: LoggerService, defaultPlugins: string[] = ['techdocs-core'], ) => { await patchMkdocsFile(mkdocsYmlPath, logger, mkdocsYml => { diff --git a/plugins/techdocs-node/src/stages/generate/techdocs.ts b/plugins/techdocs-node/src/stages/generate/techdocs.ts index 599457ee5a..554f1c29bd 100644 --- a/plugins/techdocs-node/src/stages/generate/techdocs.ts +++ b/plugins/techdocs-node/src/stages/generate/techdocs.ts @@ -16,7 +16,6 @@ import { Config } from '@backstage/config'; import path from 'path'; -import { Logger } from 'winston'; import { ScmIntegrationRegistry, ScmIntegrations, @@ -43,7 +42,8 @@ import { } from './types'; import { ForwardedError } from '@backstage/errors'; import { DockerContainerRunner } from './DockerContainerRunner'; -import { ContainerRunner } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { TechDocsContainerRunner } from '../publish/types'; /** * Generates documentation files @@ -55,8 +55,8 @@ export class TechdocsGenerator implements GeneratorBase { * and static so that techdocs-node consumers can use the same version. */ public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.4'; - private readonly logger: Logger; - private readonly containerRunner?: ContainerRunner; + private readonly logger: LoggerService; + private readonly containerRunner?: TechDocsContainerRunner; private readonly options: GeneratorConfig; private readonly scmIntegrations: ScmIntegrationRegistry; @@ -77,8 +77,8 @@ export class TechdocsGenerator implements GeneratorBase { } constructor(options: { - logger: Logger; - containerRunner?: ContainerRunner; + logger: LoggerService; + containerRunner?: TechDocsContainerRunner; config: Config; scmIntegrations: ScmIntegrationRegistry; }) { @@ -216,7 +216,7 @@ export class TechdocsGenerator implements GeneratorBase { export function readGeneratorConfig( config: Config, - logger: Logger, + logger: LoggerService, ): GeneratorConfig { const legacyGeneratorType = config.getOptionalString( 'techdocs.generators.techdocs', diff --git a/plugins/techdocs-node/src/stages/generate/types.ts b/plugins/techdocs-node/src/stages/generate/types.ts index ca816d560f..96c90f5c4d 100644 --- a/plugins/techdocs-node/src/stages/generate/types.ts +++ b/plugins/techdocs-node/src/stages/generate/types.ts @@ -16,9 +16,9 @@ import { Entity } from '@backstage/catalog-model'; import { Writable } from 'stream'; -import { Logger } from 'winston'; import { ParsedLocationAnnotation } from '../../helpers'; -import { ContainerRunner } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { TechDocsContainerRunner } from '../publish/types'; // Determines where the generator will be run export type GeneratorRunInType = 'docker' | 'local'; @@ -28,12 +28,12 @@ export type GeneratorRunInType = 'docker' | 'local'; * @public */ export type GeneratorOptions = { - logger: Logger; + logger: LoggerService; /** * @deprecated containerRunner is now instantiated in * the generator and this option will be removed in the future */ - containerRunner?: ContainerRunner; + containerRunner?: TechDocsContainerRunner; }; /** @@ -65,7 +65,7 @@ export type GeneratorRunOptions = { outputDir: string; parsedLocationAnnotation?: ParsedLocationAnnotation; etag?: string; - logger: Logger; + logger: LoggerService; logStream?: Writable; siteOptions?: { name?: string }; runAsDefaultUser?: boolean; diff --git a/plugins/techdocs-node/src/stages/prepare/dir.test.ts b/plugins/techdocs-node/src/stages/prepare/dir.test.ts index df470d7da6..2dadfa6ece 100644 --- a/plugins/techdocs-node/src/stages/prepare/dir.test.ts +++ b/plugins/techdocs-node/src/stages/prepare/dir.test.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common'; import { ConfigReader } from '@backstage/config'; import { DirectoryPreparer } from './dir'; @@ -31,7 +30,7 @@ jest.mock('../../helpers', () => ({ ...jest.requireActual<{}>('../../helpers'), })); -const logger = loggerToWinstonLogger(mockServices.logger.mock()); +const logger = mockServices.logger.mock(); const createMockEntity = (annotations: {}) => { return { diff --git a/plugins/techdocs-node/src/stages/prepare/dir.ts b/plugins/techdocs-node/src/stages/prepare/dir.ts index cecd2564e6..033b62a6a0 100644 --- a/plugins/techdocs-node/src/stages/prepare/dir.ts +++ b/plugins/techdocs-node/src/stages/prepare/dir.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { UrlReaderService } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; @@ -23,7 +22,6 @@ import { ScmIntegrations, } from '@backstage/integration'; import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common'; -import { Logger } from 'winston'; import { parseReferenceAnnotation, transformDirLocation } from '../../helpers'; import { PreparerBase, @@ -31,6 +29,7 @@ import { PreparerOptions, PreparerResponse, } from './types'; +import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api'; /** * Preparer used to retrieve documentation files from a local directory @@ -54,7 +53,7 @@ export class DirectoryPreparer implements PreparerBase { private constructor( config: Config, - _logger: Logger | null, + _logger: LoggerService | null, reader: UrlReaderService, ) { this.reader = reader; diff --git a/plugins/techdocs-node/src/stages/prepare/types.ts b/plugins/techdocs-node/src/stages/prepare/types.ts index 14ec185c97..30b4b310ed 100644 --- a/plugins/techdocs-node/src/stages/prepare/types.ts +++ b/plugins/techdocs-node/src/stages/prepare/types.ts @@ -15,8 +15,7 @@ */ import type { Entity } from '@backstage/catalog-model'; -import { UrlReaderService } from '@backstage/backend-plugin-api'; -import { Logger } from 'winston'; +import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api'; /** * A unique identifier of the tree blob, usually the commit SHA or etag from the target. @@ -29,7 +28,7 @@ export type ETag = string; * @public */ export type PreparerConfig = { - logger: Logger; + logger: LoggerService; reader: UrlReaderService; }; @@ -41,7 +40,7 @@ export type PreparerOptions = { /** * An instance of the logger */ - logger?: Logger; + logger?: LoggerService; /** * see {@link ETag} */ diff --git a/plugins/techdocs-node/src/stages/prepare/url.ts b/plugins/techdocs-node/src/stages/prepare/url.ts index 24480d2703..260ed454e8 100644 --- a/plugins/techdocs-node/src/stages/prepare/url.ts +++ b/plugins/techdocs-node/src/stages/prepare/url.ts @@ -15,9 +15,7 @@ */ import { assertError } from '@backstage/errors'; -import { UrlReaderService } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { Logger } from 'winston'; import { getDocFilesFromRepository } from '../../helpers'; import { PreparerBase, @@ -25,13 +23,14 @@ import { PreparerOptions, PreparerResponse, } from './types'; +import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api'; /** * Preparer used to retrieve documentation files from a remote repository * @public */ export class UrlPreparer implements PreparerBase { - private readonly logger: Logger; + private readonly logger: LoggerService; private readonly reader: UrlReaderService; /** @@ -42,7 +41,7 @@ export class UrlPreparer implements PreparerBase { return new UrlPreparer(options.reader, options.logger); } - private constructor(reader: UrlReaderService, logger: Logger) { + private constructor(reader: UrlReaderService, logger: LoggerService) { this.logger = logger; this.reader = reader; } diff --git a/plugins/techdocs-node/src/stages/publish/awsS3.test.ts b/plugins/techdocs-node/src/stages/publish/awsS3.test.ts index e43b303dd4..6fb855d199 100644 --- a/plugins/techdocs-node/src/stages/publish/awsS3.test.ts +++ b/plugins/techdocs-node/src/stages/publish/awsS3.test.ts @@ -42,7 +42,6 @@ import { createMockDirectory, mockServices, } from '@backstage/backend-test-utils'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; const env = process.env; let s3Mock: AwsClientStub; @@ -90,7 +89,7 @@ class ErrorReadable extends Readable { } } -const logger = loggerToWinstonLogger(mockServices.logger.mock()); +const logger = mockServices.logger.mock(); const loggerInfoSpy = jest.spyOn(logger, 'info'); const loggerErrorSpy = jest.spyOn(logger, 'error'); diff --git a/plugins/techdocs-node/src/stages/publish/awsS3.ts b/plugins/techdocs-node/src/stages/publish/awsS3.ts index 29a5277682..05902c999f 100644 --- a/plugins/techdocs-node/src/stages/publish/awsS3.ts +++ b/plugins/techdocs-node/src/stages/publish/awsS3.ts @@ -42,7 +42,6 @@ import JSON5 from 'json5'; import createLimiter from 'p-limit'; import path from 'path'; import { Readable } from 'stream'; -import { Logger } from 'winston'; import { bulkStorageOperation, getCloudPathForLocalPath, @@ -60,6 +59,7 @@ import { ReadinessResponse, TechDocsMetadata, } from './types'; +import { LoggerService } from '@backstage/backend-plugin-api'; const streamToBuffer = (stream: Readable): Promise => { return new Promise((resolve, reject) => { @@ -80,7 +80,7 @@ export class AwsS3Publish implements PublisherBase { private readonly storageClient: S3Client; private readonly bucketName: string; private readonly legacyPathCasing: boolean; - private readonly logger: Logger; + private readonly logger: LoggerService; private readonly bucketRootPath: string; private readonly sse?: 'aws:kms' | 'AES256'; @@ -88,7 +88,7 @@ export class AwsS3Publish implements PublisherBase { storageClient: S3Client; bucketName: string; legacyPathCasing: boolean; - logger: Logger; + logger: LoggerService; bucketRootPath: string; sse?: 'aws:kms' | 'AES256'; }) { @@ -102,7 +102,7 @@ export class AwsS3Publish implements PublisherBase { static async fromConfig( config: Config, - logger: Logger, + logger: LoggerService, ): Promise { let bucketName = ''; try { @@ -524,7 +524,7 @@ export class AwsS3Publish implements PublisherBase { } try { - this.logger.verbose(`Migrating ${file}`); + this.logger.debug(`Migrating ${file}`); await this.storageClient.send( new CopyObjectCommand({ Bucket: this.bucketName, diff --git a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts index 6cbfa76c81..56c2ce1b69 100644 --- a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts +++ b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import express from 'express'; @@ -224,8 +223,8 @@ const getEntityRootDir = (entity: Entity) => { return mockDir.resolve(namespace || DEFAULT_NAMESPACE, kind, name); }; -const logger = loggerToWinstonLogger(mockServices.logger.mock()); -jest.spyOn(logger, 'error').mockReturnValue(logger); +const logger = mockServices.logger.mock(); +jest.spyOn(logger, 'error'); const createPublisherFromConfig = ({ accountName = 'accountName', diff --git a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.ts b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.ts index 39a50e963a..863a4fc533 100644 --- a/plugins/techdocs-node/src/stages/publish/azureBlobStorage.ts +++ b/plugins/techdocs-node/src/stages/publish/azureBlobStorage.ts @@ -26,7 +26,6 @@ import express from 'express'; import JSON5 from 'json5'; import limiterFactory from 'p-limit'; import { default as path, default as platformPath } from 'path'; -import { Logger } from 'winston'; import { bulkStorageOperation, getCloudPathForLocalPath, @@ -43,6 +42,7 @@ import { ReadinessResponse, TechDocsMetadata, } from './types'; +import { LoggerService } from '@backstage/backend-plugin-api'; // The number of batches that may be ongoing at the same time. const BATCH_CONCURRENCY = 3; @@ -51,13 +51,13 @@ export class AzureBlobStoragePublish implements PublisherBase { private readonly storageClient: BlobServiceClient; private readonly containerName: string; private readonly legacyPathCasing: boolean; - private readonly logger: Logger; + private readonly logger: LoggerService; constructor(options: { storageClient: BlobServiceClient; containerName: string; legacyPathCasing: boolean; - logger: Logger; + logger: LoggerService; }) { this.storageClient = options.storageClient; this.containerName = options.containerName; @@ -65,7 +65,7 @@ export class AzureBlobStoragePublish implements PublisherBase { this.logger = options.logger; } - static fromConfig(config: Config, logger: Logger): PublisherBase { + static fromConfig(config: Config, logger: LoggerService): PublisherBase { let storageClient: BlobServiceClient; let containerName = ''; try { @@ -421,7 +421,7 @@ export class AzureBlobStoragePublish implements PublisherBase { if (originalPath === newPath) return; try { - this.logger.verbose(`Migrating ${originalPath}`); + this.logger.debug(`Migrating ${originalPath}`); await this.renameBlob(originalPath, newPath, removeOriginal); } catch (e) { assertError(e); diff --git a/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts b/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts index 0c94a69d6a..97627182e7 100644 --- a/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts +++ b/plugins/techdocs-node/src/stages/publish/googleStorage.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import express from 'express'; @@ -139,9 +138,9 @@ const getEntityRootDir = (entity: Entity) => { return mockDir.resolve(namespace || DEFAULT_NAMESPACE, kind, name); }; -const logger = loggerToWinstonLogger(mockServices.logger.mock()); -jest.spyOn(logger, 'info').mockReturnValue(logger); -jest.spyOn(logger, 'error').mockReturnValue(logger); +const logger = mockServices.logger.mock(); +jest.spyOn(logger, 'info'); +jest.spyOn(logger, 'error'); const createPublisherFromConfig = ({ bucketName = 'bucketName', diff --git a/plugins/techdocs-node/src/stages/publish/googleStorage.ts b/plugins/techdocs-node/src/stages/publish/googleStorage.ts index cd07f6204e..bc75a4c8bb 100644 --- a/plugins/techdocs-node/src/stages/publish/googleStorage.ts +++ b/plugins/techdocs-node/src/stages/publish/googleStorage.ts @@ -26,7 +26,6 @@ import express from 'express'; import JSON5 from 'json5'; import path from 'path'; import { Readable } from 'stream'; -import { Logger } from 'winston'; import { getFileTreeRecursively, getHeadersForFileExtension, @@ -45,19 +44,20 @@ import { ReadinessResponse, TechDocsMetadata, } from './types'; +import { LoggerService } from '@backstage/backend-plugin-api'; export class GoogleGCSPublish implements PublisherBase { private readonly storageClient: Storage; private readonly bucketName: string; private readonly legacyPathCasing: boolean; - private readonly logger: Logger; + private readonly logger: LoggerService; private readonly bucketRootPath: string; constructor(options: { storageClient: Storage; bucketName: string; legacyPathCasing: boolean; - logger: Logger; + logger: LoggerService; bucketRootPath: string; }) { this.storageClient = options.storageClient; @@ -67,7 +67,7 @@ export class GoogleGCSPublish implements PublisherBase { this.bucketRootPath = options.bucketRootPath; } - static fromConfig(config: Config, logger: Logger): PublisherBase { + static fromConfig(config: Config, logger: LoggerService): PublisherBase { let bucketName = ''; try { bucketName = config.getString('techdocs.publisher.googleGcs.bucketName'); diff --git a/plugins/techdocs-node/src/stages/publish/index.ts b/plugins/techdocs-node/src/stages/publish/index.ts index f342ccf0ae..8b6465c719 100644 --- a/plugins/techdocs-node/src/stages/publish/index.ts +++ b/plugins/techdocs-node/src/stages/publish/index.ts @@ -18,10 +18,11 @@ export type { PublisherBase, PublisherType, PublisherFactory, + PublisherBuilder, PublishRequest, PublishResponse, MigrateRequest, ReadinessResponse, TechDocsMetadata, - PublisherBuilder, + TechDocsContainerRunner, } from './types'; diff --git a/plugins/techdocs-node/src/stages/publish/local.test.ts b/plugins/techdocs-node/src/stages/publish/local.test.ts index e5e71c51ec..a6dc9c09c4 100644 --- a/plugins/techdocs-node/src/stages/publish/local.test.ts +++ b/plugins/techdocs-node/src/stages/publish/local.test.ts @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - loggerToWinstonLogger, - PluginEndpointDiscovery, -} from '@backstage/backend-common'; import { overridePackagePathResolution } from '@backstage/backend-plugin-api/testUtils'; import { ConfigReader } from '@backstage/config'; import express from 'express'; @@ -28,6 +24,7 @@ import { createMockDirectory, mockServices, } from '@backstage/backend-test-utils'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; const createMockEntity = (annotations = {}, lowerCase = false) => { return { @@ -42,7 +39,7 @@ const createMockEntity = (annotations = {}, lowerCase = false) => { }; }; -const testDiscovery: jest.Mocked = { +const testDiscovery: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007/api/techdocs'), getExternalBaseUrl: jest.fn(), }; @@ -56,7 +53,7 @@ overridePackagePathResolution({ }, }); -const logger = loggerToWinstonLogger(mockServices.logger.mock()); +const logger = mockServices.logger.mock(); describe('local publisher', () => { const mockDir = createMockDirectory(); diff --git a/plugins/techdocs-node/src/stages/publish/local.ts b/plugins/techdocs-node/src/stages/publish/local.ts index 37f41080b4..863bf12560 100644 --- a/plugins/techdocs-node/src/stages/publish/local.ts +++ b/plugins/techdocs-node/src/stages/publish/local.ts @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { PluginEndpointDiscovery } from '@backstage/backend-common'; + import { + DiscoveryService, + LoggerService, resolvePackagePath, resolveSafeChildPath, } from '@backstage/backend-plugin-api'; @@ -29,7 +31,6 @@ import fs from 'fs-extra'; import os from 'os'; import createLimiter from 'p-limit'; import path from 'path'; -import { Logger } from 'winston'; import { PublisherBase, PublishRequest, @@ -51,13 +52,13 @@ import { ForwardedError } from '@backstage/errors'; */ export class LocalPublish implements PublisherBase { private readonly legacyPathCasing: boolean; - private readonly logger: Logger; - private readonly discovery: PluginEndpointDiscovery; + private readonly logger: LoggerService; + private readonly discovery: DiscoveryService; private readonly staticDocsDir: string; constructor(options: { - logger: Logger; - discovery: PluginEndpointDiscovery; + logger: LoggerService; + discovery: DiscoveryService; legacyPathCasing: boolean; staticDocsDir: string; }) { @@ -69,8 +70,8 @@ export class LocalPublish implements PublisherBase { static fromConfig( config: Config, - logger: Logger, - discovery: PluginEndpointDiscovery, + logger: LoggerService, + discovery: DiscoveryService, ): PublisherBase { const legacyPathCasing = config.getOptionalBoolean( @@ -299,7 +300,7 @@ export class LocalPublish implements PublisherBase { // Otherwise, copy or move the file. await new Promise(resolve => { const migrate = removeOriginal ? fs.move : fs.copyFile; - this.logger.verbose(`Migrating ${relativeFile}`); + this.logger.debug(`Migrating ${relativeFile}`); migrate(file, newFile, err => { if (err) { this.logger.warn( diff --git a/plugins/techdocs-node/src/stages/publish/migrations/GoogleMigration.ts b/plugins/techdocs-node/src/stages/publish/migrations/GoogleMigration.ts index 69d5353f9f..f8ae8c316e 100644 --- a/plugins/techdocs-node/src/stages/publish/migrations/GoogleMigration.ts +++ b/plugins/techdocs-node/src/stages/publish/migrations/GoogleMigration.ts @@ -17,20 +17,24 @@ import { assertError } from '@backstage/errors'; import { File } from '@google-cloud/storage'; import { Writable } from 'stream'; -import { Logger } from 'winston'; import { lowerCaseEntityTripletInStoragePath } from '../helpers'; +import { LoggerService } from '@backstage/backend-plugin-api'; /** * Writable stream to handle object copy/move operations. This implementation * ensures we don't read in files from GCS faster than GCS can copy/move them. */ export class MigrateWriteStream extends Writable { - protected logger: Logger; + protected logger: LoggerService; protected removeOriginal: boolean; protected maxConcurrency: number; protected inFlight = 0; - constructor(logger: Logger, removeOriginal: boolean, concurrency: number) { + constructor( + logger: LoggerService, + removeOriginal: boolean, + concurrency: number, + ) { super({ objectMode: true }); this.logger = logger; this.removeOriginal = removeOriginal; @@ -66,7 +70,7 @@ export class MigrateWriteStream extends Writable { const migrate = this.removeOriginal ? file.move.bind(file) : file.copy.bind(file); - this.logger.verbose(`Migrating ${file.name}`); + this.logger.debug(`Migrating ${file.name}`); migrate(newFile) .catch(e => this.logger.warn(`Unable to migrate ${file.name}: ${e.message}`), diff --git a/plugins/techdocs-node/src/stages/publish/openStackSwift.test.ts b/plugins/techdocs-node/src/stages/publish/openStackSwift.test.ts index aa0e5f0afa..4d9972390f 100644 --- a/plugins/techdocs-node/src/stages/publish/openStackSwift.test.ts +++ b/plugins/techdocs-node/src/stages/publish/openStackSwift.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { Entity, CompoundEntityRef, @@ -172,7 +171,7 @@ const getPosixEntityRootDir = (entity: Entity) => { ); }; -const logger = loggerToWinstonLogger(mockServices.logger.mock()); +const logger = mockServices.logger.mock(); let publisher: PublisherBase; diff --git a/plugins/techdocs-node/src/stages/publish/openStackSwift.ts b/plugins/techdocs-node/src/stages/publish/openStackSwift.ts index 734a7e4451..43e7deb2f9 100644 --- a/plugins/techdocs-node/src/stages/publish/openStackSwift.ts +++ b/plugins/techdocs-node/src/stages/publish/openStackSwift.ts @@ -23,7 +23,7 @@ import path from 'path'; import { SwiftClient } from '@trendyol-js/openstack-swift-sdk'; import { NotFound } from '@trendyol-js/openstack-swift-sdk/lib/types'; import { Stream, Readable } from 'stream'; -import { Logger } from 'winston'; + import { getFileTreeRecursively, getHeadersForFileExtension, @@ -37,6 +37,7 @@ import { TechDocsMetadata, } from './types'; import { assertError, ForwardedError } from '@backstage/errors'; +import { LoggerService } from '@backstage/backend-plugin-api'; const streamToBuffer = (stream: Stream | Readable): Promise => { return new Promise((resolve, reject) => { @@ -61,19 +62,19 @@ const bufferToStream = (buffer: Buffer): Readable => { export class OpenStackSwiftPublish implements PublisherBase { private readonly storageClient: SwiftClient; private readonly containerName: string; - private readonly logger: Logger; + private readonly logger: LoggerService; constructor(options: { storageClient: SwiftClient; containerName: string; - logger: Logger; + logger: LoggerService; }) { this.storageClient = options.storageClient; this.containerName = options.containerName; this.logger = options.logger; } - static fromConfig(config: Config, logger: Logger): PublisherBase { + static fromConfig(config: Config, logger: LoggerService): PublisherBase { let containerName = ''; try { containerName = config.getString( @@ -326,7 +327,7 @@ export class OpenStackSwiftPublish implements PublisherBase { } try { - this.logger.verbose(`Migrating ${file} to ${newPath}`); + this.logger.debug(`Migrating ${file} to ${newPath}`); await this.storageClient.copy( this.containerName, file, diff --git a/plugins/techdocs-node/src/stages/publish/publish.test.ts b/plugins/techdocs-node/src/stages/publish/publish.test.ts index 0063839e2f..408a171fe0 100644 --- a/plugins/techdocs-node/src/stages/publish/publish.test.ts +++ b/plugins/techdocs-node/src/stages/publish/publish.test.ts @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - PluginEndpointDiscovery, - loggerToWinstonLogger, -} from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { Publisher } from './publish'; import { LocalPublish } from './local'; @@ -26,9 +22,10 @@ import { AzureBlobStoragePublish } from './azureBlobStorage'; import { OpenStackSwiftPublish } from './openStackSwift'; import { mockServices } from '@backstage/backend-test-utils'; import { PublisherBase } from './types'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; -const logger = loggerToWinstonLogger(mockServices.logger.mock()); -const discovery: jest.Mocked = { +const logger = mockServices.logger.mock(); +const discovery: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7007'), getExternalBaseUrl: jest.fn(), }; diff --git a/plugins/techdocs-node/src/stages/publish/types.ts b/plugins/techdocs-node/src/stages/publish/types.ts index 3acd48a468..89af730404 100644 --- a/plugins/techdocs-node/src/stages/publish/types.ts +++ b/plugins/techdocs-node/src/stages/publish/types.ts @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import { Logger } from 'winston'; +import { Writable } from 'stream'; import express from 'express'; import { Config } from '@backstage/config'; +import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api'; +import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; /** * Options for building publishers * @public */ export type PublisherFactory = { - logger: Logger; - discovery: PluginEndpointDiscovery; + logger: LoggerService; + discovery: DiscoveryService; customPublisher?: PublisherBase | undefined; }; @@ -168,3 +168,36 @@ export type PublisherBuilder = { register(type: PublisherType, publisher: PublisherBase): void; get(config: Config): PublisherBase; }; + +/** + * Handles the running of containers, on behalf of others. + * + * @public + */ +export interface TechDocsContainerRunner { + /** + * Runs a container image to completion. + */ + runContainer(opts: { + imageName: string; + command?: string | string[]; + args: string[]; + logStream?: Writable; + mountDirs?: Record; + workingDir?: string; + envVars?: Record; + pullImage?: boolean; + defaultUser?: boolean; + pullOptions?: { + authconfig?: { + username?: string; + password?: string; + auth?: string; + email?: string; + serveraddress?: string; + [key: string]: unknown; + }; + [key: string]: unknown; + }; + }): Promise; +}