Merge pull request #6639 from backstage/techdocs/ref-follow-ups
[TechDocs] Remove deprecated preparer support and document ref values
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
---
|
||||
'@backstage/techdocs-common': minor
|
||||
---
|
||||
|
||||
TechDocs has dropped all support for the long-ago deprecated git-based common
|
||||
prepares as well as all corresponding values in `backstage.io/techdocs-ref`
|
||||
annotations.
|
||||
|
||||
Entities whose `backstage.io/techdocs-ref` annotation values still begin with
|
||||
`github:`, `gitlab:`, `bitbucket:`, or `azure/api:` will no longer be generated
|
||||
by TechDocs. Be sure to update these values so that they align with their
|
||||
expected format and your usage of TechDocs.
|
||||
|
||||
For details, see [this explainer on TechDocs ref annotation values][how].
|
||||
|
||||
[how]: https://backstage.io/docs/features/techdocs/how-to-guides#how-to-understand-techdocs-ref-annotation-values
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
The recommended value for a `backstage.io/techdocs-ref` annotation is now
|
||||
`dir:.`, indicating "documentation source files are located in the same
|
||||
directory relative to the catalog entity." Note that `url:<location>` values
|
||||
are still supported.
|
||||
+1
-1
@@ -15,7 +15,7 @@ metadata:
|
||||
url: https://discord.com/invite/EBHEGzX
|
||||
annotations:
|
||||
github.com/project-slug: backstage/backstage
|
||||
backstage.io/techdocs-ref: url:https://github.com/backstage/backstage
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
lighthouse.com/website-url: https://backstage.io
|
||||
spec:
|
||||
type: library
|
||||
|
||||
@@ -63,12 +63,18 @@ the `app-config.yaml` of a Backstage installation.
|
||||
# Example:
|
||||
metadata:
|
||||
annotations:
|
||||
backstage.io/techdocs-ref: url:https://github.com/backstage/backstage/tree/master
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
```
|
||||
|
||||
The value of this annotation is a location reference string (see above). If this
|
||||
annotation is specified, it is expected to point to a repository that the
|
||||
TechDocs system can read and generate docs from.
|
||||
The value of this annotation informs _where_ TechDocs source content is stored
|
||||
so that it can be read and docs can be generated from it. Most commonly, it's
|
||||
written as a path, relative to the location of the `catalog-info.yaml` itself,
|
||||
where the associated `mkdocs.yml` file can be found.
|
||||
|
||||
In unusual situations where the documentation for a catalog entity does not live
|
||||
alongside the entity's source code, the value of this annotation can point to an
|
||||
absolute URL, matching the location reference string format outlined above, for
|
||||
example: `url:https://github.com/backstage/backstage/tree/master`
|
||||
|
||||
### backstage.io/view-url, backstage.io/edit-url
|
||||
|
||||
|
||||
@@ -66,9 +66,7 @@ Update your component's entity description by adding the following lines to its
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
backstage.io/techdocs-ref: url:https://github.com/org/repo
|
||||
# Or
|
||||
# backstage.io/techdocs-ref: url:https://github.com/org/repo/tree/branchName/subFolder
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
```
|
||||
|
||||
The
|
||||
|
||||
@@ -35,38 +35,64 @@ In your Backstage instance's `app-config.yaml`, set `techdocs.builder` from
|
||||
`'local'` to `'external'`. By doing this, TechDocs will not try to generate
|
||||
docs. Look at [TechDocs configuration](configuration.md) for reference.
|
||||
|
||||
## How to use URL Reader in TechDocs Prepare step?
|
||||
## How to understand techdocs-ref annotation values
|
||||
|
||||
If TechDocs is configured to generate docs, it will first download the
|
||||
repository associated with the `backstage.io/techdocs-ref` annotation defined in
|
||||
the Entity's `catalog-info.yaml` file. This is also called the
|
||||
If TechDocs is configured to generate docs, it will first download source files
|
||||
based on the value of the `backstage.io/techdocs-ref` annotation defined in the
|
||||
Entity's `catalog-info.yaml` file. This is also called the
|
||||
[Prepare](./concepts.md#techdocs-preparer) step.
|
||||
|
||||
There are two kinds of preparers or two ways of downloading these source files
|
||||
We strongly recommend that the `backstage.io/techdocs-ref` annotation in each
|
||||
documented catalog entity's `catalog-info.yaml` be set to `dir:.` in almost all
|
||||
situations. This is because TechDocs is aligned with the "docs like code"
|
||||
philosophy, whereby documentation should be authored and managed alongside the
|
||||
source code of the underlying software itself.
|
||||
|
||||
- Preparer 1: Doing a `git clone` of the repository (also known as Common Git
|
||||
Preparer)
|
||||
- Preparer 2: Downloading an archive.zip or equivalent of the repository (also
|
||||
known as URL Reader)
|
||||
When you see `dir:.`, you can translate it to mean:
|
||||
|
||||
If `backstage.io/techdocs-ref` is equal to any of these -
|
||||
- That the documentation source code lives in the same location as the
|
||||
`catalog-info.yaml` file.
|
||||
- That, in particular, the `mkdocs.yml` file is a sibling of `catalog-info.yaml`
|
||||
(meaning, it is in the same directory)
|
||||
- And that all of the source content of the documentation would be available if
|
||||
one were to download the directory containing those two files (as well as all
|
||||
sub-directories).
|
||||
|
||||
1. `github:https://githubhost.com/org/repo`
|
||||
2. `gitlab:https://gitlabhost.com/org/repo`
|
||||
3. `bitbucket:https://bitbuckethost.com/project/repo`
|
||||
4. `azure/api:https://azurehost.com/org/project`
|
||||
The directory tree of the entity would look something like this:
|
||||
|
||||
Then Common Git Preparer will be used i.e. a `git clone`. But the URL Reader is
|
||||
a much faster way to do this step. Convert the `backstage.io/techdocs-ref`
|
||||
values to the following -
|
||||
```
|
||||
├── catalog-info.yaml
|
||||
├── mkdocs.yml
|
||||
└── docs
|
||||
└── index.md
|
||||
```
|
||||
|
||||
1. `url:https://githubhost.com/org/repo/tree/<branch_name>`
|
||||
2. `url:https://gitlabhost.com/org/repo/tree/<branch_name>`
|
||||
3. `url:https://bitbuckethost.com/project/repo/src/<branch_name>`
|
||||
4. `url:https://azurehost.com/organization/project/_git/repository`
|
||||
If, for example, you wanted to keep a lean root directory, you could place your
|
||||
`mkdocs.yml` file in a subdirectory and update the `backstage.io/techdocs-ref`
|
||||
annotation value accordingly, e.g. to `dir:./sub-folder`:
|
||||
|
||||
Note that you can also provide a path to a non-root directory inside the
|
||||
repository which contains the `docs/` directory.
|
||||
```
|
||||
├── catalog-info.yaml
|
||||
└── sub-folder
|
||||
├── mkdocs.yml
|
||||
└── docs
|
||||
└── index.md
|
||||
```
|
||||
|
||||
In rare situations where your TechDocs source content is managed and stored in a
|
||||
location completely separate from your `catalog-info.yaml`, you can instead
|
||||
specify a URL location reference, the exact value of which will vary based on
|
||||
the source code hosting provider. Notice that instead of the `dir:` prefix, the
|
||||
`url:` prefix is used instead. For example:
|
||||
|
||||
- **GitHub**: `url:https://githubhost.com/org/repo/tree/<branch_name>`
|
||||
- **GitLab**: `url:https://gitlabhost.com/org/repo/tree/<branch_name>`
|
||||
- **Bitbucket**: `url:https://bitbuckethost.com/project/repo/src/<branch_name>`
|
||||
- **Azure**: `url:https://azurehost.com/organization/project/_git/repository`
|
||||
|
||||
Note, just as it's possible to specify a subdirectory with the `dir:` prefix,
|
||||
you can also provide a path to a non-root directory inside the repository which
|
||||
contains the `mkdocs.yml` file and `docs/` directory.
|
||||
|
||||
e.g.
|
||||
`url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component`
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
# Example for optional annotations
|
||||
# annotations:
|
||||
# github.com/project-slug: backstage/backstage
|
||||
# backstage.io/techdocs-ref: url:https://github.com/backstage/backstage
|
||||
# backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: website
|
||||
owner: john@example.com
|
||||
|
||||
@@ -5,50 +5,24 @@
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { AzureIntegrationConfig } from '@backstage/integration';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ContainerRunner } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityName } from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import { GitHubIntegrationConfig } from '@backstage/integration';
|
||||
import { GitLabIntegrationConfig } from '@backstage/integration';
|
||||
import { Logger as Logger_2 } from 'winston';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Writable } from 'stream';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "checkoutGitRepository" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const checkoutGitRepository: (
|
||||
repoUrl: string,
|
||||
config: Config,
|
||||
logger: Logger_2,
|
||||
) => Promise<string>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CommonGitPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class CommonGitPreparer implements PreparerBase {
|
||||
constructor(config: Config, logger: Logger_2);
|
||||
// Warning: (ae-forgotten-export) The symbol "PreparerResponse" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
prepare(
|
||||
entity: Entity,
|
||||
options?: {
|
||||
etag?: string;
|
||||
},
|
||||
): Promise<PreparerResponse>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "DirectoryPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class DirectoryPreparer implements PreparerBase {
|
||||
constructor(config: Config, _logger: Logger_2, reader: UrlReader);
|
||||
// Warning: (ae-forgotten-export) The symbol "PreparerResponse" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
prepare(
|
||||
entity: Entity,
|
||||
@@ -121,22 +95,6 @@ export class Generators implements GeneratorBuilder {
|
||||
register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase): void;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getAzureIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getAzureIntegrationConfig: (
|
||||
config: Config,
|
||||
host: string,
|
||||
) => AzureIntegrationConfig;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getDefaultBranch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getDefaultBranch: (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
) => Promise<string>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getDocFilesFromRepository" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -151,48 +109,6 @@ export const getDocFilesFromRepository: (
|
||||
| undefined,
|
||||
) => Promise<PreparerResponse>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getGitHost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function getGitHost(url: string): string;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getGitHubIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getGitHubIntegrationConfig: (
|
||||
config: Config,
|
||||
host: string,
|
||||
) => GitHubIntegrationConfig;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getGitLabIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getGitLabIntegrationConfig: (
|
||||
config: Config,
|
||||
host: string,
|
||||
) => GitLabIntegrationConfig;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getGitRepositoryTempFolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getGitRepositoryTempFolder: (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
) => Promise<string>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getGitRepoType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function getGitRepoType(url: string): string;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getLastCommitTimestamp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getLastCommitTimestamp: (
|
||||
repositoryLocation: string,
|
||||
logger: Logger_2,
|
||||
) => Promise<number>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getLocationForEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -201,14 +117,6 @@ export const getLocationForEntity: (
|
||||
scmIntegration: ScmIntegrationRegistry,
|
||||
) => ParsedLocationAnnotation;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "getTokenForGitRepo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const getTokenForGitRepo: (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
) => Promise<string | undefined>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ParsedLocationAnnotation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -311,14 +219,8 @@ export type PublisherType =
|
||||
|
||||
// Warning: (ae-missing-release-tag) "RemoteProtocol" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export type RemoteProtocol =
|
||||
| 'url'
|
||||
| 'dir'
|
||||
| 'github'
|
||||
| 'gitlab'
|
||||
| 'file'
|
||||
| 'azure/api';
|
||||
// @public (undocumented)
|
||||
export type RemoteProtocol = 'url' | 'dir';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "TechdocsGenerator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -46,10 +46,8 @@
|
||||
"@google-cloud/storage": "^5.6.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"aws-sdk": "^2.840.0",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "9.1.0",
|
||||
"git-url-parse": "~11.4.4",
|
||||
"js-yaml": "^4.0.0",
|
||||
"json5": "^2.1.3",
|
||||
"mime-types": "^2.1.27",
|
||||
@@ -62,7 +60,6 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.6",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/js-yaml": "^4.0.0",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"@types/mock-fs": "^4.13.0",
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* 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 fetch from 'cross-fetch';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
getGitHubRequestOptions,
|
||||
getGitLabRequestOptions,
|
||||
getAzureRequestOptions,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
getGitHost,
|
||||
getGitRepoType,
|
||||
getGitHubIntegrationConfig,
|
||||
getGitLabIntegrationConfig,
|
||||
getAzureIntegrationConfig,
|
||||
} from './git-auth';
|
||||
|
||||
interface IGitlabBranch {
|
||||
name: string;
|
||||
merged: boolean;
|
||||
protected: boolean;
|
||||
default: boolean;
|
||||
developers_can_push: boolean;
|
||||
developers_can_merge: boolean;
|
||||
can_push: boolean;
|
||||
web_url: string;
|
||||
commit: {
|
||||
author_email: string;
|
||||
author_name: string;
|
||||
authored_date: string;
|
||||
committed_date: string;
|
||||
committer_email: string;
|
||||
committer_name: string;
|
||||
id: string;
|
||||
short_id: string;
|
||||
title: string;
|
||||
message: string;
|
||||
parent_ids: string[];
|
||||
};
|
||||
}
|
||||
|
||||
function getGithubApiUrl(config: Config, url: string): URL {
|
||||
const { resource, owner, name } = parseGitUrl(url);
|
||||
const providerConfigs =
|
||||
config.getOptionalConfigArray('integrations.github') ?? [];
|
||||
|
||||
const hostConfig = providerConfigs.filter(
|
||||
providerConfig => providerConfig.getOptionalString('host') === resource,
|
||||
);
|
||||
|
||||
const apiBaseUrl =
|
||||
hostConfig[0]?.getOptionalString('apiBaseUrl') ?? 'https://api.github.com';
|
||||
const apiRepos = 'repos';
|
||||
|
||||
return new URL(`${apiBaseUrl}/${apiRepos}/${owner}/${name}`);
|
||||
}
|
||||
|
||||
function getGitlabApiUrl(url: string): URL {
|
||||
const { protocol, resource, full_name: fullName } = parseGitUrl(url);
|
||||
const apiProjectsBasePath = 'api/v4/projects';
|
||||
const project = encodeURIComponent(fullName);
|
||||
const branches = 'repository/branches';
|
||||
|
||||
return new URL(
|
||||
`${protocol}://${resource}/${apiProjectsBasePath}/${project}/${branches}`,
|
||||
);
|
||||
}
|
||||
|
||||
function getAzureApiUrl(url: string): URL {
|
||||
const { protocol, resource, organization, owner, name } = parseGitUrl(url);
|
||||
const apiRepoPath = '_apis/git/repositories';
|
||||
const apiVersion = 'api-version=6.0';
|
||||
|
||||
return new URL(
|
||||
`${protocol}://${resource}/${organization}/${owner}/${apiRepoPath}/${name}?${apiVersion}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function getGithubDefaultBranch(
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string> {
|
||||
const path = getGithubApiUrl(config, repositoryUrl).toString();
|
||||
const host = getGitHost(repositoryUrl);
|
||||
|
||||
const integrationConfig = getGitHubIntegrationConfig(config, host);
|
||||
const options = getGitHubRequestOptions(integrationConfig);
|
||||
|
||||
try {
|
||||
const raw = await fetch(path, options);
|
||||
|
||||
if (!raw.ok) {
|
||||
throw new Error(
|
||||
`Failed to load url: ${raw.status} ${raw.statusText}. Make sure you have permission to repository: ${repositoryUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const { default_branch: branch } = await raw.json();
|
||||
|
||||
if (!branch) {
|
||||
throw new Error('Not found github default branch');
|
||||
}
|
||||
|
||||
return branch;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get github default branch: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getGitlabDefaultBranch(
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string> {
|
||||
const path = getGitlabApiUrl(repositoryUrl).toString();
|
||||
const host = getGitHost(repositoryUrl);
|
||||
|
||||
const integrationConfig = getGitLabIntegrationConfig(config, host);
|
||||
const options = getGitLabRequestOptions(integrationConfig);
|
||||
|
||||
try {
|
||||
const raw = await fetch(path, options);
|
||||
|
||||
if (!raw.ok) {
|
||||
throw new Error(
|
||||
`Failed to load url: ${raw.status} ${raw.statusText}. Make sure you have permission to repository: ${repositoryUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await raw.json();
|
||||
const { name } = (result || []).find(
|
||||
(branch: IGitlabBranch) => branch.default === true,
|
||||
);
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Not found gitlab default branch');
|
||||
}
|
||||
|
||||
return name;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get gitlab default branch: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAzureDefaultBranch(
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string> {
|
||||
const path = getAzureApiUrl(repositoryUrl).toString();
|
||||
const host = getGitHost(repositoryUrl);
|
||||
|
||||
const integrationConfig = getAzureIntegrationConfig(config, host);
|
||||
const options = getAzureRequestOptions(integrationConfig);
|
||||
|
||||
try {
|
||||
const urlResponse = await fetch(path, options);
|
||||
if (!urlResponse.ok) {
|
||||
throw new Error(
|
||||
`Failed to load url: ${urlResponse.status} ${urlResponse.statusText}. Make sure you have permission to repository: ${repositoryUrl}`,
|
||||
);
|
||||
}
|
||||
const urlResult = await urlResponse.json();
|
||||
|
||||
const idResponse = await fetch(urlResult.url, options);
|
||||
if (!idResponse.ok) {
|
||||
throw new Error(
|
||||
`Failed to load url: ${idResponse.status} ${idResponse.statusText}. Make sure you have permission to repository: ${urlResult.repository.url}`,
|
||||
);
|
||||
}
|
||||
const idResult = await idResponse.json();
|
||||
const name = idResult.defaultBranch;
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Not found Azure DevOps default branch');
|
||||
}
|
||||
|
||||
return name;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get Azure DevOps default branch: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const getDefaultBranch = async (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string> => {
|
||||
const type = getGitRepoType(repositoryUrl);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'github':
|
||||
return await getGithubDefaultBranch(repositoryUrl, config);
|
||||
case 'gitlab':
|
||||
return await getGitlabDefaultBranch(repositoryUrl, config);
|
||||
case 'azure/api':
|
||||
return await getAzureDefaultBranch(repositoryUrl, config);
|
||||
|
||||
default:
|
||||
throw new Error('Failed to get repository type');
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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 parseGitUrl from 'git-url-parse';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
readGitHubIntegrationConfigs,
|
||||
readGitLabIntegrationConfigs,
|
||||
readAzureIntegrationConfigs,
|
||||
GitHubIntegrationConfig,
|
||||
GitLabIntegrationConfig,
|
||||
AzureIntegrationConfig,
|
||||
} from '@backstage/integration';
|
||||
|
||||
export function getGitHost(url: string): string {
|
||||
const { resource } = parseGitUrl(url);
|
||||
return resource;
|
||||
}
|
||||
|
||||
export function getGitRepoType(url: string): string {
|
||||
const typeMapping = [
|
||||
{ url: /github*/g, type: 'github' },
|
||||
{ url: /gitlab*/g, type: 'gitlab' },
|
||||
{ url: /azure*/g, type: 'azure/api' },
|
||||
];
|
||||
|
||||
const type = typeMapping.filter(item => item.url.test(url))[0]?.type;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
export const getGitHubIntegrationConfig = (
|
||||
config: Config,
|
||||
host: string,
|
||||
): GitHubIntegrationConfig => {
|
||||
const allGitHubConfigs = readGitHubIntegrationConfigs(
|
||||
config.getOptionalConfigArray('integrations.github') ?? [],
|
||||
);
|
||||
const gitHubIntegrationConfig = allGitHubConfigs.find(v => v.host === host);
|
||||
if (!gitHubIntegrationConfig) {
|
||||
throw new Error(`Unable to locate GitHub integration for the host ${host}`);
|
||||
}
|
||||
return gitHubIntegrationConfig;
|
||||
};
|
||||
|
||||
export const getGitLabIntegrationConfig = (
|
||||
config: Config,
|
||||
host: string,
|
||||
): GitLabIntegrationConfig => {
|
||||
const allGitLabConfigs = readGitLabIntegrationConfigs(
|
||||
config.getOptionalConfigArray('integrations.gitlab') ?? [],
|
||||
);
|
||||
const gitLabIntegrationConfig = allGitLabConfigs.find(v => v.host === host);
|
||||
if (!gitLabIntegrationConfig) {
|
||||
throw new Error(`Unable to locate GitLab integration for the host ${host}`);
|
||||
}
|
||||
return gitLabIntegrationConfig;
|
||||
};
|
||||
|
||||
export const getAzureIntegrationConfig = (
|
||||
config: Config,
|
||||
host: string,
|
||||
): AzureIntegrationConfig => {
|
||||
const allAzureIntegrationConfig = readAzureIntegrationConfigs(
|
||||
config.getOptionalConfigArray('integrations.azure') ?? [],
|
||||
);
|
||||
const azureIntegrationConfig = allAzureIntegrationConfig.find(
|
||||
v => v.host === host,
|
||||
);
|
||||
if (!azureIntegrationConfig) {
|
||||
throw new Error(`Unable to locate Azure integration for the host ${host}`);
|
||||
}
|
||||
return azureIntegrationConfig;
|
||||
};
|
||||
|
||||
export const getTokenForGitRepo = async (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string | undefined> => {
|
||||
const host = getGitHost(repositoryUrl);
|
||||
const type = getGitRepoType(repositoryUrl);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'github':
|
||||
return getGitHubIntegrationConfig(config, host).token;
|
||||
case 'gitlab':
|
||||
return getGitLabIntegrationConfig(config, host).token;
|
||||
case 'azure/api':
|
||||
return getAzureIntegrationConfig(config, host).token;
|
||||
default:
|
||||
throw new Error('Failed to get repository type');
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -14,26 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Git,
|
||||
resolveSafeChildPath,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common';
|
||||
import {
|
||||
Entity,
|
||||
getEntitySourceLocation,
|
||||
parseLocationReference,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import fs from 'fs-extra';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { Logger } from 'winston';
|
||||
import { getDefaultBranch } from './default-branch';
|
||||
import { getGitRepoType, getTokenForGitRepo } from './git-auth';
|
||||
import { PreparerResponse, RemoteProtocol } from './stages/prepare/types';
|
||||
|
||||
export type ParsedLocationAnnotation = {
|
||||
@@ -121,9 +111,6 @@ export const getLocationForEntity = (
|
||||
);
|
||||
|
||||
switch (annotation.type) {
|
||||
case 'github':
|
||||
case 'gitlab':
|
||||
case 'azure/api':
|
||||
case 'url':
|
||||
return annotation;
|
||||
case 'dir':
|
||||
@@ -133,124 +120,6 @@ export const getLocationForEntity = (
|
||||
}
|
||||
};
|
||||
|
||||
export const getGitRepositoryTempFolder = async (
|
||||
repositoryUrl: string,
|
||||
config: Config,
|
||||
): Promise<string> => {
|
||||
const parsedGitLocation = parseGitUrl(repositoryUrl);
|
||||
// removes .git from git location path
|
||||
parsedGitLocation.git_suffix = false;
|
||||
|
||||
if (!parsedGitLocation.ref) {
|
||||
parsedGitLocation.ref = await getDefaultBranch(
|
||||
parsedGitLocation.toString('https'),
|
||||
config,
|
||||
);
|
||||
}
|
||||
|
||||
return path.join(
|
||||
// fs.realpathSync fixes a problem with macOS returning a path that is a symlink
|
||||
fs.realpathSync(os.tmpdir()),
|
||||
'backstage-repo',
|
||||
parsedGitLocation.resource,
|
||||
parsedGitLocation.owner,
|
||||
parsedGitLocation.name,
|
||||
parsedGitLocation.ref,
|
||||
);
|
||||
};
|
||||
|
||||
export const checkoutGitRepository = async (
|
||||
repoUrl: string,
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
): Promise<string> => {
|
||||
const parsedGitLocation = parseGitUrl(repoUrl);
|
||||
const repositoryTmpPath = await getGitRepositoryTempFolder(repoUrl, config);
|
||||
const token = await getTokenForGitRepo(repoUrl, config);
|
||||
|
||||
// Initialize a git client
|
||||
let git = Git.fromAuth({ logger });
|
||||
|
||||
// Docs about why username and password are set to these specific values.
|
||||
// https://isomorphic-git.org/docs/en/onAuth#oauth2-tokens
|
||||
if (token) {
|
||||
const type = getGitRepoType(repoUrl);
|
||||
switch (type) {
|
||||
case 'github':
|
||||
git = Git.fromAuth({
|
||||
username: 'x-access-token',
|
||||
password: token,
|
||||
logger,
|
||||
});
|
||||
parsedGitLocation.token = `x-access-token:${token}`;
|
||||
break;
|
||||
case 'gitlab':
|
||||
git = Git.fromAuth({
|
||||
username: 'oauth2',
|
||||
password: token,
|
||||
logger,
|
||||
});
|
||||
parsedGitLocation.token = `dummyUsername:${token}`;
|
||||
parsedGitLocation.git_suffix = true;
|
||||
break;
|
||||
case 'azure/api':
|
||||
git = Git.fromAuth({
|
||||
username: 'notempty',
|
||||
password: token,
|
||||
logger: logger,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
parsedGitLocation.token = `:${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Pull from repository if it has already been cloned.
|
||||
if (fs.existsSync(repositoryTmpPath)) {
|
||||
try {
|
||||
const currentBranchName = await git.currentBranch({
|
||||
dir: repositoryTmpPath,
|
||||
});
|
||||
|
||||
await git.fetch({ dir: repositoryTmpPath, remote: 'origin' });
|
||||
await git.merge({
|
||||
dir: repositoryTmpPath,
|
||||
theirs: `origin/${currentBranchName}`,
|
||||
ours: currentBranchName || undefined,
|
||||
author: { name: 'Backstage TechDocs', email: 'techdocs@backstage.io' },
|
||||
committer: {
|
||||
name: 'Backstage TechDocs',
|
||||
email: 'techdocs@backstage.io',
|
||||
},
|
||||
});
|
||||
return repositoryTmpPath;
|
||||
} catch (e) {
|
||||
logger.info(
|
||||
`Found error "${e.message}" in cached repository "${repoUrl}" when getting latest changes. Removing cached repository.`,
|
||||
);
|
||||
fs.removeSync(repositoryTmpPath);
|
||||
}
|
||||
}
|
||||
|
||||
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
|
||||
|
||||
fs.mkdirSync(repositoryTmpPath, { recursive: true });
|
||||
await git.clone({ url: repositoryCheckoutUrl, dir: repositoryTmpPath });
|
||||
|
||||
return repositoryTmpPath;
|
||||
};
|
||||
|
||||
export const getLastCommitTimestamp = async (
|
||||
repositoryLocation: string,
|
||||
logger: Logger,
|
||||
): Promise<number> => {
|
||||
const git = Git.fromAuth({ logger });
|
||||
const sha = await git.resolveRef({ dir: repositoryLocation, ref: 'HEAD' });
|
||||
const commit = await git.readCommit({ dir: repositoryLocation, sha });
|
||||
|
||||
return commit.commit.committer.timestamp;
|
||||
};
|
||||
|
||||
export const getDocFilesFromRepository = async (
|
||||
reader: UrlReader,
|
||||
entity: Entity,
|
||||
|
||||
@@ -15,5 +15,3 @@
|
||||
*/
|
||||
export * from './stages';
|
||||
export * from './helpers';
|
||||
export * from './default-branch';
|
||||
export * from './git-auth';
|
||||
|
||||
@@ -124,7 +124,7 @@ describe('helpers', () => {
|
||||
};
|
||||
|
||||
const parsedLocationAnnotation2: ParsedLocationAnnotation = {
|
||||
type: 'file',
|
||||
type: 'file' as RemoteProtocol,
|
||||
target: '/home/user/workspace/docs-repository/catalog-info.yaml',
|
||||
};
|
||||
|
||||
@@ -147,7 +147,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should return correct target url for supported hosts', () => {
|
||||
const parsedLocationAnnotation1: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/backstage/backstage.git',
|
||||
};
|
||||
|
||||
@@ -156,7 +156,7 @@ describe('helpers', () => {
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation2: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/org/repo',
|
||||
};
|
||||
|
||||
@@ -165,7 +165,7 @@ describe('helpers', () => {
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation3: ParsedLocationAnnotation = {
|
||||
type: 'gitlab',
|
||||
type: 'gitlab' as RemoteProtocol,
|
||||
target: 'https://gitlab.com/org/repo',
|
||||
};
|
||||
|
||||
@@ -174,7 +174,7 @@ describe('helpers', () => {
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation4: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target:
|
||||
'github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component',
|
||||
};
|
||||
@@ -200,7 +200,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should add repo_url to mkdocs.yml', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/backstage/backstage',
|
||||
};
|
||||
|
||||
@@ -219,7 +219,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should add repo_url to mkdocs.yml that contains custom yaml tags', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/backstage/backstage',
|
||||
};
|
||||
|
||||
@@ -241,7 +241,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should not override existing repo_url in mkdocs.yml', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github',
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/neworg/newrepo',
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import path, { resolve as resolvePath } from 'path';
|
||||
import { PassThrough, Writable } from 'stream';
|
||||
import { Logger } from 'winston';
|
||||
import { ParsedLocationAnnotation } from '../../helpers';
|
||||
import { RemoteProtocol } from '../prepare/types';
|
||||
import { SupportedGeneratorKey } from './types';
|
||||
|
||||
// TODO: Implement proper support for more generators.
|
||||
@@ -94,12 +93,12 @@ export const runCommand = async ({
|
||||
* - (anything that is not valid as described above)
|
||||
*
|
||||
* @param {string} repoUrl URL supposed to be used as repo_url in mkdocs.yml
|
||||
* @param {RemoteProtocol} locationType Type of source code host - github, gitlab, dir, url, etc.
|
||||
* @param {string} locationType Type of source code host - github, gitlab, dir, url, etc.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isValidRepoUrlForMkdocs = (
|
||||
repoUrl: string,
|
||||
locationType: RemoteProtocol,
|
||||
locationType: string,
|
||||
): boolean => {
|
||||
// Trim trailing slash
|
||||
const cleanRepoUrl = repoUrl.replace(/\/$/, '');
|
||||
@@ -266,6 +265,8 @@ export const patchMkdocsYmlPreBuild = async (
|
||||
const repoUrl = getRepoUrlFromLocationAnnotation(parsedLocationAnnotation);
|
||||
if (repoUrl !== undefined) {
|
||||
// mkdocs.yml will not build with invalid repo_url. So, make sure it is valid.
|
||||
// TODO: this is no longer working/meaningful because annotation type is
|
||||
// now only ever "url" or "dir." Should be re-implemented!
|
||||
if (isValidRepoUrlForMkdocs(repoUrl, parsedLocationAnnotation.type)) {
|
||||
mkdocsYml.repo_url = repoUrl;
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { checkoutGitRepository } from '../../helpers';
|
||||
import { CommonGitPreparer } from './commonGit';
|
||||
|
||||
function normalizePath(path: string) {
|
||||
return path
|
||||
.replace(/^[a-z]:/i, '')
|
||||
.split('\\')
|
||||
.join('/');
|
||||
}
|
||||
|
||||
jest.mock('../../helpers', () => ({
|
||||
...jest.requireActual<{}>('../../helpers'),
|
||||
checkoutGitRepository: jest.fn(() => '/tmp/backstage-repo/org/name/branch'),
|
||||
getLastCommitTimestamp: jest.fn(() => 12345678),
|
||||
}));
|
||||
|
||||
const createMockEntity = (annotations = {}) => {
|
||||
return {
|
||||
apiVersion: 'version',
|
||||
kind: 'TestKind',
|
||||
metadata: {
|
||||
name: 'test-component-name',
|
||||
annotations: {
|
||||
...annotations,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const mockConfig = new ConfigReader({});
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
describe('commonGit preparer', () => {
|
||||
it('should prepare temp docs path from github repo', async () => {
|
||||
const preparer = new CommonGitPreparer(mockConfig, logger);
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/techdocs-ref':
|
||||
'github:https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component',
|
||||
});
|
||||
|
||||
const { preparedDir } = await preparer.prepare(mockEntity);
|
||||
expect(checkoutGitRepository).toHaveBeenCalledTimes(1);
|
||||
expect(normalizePath(preparedDir)).toEqual(
|
||||
'/tmp/backstage-repo/org/name/branch/plugins/techdocs-backend/examples/documented-component',
|
||||
);
|
||||
});
|
||||
|
||||
it('should prepare temp docs path from gitlab repo', async () => {
|
||||
const preparer = new CommonGitPreparer(mockConfig, logger);
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/techdocs-ref':
|
||||
'gitlab:https://gitlab.com/xesjkeee/go-logger/blob/master/catalog-info.yaml',
|
||||
});
|
||||
|
||||
const { preparedDir } = await preparer.prepare(mockEntity);
|
||||
expect(checkoutGitRepository).toHaveBeenCalledTimes(2);
|
||||
expect(normalizePath(preparedDir)).toEqual(
|
||||
'/tmp/backstage-repo/org/name/branch/catalog-info.yaml',
|
||||
);
|
||||
});
|
||||
|
||||
it('should prepare temp docs path from azure repo', async () => {
|
||||
const preparer = new CommonGitPreparer(mockConfig, logger);
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/techdocs-ref':
|
||||
'azure/api:https://dev.azure.com/backstage-org/backstage-project/_git/template-repo?path=%2Ftemplate.yaml',
|
||||
});
|
||||
|
||||
const { preparedDir } = await preparer.prepare(mockEntity);
|
||||
expect(checkoutGitRepository).toHaveBeenCalledTimes(3);
|
||||
expect(normalizePath(preparedDir)).toEqual(
|
||||
'/tmp/backstage-repo/org/name/branch/template.yaml',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* 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 { NotModifiedError } from '@backstage/errors';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import path from 'path';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
checkoutGitRepository,
|
||||
getLastCommitTimestamp,
|
||||
parseReferenceAnnotation,
|
||||
} from '../../helpers';
|
||||
import { PreparerBase, PreparerResponse } from './types';
|
||||
|
||||
export class CommonGitPreparer implements PreparerBase {
|
||||
private readonly config: Config;
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(config: Config, logger: Logger) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async prepare(
|
||||
entity: Entity,
|
||||
options?: { etag?: string },
|
||||
): Promise<PreparerResponse> {
|
||||
this.logger.warn(
|
||||
`You are using the legacy git preparer in TechDocs for \`${entity.metadata.name}\` which will be removed in near future (March 2021). ` +
|
||||
`Migrate to URL reader by updating \`backstage.io/techdocs-ref\` annotation in \`catalog-info.yaml\` ` +
|
||||
`to be prefixed with \`url:\`. Read the migration guide and benefits at https://github.com/backstage/backstage/issues/4409 `,
|
||||
);
|
||||
|
||||
const { target } = parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
entity,
|
||||
);
|
||||
|
||||
try {
|
||||
// Update repository or do a fresh clone.
|
||||
const repoPath = await checkoutGitRepository(
|
||||
target,
|
||||
this.config,
|
||||
this.logger,
|
||||
);
|
||||
// Check if etag has changed for cache invalidation.
|
||||
const etag = await getLastCommitTimestamp(repoPath, this.logger);
|
||||
if (options?.etag === etag.toString()) {
|
||||
throw new NotModifiedError();
|
||||
}
|
||||
|
||||
const parsedGitLocation = parseGitUrl(target);
|
||||
return {
|
||||
preparedDir: path.join(repoPath, parsedGitLocation.filepath),
|
||||
etag: etag.toString(),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof NotModifiedError) {
|
||||
this.logger.debug(`Cache is valid for etag ${options?.etag}`);
|
||||
} else {
|
||||
this.logger.debug(`Repo checkout failed with error ${error.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { DirectoryPreparer } from './dir';
|
||||
export { CommonGitPreparer } from './commonGit';
|
||||
export { UrlPreparer } from './url';
|
||||
export { Preparers } from './preparers';
|
||||
export type { PreparerBuilder, PreparerBase, RemoteProtocol } from './types';
|
||||
|
||||
@@ -19,7 +19,6 @@ import { Config } from '@backstage/config';
|
||||
import { Logger } from 'winston';
|
||||
import { parseReferenceAnnotation } from '../../helpers';
|
||||
import { DirectoryPreparer } from './dir';
|
||||
import { CommonGitPreparer } from './commonGit';
|
||||
import { UrlPreparer } from './url';
|
||||
import { PreparerBase, PreparerBuilder, RemoteProtocol } from './types';
|
||||
|
||||
@@ -47,12 +46,6 @@ export class Preparers implements PreparerBuilder {
|
||||
const directoryPreparer = new DirectoryPreparer(config, logger, reader);
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
// Common git preparers will be deprecated soon.
|
||||
const commonGitPreparer = new CommonGitPreparer(config, logger);
|
||||
preparers.register('github', commonGitPreparer);
|
||||
preparers.register('gitlab', commonGitPreparer);
|
||||
preparers.register('azure/api', commonGitPreparer);
|
||||
|
||||
return preparers;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,14 +48,4 @@ export type PreparerBuilder = {
|
||||
get(entity: Entity): PreparerBase;
|
||||
};
|
||||
|
||||
/**
|
||||
* Everything except `url` will be deprecated.
|
||||
* Read more https://github.com/backstage/backstage/issues/4409
|
||||
*/
|
||||
export type RemoteProtocol =
|
||||
| 'url'
|
||||
| 'dir'
|
||||
| 'github'
|
||||
| 'gitlab'
|
||||
| 'file'
|
||||
| 'azure/api';
|
||||
export type RemoteProtocol = 'url' | 'dir';
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ metadata:
|
||||
description: {{cookiecutter.description | jsonify}}
|
||||
annotations:
|
||||
github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: url:{{"https://" + cookiecutter.destination.host + "/" + cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: experimental
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
site_name: {{cookiecutter.component_id | jsonify}}
|
||||
site_description: {{cookiecutter.description | jsonify}}
|
||||
|
||||
nav:
|
||||
- Introduction: index.md
|
||||
|
||||
plugins:
|
||||
- techdocs-core
|
||||
+1
-1
@@ -5,7 +5,7 @@ metadata:
|
||||
description: {{cookiecutter.description | jsonify}}
|
||||
annotations:
|
||||
github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: url:{{"https://" + cookiecutter.destination.host + "/" + cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: documentation
|
||||
lifecycle: experimental
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ metadata:
|
||||
description: {{cookiecutter.description | jsonify}}
|
||||
annotations:
|
||||
github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: url:{{"https://" + cookiecutter.destination.host + "/" + cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: experimental
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ metadata:
|
||||
description: {{cookiecutter.description | jsonify}}
|
||||
annotations:
|
||||
github.com/project-slug: {{cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: url:{{"https://" + cookiecutter.destination.host + "/" + cookiecutter.destination.owner + "/" + cookiecutter.destination.repo}}
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
|
||||
@@ -4,7 +4,7 @@ metadata:
|
||||
name: documented-component
|
||||
description: A Service with TechDocs documentation
|
||||
annotations:
|
||||
backstage.io/techdocs-ref: 'url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component'
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
|
||||
Reference in New Issue
Block a user