@backstage/plugin-techdocs-backend 1.11.7-next.0 → 1.11.7-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @backstage/plugin-techdocs-backend
|
|
2
2
|
|
|
3
|
+
## 1.11.7-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7828186: Minor type fix
|
|
8
|
+
- 8f03776: Properly clean up temporary files on build failures
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/integration@1.16.2-next.0
|
|
11
|
+
- @backstage/backend-plugin-api@1.2.1-next.1
|
|
12
|
+
- @backstage/catalog-client@1.9.1
|
|
13
|
+
- @backstage/catalog-model@1.7.3
|
|
14
|
+
- @backstage/config@1.3.2
|
|
15
|
+
- @backstage/errors@1.2.7
|
|
16
|
+
- @backstage/plugin-catalog-common@1.1.3
|
|
17
|
+
- @backstage/plugin-catalog-node@1.16.1-next.1
|
|
18
|
+
- @backstage/plugin-permission-common@0.8.4
|
|
19
|
+
- @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2
|
|
20
|
+
- @backstage/plugin-techdocs-common@0.1.0
|
|
21
|
+
- @backstage/plugin-techdocs-node@1.13.1-next.2
|
|
22
|
+
|
|
23
|
+
## 1.11.7-next.1
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies
|
|
28
|
+
- @backstage/backend-plugin-api@1.2.1-next.1
|
|
29
|
+
- @backstage/catalog-client@1.9.1
|
|
30
|
+
- @backstage/catalog-model@1.7.3
|
|
31
|
+
- @backstage/config@1.3.2
|
|
32
|
+
- @backstage/errors@1.2.7
|
|
33
|
+
- @backstage/integration@1.16.1
|
|
34
|
+
- @backstage/plugin-catalog-common@1.1.3
|
|
35
|
+
- @backstage/plugin-catalog-node@1.16.1-next.1
|
|
36
|
+
- @backstage/plugin-permission-common@0.8.4
|
|
37
|
+
- @backstage/plugin-search-backend-module-techdocs@0.3.7-next.1
|
|
38
|
+
- @backstage/plugin-techdocs-common@0.1.0
|
|
39
|
+
- @backstage/plugin-techdocs-node@1.13.1-next.1
|
|
40
|
+
|
|
3
41
|
## 1.11.7-next.0
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
|
@@ -75,94 +75,100 @@ class DocsBuilder {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
let preparedDir;
|
|
78
|
-
let
|
|
78
|
+
let outputDir;
|
|
79
79
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
let newEtag;
|
|
81
|
+
try {
|
|
82
|
+
const preparerResponse = await this.preparer.prepare(this.entity, {
|
|
83
|
+
etag: storedEtag,
|
|
84
|
+
logger: this.logger
|
|
85
|
+
});
|
|
86
|
+
preparedDir = preparerResponse.preparedDir;
|
|
87
|
+
newEtag = preparerResponse.etag;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (errors.isError(err) && err.name === "NotModifiedError") {
|
|
90
|
+
new BuildMetadataStorage.BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();
|
|
91
|
+
this.logger.debug(
|
|
92
|
+
`Docs for ${catalogModel.stringifyEntityRef(
|
|
93
|
+
this.entity
|
|
94
|
+
)} are unmodified. Using cache, skipping generate and prepare`
|
|
95
|
+
);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
this.logger.info(
|
|
101
|
+
`Prepare step completed for entity ${catalogModel.stringifyEntityRef(
|
|
102
|
+
this.entity
|
|
103
|
+
)}, stored at ${preparedDir}`
|
|
104
|
+
);
|
|
105
|
+
this.logger.info(
|
|
106
|
+
`Step 2 of 3: Generating docs for entity ${catalogModel.stringifyEntityRef(
|
|
107
|
+
this.entity
|
|
108
|
+
)}`
|
|
109
|
+
);
|
|
110
|
+
const workingDir = this.config.getOptionalString(
|
|
111
|
+
"backend.workingDirectory"
|
|
112
|
+
);
|
|
113
|
+
const tmpdirPath = workingDir || os__default.default.tmpdir();
|
|
114
|
+
const tmpdirResolvedPath = fs__default.default.realpathSync(tmpdirPath);
|
|
115
|
+
outputDir = await fs__default.default.mkdtemp(
|
|
116
|
+
path__default.default.join(tmpdirResolvedPath, "techdocs-tmp-")
|
|
117
|
+
);
|
|
118
|
+
const parsedLocationAnnotation = pluginTechdocsNode.getLocationForEntity(
|
|
119
|
+
this.entity,
|
|
120
|
+
this.scmIntegrations
|
|
121
|
+
);
|
|
122
|
+
await this.generator.run({
|
|
123
|
+
inputDir: preparedDir,
|
|
124
|
+
outputDir,
|
|
125
|
+
parsedLocationAnnotation,
|
|
126
|
+
etag: newEtag,
|
|
127
|
+
logger: this.logger,
|
|
128
|
+
logStream: this.logStream,
|
|
129
|
+
siteOptions: {
|
|
130
|
+
name: this.entity.metadata.title ?? this.entity.metadata.name
|
|
131
|
+
}
|
|
83
132
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
133
|
+
this.logger.info(
|
|
134
|
+
`Step 3 of 3: Publishing docs for entity ${catalogModel.stringifyEntityRef(
|
|
135
|
+
this.entity
|
|
136
|
+
)}`
|
|
137
|
+
);
|
|
138
|
+
const published = await this.publisher.publish({
|
|
139
|
+
entity: this.entity,
|
|
140
|
+
directory: outputDir
|
|
141
|
+
});
|
|
142
|
+
if (this.cache && published && published?.objects?.length) {
|
|
89
143
|
this.logger.debug(
|
|
90
|
-
`
|
|
91
|
-
this.entity
|
|
92
|
-
)} are unmodified. Using cache, skipping generate and prepare`
|
|
144
|
+
`Invalidating ${published.objects.length} cache objects`
|
|
93
145
|
);
|
|
94
|
-
|
|
146
|
+
await this.cache.invalidateMultiple(published.objects);
|
|
95
147
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
);
|
|
108
|
-
const workingDir = this.config.getOptionalString(
|
|
109
|
-
"backend.workingDirectory"
|
|
110
|
-
);
|
|
111
|
-
const tmpdirPath = workingDir || os__default.default.tmpdir();
|
|
112
|
-
const tmpdirResolvedPath = fs__default.default.realpathSync(tmpdirPath);
|
|
113
|
-
const outputDir = await fs__default.default.mkdtemp(
|
|
114
|
-
path__default.default.join(tmpdirResolvedPath, "techdocs-tmp-")
|
|
115
|
-
);
|
|
116
|
-
const parsedLocationAnnotation = pluginTechdocsNode.getLocationForEntity(
|
|
117
|
-
this.entity,
|
|
118
|
-
this.scmIntegrations
|
|
119
|
-
);
|
|
120
|
-
await this.generator.run({
|
|
121
|
-
inputDir: preparedDir,
|
|
122
|
-
outputDir,
|
|
123
|
-
parsedLocationAnnotation,
|
|
124
|
-
etag: newEtag,
|
|
125
|
-
logger: this.logger,
|
|
126
|
-
logStream: this.logStream,
|
|
127
|
-
siteOptions: {
|
|
128
|
-
name: this.entity.metadata.title ?? this.entity.metadata.name
|
|
148
|
+
} finally {
|
|
149
|
+
if (preparedDir && this.preparer.shouldCleanPreparedDirectory()) {
|
|
150
|
+
this.logger.debug(`Removing prepared directory ${preparedDir}`);
|
|
151
|
+
try {
|
|
152
|
+
fs__default.default.remove(preparedDir);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
errors.assertError(error);
|
|
155
|
+
this.logger.debug(
|
|
156
|
+
`Error removing prepared directory ${error.message}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
129
159
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
160
|
+
if (outputDir) {
|
|
161
|
+
this.logger.debug(`Removing generated directory ${outputDir}`);
|
|
162
|
+
try {
|
|
163
|
+
fs__default.default.remove(outputDir);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
errors.assertError(error);
|
|
166
|
+
this.logger.debug(
|
|
167
|
+
`Error removing generated directory ${error.message}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
140
170
|
}
|
|
141
171
|
}
|
|
142
|
-
this.logger.info(
|
|
143
|
-
`Step 3 of 3: Publishing docs for entity ${catalogModel.stringifyEntityRef(
|
|
144
|
-
this.entity
|
|
145
|
-
)}`
|
|
146
|
-
);
|
|
147
|
-
const published = await this.publisher.publish({
|
|
148
|
-
entity: this.entity,
|
|
149
|
-
directory: outputDir
|
|
150
|
-
});
|
|
151
|
-
if (this.cache && published && published?.objects?.length) {
|
|
152
|
-
this.logger.debug(
|
|
153
|
-
`Invalidating ${published.objects.length} cache objects`
|
|
154
|
-
);
|
|
155
|
-
await this.cache.invalidateMultiple(published.objects);
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
fs__default.default.remove(outputDir);
|
|
159
|
-
this.logger.debug(
|
|
160
|
-
`Removing generated directory ${outputDir} since the site has been published`
|
|
161
|
-
);
|
|
162
|
-
} catch (error) {
|
|
163
|
-
errors.assertError(error);
|
|
164
|
-
this.logger.debug(`Error removing generated directory ${error.message}`);
|
|
165
|
-
}
|
|
166
172
|
new BuildMetadataStorage.BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();
|
|
167
173
|
return true;
|
|
168
174
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.cjs.js","sources":["../../src/DocsBuilder/builder.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { assertError, isError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBase,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBase,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport path from 'path';\nimport { Writable } from 'stream';\nimport { Logger } from 'winston';\nimport { BuildMetadataStorage } from './BuildMetadataStorage';\nimport { TechDocsCache } from '../cache';\n\ntype DocsBuilderArguments = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n entity: Entity;\n logger: Logger;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n logStream?: Writable;\n cache?: TechDocsCache;\n};\n\nexport class DocsBuilder {\n private preparer: PreparerBase;\n private generator: GeneratorBase;\n private publisher: PublisherBase;\n private entity: Entity;\n private logger: Logger;\n private config: Config;\n private scmIntegrations: ScmIntegrationRegistry;\n private logStream: Writable | undefined;\n private cache?: TechDocsCache;\n\n constructor({\n preparers,\n generators,\n publisher,\n entity,\n logger,\n config,\n scmIntegrations,\n logStream,\n cache,\n }: DocsBuilderArguments) {\n this.preparer = preparers.get(entity);\n this.generator = generators.get(entity);\n this.publisher = publisher;\n this.entity = entity;\n this.logger = logger;\n this.config = config;\n this.scmIntegrations = scmIntegrations;\n this.logStream = logStream;\n this.cache = cache;\n }\n\n /**\n * Build the docs and return whether they have been newly generated or have been cached\n * @returns true, if the docs have been built. false, if the cached docs are still up-to-date.\n */\n public async build(): Promise<boolean> {\n if (!this.entity.metadata.uid) {\n throw new Error(\n 'Trying to build documentation for entity not in software catalog',\n );\n }\n\n /**\n * Prepare (and cache check)\n */\n\n this.logger.info(\n `Step 1 of 3: Preparing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n // If available, use the etag stored in techdocs_metadata.json to\n // check if docs are outdated and need to be regenerated.\n let storedEtag: string | undefined;\n if (await this.publisher.hasDocsBeenGenerated(this.entity)) {\n try {\n storedEtag = (\n await this.publisher.fetchTechDocsMetadata({\n namespace: this.entity.metadata.namespace ?? DEFAULT_NAMESPACE,\n kind: this.entity.kind,\n name: this.entity.metadata.name,\n })\n ).etag;\n } catch (err) {\n // Proceed with a fresh build\n this.logger.warn(\n `Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`,\n );\n }\n }\n\n let preparedDir: string;\n let newEtag: string;\n try {\n const preparerResponse = await this.preparer.prepare(this.entity, {\n etag: storedEtag,\n logger: this.logger,\n });\n\n preparedDir = preparerResponse.preparedDir;\n newEtag = preparerResponse.etag;\n } catch (err) {\n if (isError(err) && err.name === 'NotModifiedError') {\n // No need to prepare anymore since cache is valid.\n // Set last check happened to now\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n this.logger.debug(\n `Docs for ${stringifyEntityRef(\n this.entity,\n )} are unmodified. Using cache, skipping generate and prepare`,\n );\n return false;\n }\n throw err;\n }\n\n this.logger.info(\n `Prepare step completed for entity ${stringifyEntityRef(\n this.entity,\n )}, stored at ${preparedDir}`,\n );\n\n /**\n * Generate\n */\n\n this.logger.info(\n `Step 2 of 3: Generating docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const workingDir = this.config.getOptionalString(\n 'backend.workingDirectory',\n );\n const tmpdirPath = workingDir || os.tmpdir();\n // Fixes a problem with macOS returning a path that is a symlink\n const tmpdirResolvedPath = fs.realpathSync(tmpdirPath);\n const outputDir = await fs.mkdtemp(\n path.join(tmpdirResolvedPath, 'techdocs-tmp-'),\n );\n\n const parsedLocationAnnotation = getLocationForEntity(\n this.entity,\n this.scmIntegrations,\n );\n await this.generator.run({\n inputDir: preparedDir,\n outputDir,\n parsedLocationAnnotation,\n etag: newEtag,\n logger: this.logger,\n logStream: this.logStream,\n siteOptions: {\n name: this.entity.metadata.title ?? this.entity.metadata.name,\n },\n });\n\n // Remove Prepared directory since it is no longer needed.\n // Caveat: Can not remove prepared directory in case of git preparer since the\n // local git repository is used to get etag on subsequent requests.\n if (this.preparer.shouldCleanPreparedDirectory()) {\n this.logger.debug(\n `Removing prepared directory ${preparedDir} since the site has been generated`,\n );\n try {\n // Not a blocker hence no need to await this.\n fs.remove(preparedDir);\n } catch (error) {\n assertError(error);\n this.logger.debug(`Error removing prepared directory ${error.message}`);\n }\n }\n\n /**\n * Publish\n */\n\n this.logger.info(\n `Step 3 of 3: Publishing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const published = await this.publisher.publish({\n entity: this.entity,\n directory: outputDir,\n });\n\n // Invalidate the cache for any published objects.\n if (this.cache && published && published?.objects?.length) {\n this.logger.debug(\n `Invalidating ${published.objects.length} cache objects`,\n );\n await this.cache.invalidateMultiple(published.objects);\n }\n\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n this.logger.debug(\n `Removing generated directory ${outputDir} since the site has been published`,\n );\n } catch (error) {\n assertError(error);\n this.logger.debug(`Error removing generated directory ${error.message}`);\n }\n\n // Update the last check time for the entity\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n\n return true;\n }\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","BuildMetadataStorage","os","fs","path","getLocationForEntity","assertError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builder.cjs.js","sources":["../../src/DocsBuilder/builder.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { assertError, isError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBase,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBase,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport path from 'path';\nimport { Writable } from 'stream';\nimport { Logger } from 'winston';\nimport { BuildMetadataStorage } from './BuildMetadataStorage';\nimport { TechDocsCache } from '../cache';\n\ntype DocsBuilderArguments = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n entity: Entity;\n logger: Logger;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n logStream?: Writable;\n cache?: TechDocsCache;\n};\n\nexport class DocsBuilder {\n private preparer: PreparerBase;\n private generator: GeneratorBase;\n private publisher: PublisherBase;\n private entity: Entity;\n private logger: Logger;\n private config: Config;\n private scmIntegrations: ScmIntegrationRegistry;\n private logStream: Writable | undefined;\n private cache?: TechDocsCache;\n\n constructor({\n preparers,\n generators,\n publisher,\n entity,\n logger,\n config,\n scmIntegrations,\n logStream,\n cache,\n }: DocsBuilderArguments) {\n this.preparer = preparers.get(entity);\n this.generator = generators.get(entity);\n this.publisher = publisher;\n this.entity = entity;\n this.logger = logger;\n this.config = config;\n this.scmIntegrations = scmIntegrations;\n this.logStream = logStream;\n this.cache = cache;\n }\n\n /**\n * Build the docs and return whether they have been newly generated or have been cached\n * @returns true, if the docs have been built. false, if the cached docs are still up-to-date.\n */\n public async build(): Promise<boolean> {\n if (!this.entity.metadata.uid) {\n throw new Error(\n 'Trying to build documentation for entity not in software catalog',\n );\n }\n\n /**\n * Prepare (and cache check)\n */\n\n this.logger.info(\n `Step 1 of 3: Preparing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n // If available, use the etag stored in techdocs_metadata.json to\n // check if docs are outdated and need to be regenerated.\n let storedEtag: string | undefined;\n if (await this.publisher.hasDocsBeenGenerated(this.entity)) {\n try {\n storedEtag = (\n await this.publisher.fetchTechDocsMetadata({\n namespace: this.entity.metadata.namespace ?? DEFAULT_NAMESPACE,\n kind: this.entity.kind,\n name: this.entity.metadata.name,\n })\n ).etag;\n } catch (err) {\n // Proceed with a fresh build\n this.logger.warn(\n `Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`,\n );\n }\n }\n\n let preparedDir: string | undefined;\n let outputDir: string | undefined;\n\n try {\n let newEtag: string;\n try {\n const preparerResponse = await this.preparer.prepare(this.entity, {\n etag: storedEtag,\n logger: this.logger,\n });\n\n preparedDir = preparerResponse.preparedDir;\n newEtag = preparerResponse.etag;\n } catch (err) {\n if (isError(err) && err.name === 'NotModifiedError') {\n // No need to prepare anymore since cache is valid.\n // Set last check happened to now\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n this.logger.debug(\n `Docs for ${stringifyEntityRef(\n this.entity,\n )} are unmodified. Using cache, skipping generate and prepare`,\n );\n return false;\n }\n throw err;\n }\n\n this.logger.info(\n `Prepare step completed for entity ${stringifyEntityRef(\n this.entity,\n )}, stored at ${preparedDir}`,\n );\n\n /**\n * Generate\n */\n\n this.logger.info(\n `Step 2 of 3: Generating docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const workingDir = this.config.getOptionalString(\n 'backend.workingDirectory',\n );\n const tmpdirPath = workingDir || os.tmpdir();\n // Fixes a problem with macOS returning a path that is a symlink\n const tmpdirResolvedPath = fs.realpathSync(tmpdirPath);\n outputDir = await fs.mkdtemp(\n path.join(tmpdirResolvedPath, 'techdocs-tmp-'),\n );\n\n const parsedLocationAnnotation = getLocationForEntity(\n this.entity,\n this.scmIntegrations,\n );\n await this.generator.run({\n inputDir: preparedDir,\n outputDir,\n parsedLocationAnnotation,\n etag: newEtag,\n logger: this.logger,\n logStream: this.logStream,\n siteOptions: {\n name: this.entity.metadata.title ?? this.entity.metadata.name,\n },\n });\n\n /**\n * Publish\n */\n\n this.logger.info(\n `Step 3 of 3: Publishing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const published = await this.publisher.publish({\n entity: this.entity,\n directory: outputDir,\n });\n\n // Invalidate the cache for any published objects.\n if (this.cache && published && published?.objects?.length) {\n this.logger.debug(\n `Invalidating ${published.objects.length} cache objects`,\n );\n await this.cache.invalidateMultiple(published.objects);\n }\n } finally {\n // Remove Prepared directory since it is no longer needed.\n // Caveat: Can not remove prepared directory in case of git preparer since the\n // local git repository is used to get etag on subsequent requests.\n if (preparedDir && this.preparer.shouldCleanPreparedDirectory()) {\n this.logger.debug(`Removing prepared directory ${preparedDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(preparedDir);\n } catch (error) {\n assertError(error);\n this.logger.debug(\n `Error removing prepared directory ${error.message}`,\n );\n }\n }\n\n if (outputDir) {\n this.logger.debug(`Removing generated directory ${outputDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n } catch (error) {\n assertError(error);\n this.logger.debug(\n `Error removing generated directory ${error.message}`,\n );\n }\n }\n }\n\n // Update the last check time for the entity\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n\n return true;\n }\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","BuildMetadataStorage","os","fs","path","getLocationForEntity","assertError"],"mappings":"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"}
|
|
@@ -24,7 +24,9 @@ const createCacheMiddleware = ({
|
|
|
24
24
|
let writeToCache = true;
|
|
25
25
|
const chunks = [];
|
|
26
26
|
socket.write = (data, encoding, callback) => {
|
|
27
|
-
chunks.push(
|
|
27
|
+
chunks.push(
|
|
28
|
+
typeof data === "string" ? Buffer.from(data) : Buffer.from(data)
|
|
29
|
+
);
|
|
28
30
|
if (typeof encoding === "function") {
|
|
29
31
|
return realWrite(data, encoding);
|
|
30
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cacheMiddleware.cjs.js","sources":["../../src/cache/cacheMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Router } from 'express';\nimport router from 'express-promise-router';\nimport { TechDocsCache } from './TechDocsCache';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\ntype CacheMiddlewareOptions = {\n cache: TechDocsCache;\n logger: LoggerService;\n};\n\ntype ErrorCallback = (err?: Error) => void;\n\nexport const createCacheMiddleware = ({\n cache,\n}: CacheMiddlewareOptions): Router => {\n const cacheMiddleware = router();\n\n // Middleware that, through socket monkey patching, captures responses as\n // they're sent over /static/docs/* and caches them. Subsequent requests are\n // loaded from cache. Cache key is the object's path (after `/static/docs/`).\n cacheMiddleware.use(async (req, res, next) => {\n const socket = res.socket;\n const isCacheable = req.path.startsWith('/static/docs/');\n const isGetRequest = req.method === 'GET';\n\n // Continue early if this is non-cacheable, or there's no socket.\n if (!isCacheable || !socket) {\n next();\n return;\n }\n\n // Make concrete references to these things.\n const reqPath = decodeURI(req.path.match(/\\/static\\/docs\\/(.*)$/)![1]);\n const realEnd = socket.end.bind(socket);\n const realWrite = socket.write.bind(socket);\n let writeToCache = true;\n const chunks: Buffer[] = [];\n\n // Monkey-patch the response's socket to keep track of chunks as they are\n // written over the wire.\n socket.write = (\n data: string | Uint8Array,\n encoding?: BufferEncoding | ErrorCallback,\n callback?: ErrorCallback,\n ) => {\n chunks.push(Buffer.from(data));\n if (typeof encoding === 'function') {\n return realWrite(data, encoding);\n }\n return realWrite(data, encoding, callback);\n };\n\n // When a socket is closed, if there were no errors and the data written\n // over the socket should be cached, cache it!\n socket.on('close', async hadError => {\n const content = Buffer.concat(chunks);\n const head = content.toString('utf8', 0, 12);\n if (\n isGetRequest &&\n writeToCache &&\n !hadError &&\n head.match(/HTTP\\/\\d\\.\\d 200/)\n ) {\n await cache.set(reqPath, content);\n }\n });\n\n // Attempt to retrieve data from the cache.\n const cached = await cache.get(reqPath);\n\n // If there is a cache hit, write it out on the socket, ensure we don't re-\n // cache the data, and prevent going back to canonical storage by never\n // calling next().\n if (cached) {\n writeToCache = false;\n realEnd(cached);\n return;\n }\n\n // No data retrieved from cache: allow retrieval from canonical storage.\n next();\n });\n\n return cacheMiddleware;\n};\n"],"names":["router"],"mappings":";;;;;;;;AA2BO,MAAM,wBAAwB,CAAC;AAAA,EACpC;AACF,CAAsC,KAAA;AACpC,EAAA,MAAM,kBAAkBA,uBAAO,EAAA;AAK/B,EAAA,eAAA,CAAgB,GAAI,CAAA,OAAO,GAAK,EAAA,GAAA,EAAK,IAAS,KAAA;AAC5C,IAAA,MAAM,SAAS,GAAI,CAAA,MAAA;AACnB,IAAA,MAAM,WAAc,GAAA,GAAA,CAAI,IAAK,CAAA,UAAA,CAAW,eAAe,CAAA;AACvD,IAAM,MAAA,YAAA,GAAe,IAAI,MAAW,KAAA,KAAA;AAGpC,IAAI,IAAA,CAAC,WAAe,IAAA,CAAC,MAAQ,EAAA;AAC3B,MAAK,IAAA,EAAA;AACL,MAAA;AAAA;AAIF,IAAM,MAAA,OAAA,GAAU,UAAU,GAAI,CAAA,IAAA,CAAK,MAAM,uBAAuB,CAAA,CAAG,CAAC,CAAC,CAAA;AACrE,IAAA,MAAM,OAAU,GAAA,MAAA,CAAO,GAAI,CAAA,IAAA,CAAK,MAAM,CAAA;AACtC,IAAA,MAAM,SAAY,GAAA,MAAA,CAAO,KAAM,CAAA,IAAA,CAAK,MAAM,CAAA;AAC1C,IAAA,IAAI,YAAe,GAAA,IAAA;AACnB,IAAA,MAAM,SAAmB,EAAC;AAI1B,IAAA,MAAA,CAAO,KAAQ,GAAA,CACb,IACA,EAAA,QAAA,EACA,QACG,KAAA;
|
|
1
|
+
{"version":3,"file":"cacheMiddleware.cjs.js","sources":["../../src/cache/cacheMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Router } from 'express';\nimport router from 'express-promise-router';\nimport { TechDocsCache } from './TechDocsCache';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\ntype CacheMiddlewareOptions = {\n cache: TechDocsCache;\n logger: LoggerService;\n};\n\ntype ErrorCallback = (err?: Error) => void;\n\nexport const createCacheMiddleware = ({\n cache,\n}: CacheMiddlewareOptions): Router => {\n const cacheMiddleware = router();\n\n // Middleware that, through socket monkey patching, captures responses as\n // they're sent over /static/docs/* and caches them. Subsequent requests are\n // loaded from cache. Cache key is the object's path (after `/static/docs/`).\n cacheMiddleware.use(async (req, res, next) => {\n const socket = res.socket;\n const isCacheable = req.path.startsWith('/static/docs/');\n const isGetRequest = req.method === 'GET';\n\n // Continue early if this is non-cacheable, or there's no socket.\n if (!isCacheable || !socket) {\n next();\n return;\n }\n\n // Make concrete references to these things.\n const reqPath = decodeURI(req.path.match(/\\/static\\/docs\\/(.*)$/)![1]);\n const realEnd = socket.end.bind(socket);\n const realWrite = socket.write.bind(socket);\n let writeToCache = true;\n const chunks: Buffer[] = [];\n\n // Monkey-patch the response's socket to keep track of chunks as they are\n // written over the wire.\n socket.write = (\n data: string | Uint8Array,\n encoding?: BufferEncoding | ErrorCallback,\n callback?: ErrorCallback,\n ) => {\n // This cast is obviously weird, but it covers a type bug in @types/node\n // which does not gracefully handle union types.\n chunks.push(\n typeof data === 'string' ? Buffer.from(data) : Buffer.from(data),\n );\n if (typeof encoding === 'function') {\n return realWrite(data, encoding);\n }\n return realWrite(data, encoding, callback);\n };\n\n // When a socket is closed, if there were no errors and the data written\n // over the socket should be cached, cache it!\n socket.on('close', async hadError => {\n const content = Buffer.concat(chunks);\n const head = content.toString('utf8', 0, 12);\n if (\n isGetRequest &&\n writeToCache &&\n !hadError &&\n head.match(/HTTP\\/\\d\\.\\d 200/)\n ) {\n await cache.set(reqPath, content);\n }\n });\n\n // Attempt to retrieve data from the cache.\n const cached = await cache.get(reqPath);\n\n // If there is a cache hit, write it out on the socket, ensure we don't re-\n // cache the data, and prevent going back to canonical storage by never\n // calling next().\n if (cached) {\n writeToCache = false;\n realEnd(cached);\n return;\n }\n\n // No data retrieved from cache: allow retrieval from canonical storage.\n next();\n });\n\n return cacheMiddleware;\n};\n"],"names":["router"],"mappings":";;;;;;;;AA2BO,MAAM,wBAAwB,CAAC;AAAA,EACpC;AACF,CAAsC,KAAA;AACpC,EAAA,MAAM,kBAAkBA,uBAAO,EAAA;AAK/B,EAAA,eAAA,CAAgB,GAAI,CAAA,OAAO,GAAK,EAAA,GAAA,EAAK,IAAS,KAAA;AAC5C,IAAA,MAAM,SAAS,GAAI,CAAA,MAAA;AACnB,IAAA,MAAM,WAAc,GAAA,GAAA,CAAI,IAAK,CAAA,UAAA,CAAW,eAAe,CAAA;AACvD,IAAM,MAAA,YAAA,GAAe,IAAI,MAAW,KAAA,KAAA;AAGpC,IAAI,IAAA,CAAC,WAAe,IAAA,CAAC,MAAQ,EAAA;AAC3B,MAAK,IAAA,EAAA;AACL,MAAA;AAAA;AAIF,IAAM,MAAA,OAAA,GAAU,UAAU,GAAI,CAAA,IAAA,CAAK,MAAM,uBAAuB,CAAA,CAAG,CAAC,CAAC,CAAA;AACrE,IAAA,MAAM,OAAU,GAAA,MAAA,CAAO,GAAI,CAAA,IAAA,CAAK,MAAM,CAAA;AACtC,IAAA,MAAM,SAAY,GAAA,MAAA,CAAO,KAAM,CAAA,IAAA,CAAK,MAAM,CAAA;AAC1C,IAAA,IAAI,YAAe,GAAA,IAAA;AACnB,IAAA,MAAM,SAAmB,EAAC;AAI1B,IAAA,MAAA,CAAO,KAAQ,GAAA,CACb,IACA,EAAA,QAAA,EACA,QACG,KAAA;AAGH,MAAO,MAAA,CAAA,IAAA;AAAA,QACL,OAAO,SAAS,QAAW,GAAA,MAAA,CAAO,KAAK,IAAI,CAAA,GAAI,MAAO,CAAA,IAAA,CAAK,IAAI;AAAA,OACjE;AACA,MAAI,IAAA,OAAO,aAAa,UAAY,EAAA;AAClC,QAAO,OAAA,SAAA,CAAU,MAAM,QAAQ,CAAA;AAAA;AAEjC,MAAO,OAAA,SAAA,CAAU,IAAM,EAAA,QAAA,EAAU,QAAQ,CAAA;AAAA,KAC3C;AAIA,IAAO,MAAA,CAAA,EAAA,CAAG,OAAS,EAAA,OAAM,QAAY,KAAA;AACnC,MAAM,MAAA,OAAA,GAAU,MAAO,CAAA,MAAA,CAAO,MAAM,CAAA;AACpC,MAAA,MAAM,IAAO,GAAA,OAAA,CAAQ,QAAS,CAAA,MAAA,EAAQ,GAAG,EAAE,CAAA;AAC3C,MAAA,IACE,gBACA,YACA,IAAA,CAAC,YACD,IAAK,CAAA,KAAA,CAAM,kBAAkB,CAC7B,EAAA;AACA,QAAM,MAAA,KAAA,CAAM,GAAI,CAAA,OAAA,EAAS,OAAO,CAAA;AAAA;AAClC,KACD,CAAA;AAGD,IAAA,MAAM,MAAS,GAAA,MAAM,KAAM,CAAA,GAAA,CAAI,OAAO,CAAA;AAKtC,IAAA,IAAI,MAAQ,EAAA;AACV,MAAe,YAAA,GAAA,KAAA;AACf,MAAA,OAAA,CAAQ,MAAM,CAAA;AACd,MAAA;AAAA;AAIF,IAAK,IAAA,EAAA;AAAA,GACN,CAAA;AAED,EAAO,OAAA,eAAA;AACT;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-techdocs-backend",
|
|
3
|
-
"version": "1.11.7-next.
|
|
3
|
+
"version": "1.11.7-next.2",
|
|
4
4
|
"description": "The Backstage backend plugin that renders technical documentation for your components",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"types": "./dist/index.d.ts",
|
|
51
51
|
"typesVersions": {
|
|
52
52
|
"*": {
|
|
53
|
-
"
|
|
53
|
+
"*": [
|
|
54
54
|
"dist/index.d.ts"
|
|
55
55
|
],
|
|
56
56
|
"alpha": [
|
|
@@ -73,18 +73,18 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@backstage/backend-common": "^0.25.0",
|
|
76
|
-
"@backstage/backend-plugin-api": "1.2.1-next.
|
|
76
|
+
"@backstage/backend-plugin-api": "1.2.1-next.1",
|
|
77
77
|
"@backstage/catalog-client": "1.9.1",
|
|
78
78
|
"@backstage/catalog-model": "1.7.3",
|
|
79
79
|
"@backstage/config": "1.3.2",
|
|
80
80
|
"@backstage/errors": "1.2.7",
|
|
81
|
-
"@backstage/integration": "1.16.
|
|
81
|
+
"@backstage/integration": "1.16.2-next.0",
|
|
82
82
|
"@backstage/plugin-catalog-common": "1.1.3",
|
|
83
|
-
"@backstage/plugin-catalog-node": "1.16.1-next.
|
|
83
|
+
"@backstage/plugin-catalog-node": "1.16.1-next.1",
|
|
84
84
|
"@backstage/plugin-permission-common": "0.8.4",
|
|
85
|
-
"@backstage/plugin-search-backend-module-techdocs": "0.3.7-next.
|
|
85
|
+
"@backstage/plugin-search-backend-module-techdocs": "0.3.7-next.2",
|
|
86
86
|
"@backstage/plugin-techdocs-common": "0.1.0",
|
|
87
|
-
"@backstage/plugin-techdocs-node": "1.13.1-next.
|
|
87
|
+
"@backstage/plugin-techdocs-node": "1.13.1-next.2",
|
|
88
88
|
"@types/express": "^4.17.6",
|
|
89
89
|
"express": "^4.17.1",
|
|
90
90
|
"express-promise-router": "^4.1.0",
|
|
@@ -95,9 +95,9 @@
|
|
|
95
95
|
"winston": "^3.2.1"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
|
-
"@backstage/backend-defaults": "0.8.2-next.
|
|
99
|
-
"@backstage/backend-test-utils": "1.3.1-next.
|
|
100
|
-
"@backstage/cli": "0.
|
|
98
|
+
"@backstage/backend-defaults": "0.8.2-next.2",
|
|
99
|
+
"@backstage/backend-test-utils": "1.3.1-next.2",
|
|
100
|
+
"@backstage/cli": "0.31.0-next.1",
|
|
101
101
|
"msw": "^2.0.0",
|
|
102
102
|
"supertest": "^7.0.0"
|
|
103
103
|
},
|