Publish to provider based on url

This commit is contained in:
Johan Haals
2021-01-18 11:37:50 +01:00
parent 7b038dd16c
commit 931f0c6b89
5 changed files with 62 additions and 73 deletions
@@ -120,6 +120,7 @@ export class JobProcessor implements Processor {
// Log to the current stage the error that occurred and fail the stage.
stage.status = 'FAILED';
logger.error(`Stage failed with error: ${error.message}`);
logger.error(error.stack);
// Throw the error so the job can be failed too.
throw error;
@@ -15,11 +15,10 @@
*/
import { PublisherBase, PublisherOptions, PublisherResult } from './types';
import { Gitlab } from '@gitbeaker/core';
import { Config, JsonValue } from '@backstage/config';
import { Gitlab } from '@gitbeaker/node';
import { Config } from '@backstage/config';
import { Logger } from 'winston';
import { initRepoAndPush } from './helpers';
import { RequiredTemplateValues } from '../templater';
import gitUrlParse from 'git-url-parse';
import {
@@ -67,15 +66,31 @@ export class GitlabPublisher implements PublisherBase {
async publish({
values,
directory,
logger,
}: PublisherOptions): Promise<PublisherResult> {
const remoteUrl = await this.createRemote(values);
const { host } = new URL(remoteUrl);
const token = this.getToken(host);
const { resource: host, owner, name } = gitUrlParse(values.storePath);
const token = this.getToken(host);
if (!token) {
throw new Error('No token provided to create the remote repository');
throw new Error(
'No authentication set for Gitlab publisher. Creating the remote repository is not possible without a token',
);
}
const baseUrl = this.getBaseUrl(host);
if (!baseUrl) {
throw new Error(
'No host set for Gitlab publisher. Creating the remote repository is not possible without a host',
);
}
const remoteUrl = await this.createRemote({
host: baseUrl,
owner,
name,
token,
});
await initRepoAndPush({
dir: directory,
remoteUrl,
@@ -83,10 +98,14 @@ export class GitlabPublisher implements PublisherBase {
username: 'oauth2',
password: token,
},
logger: this.logger,
logger,
});
return { remoteUrl };
const catalogInfoUrl = remoteUrl.replace(
/\.git$/,
'/-/blob/master/catalog-info.yaml',
);
return { remoteUrl, catalogInfoUrl };
}
private getToken(host: string): string | undefined {
@@ -103,33 +122,15 @@ export class GitlabPublisher implements PublisherBase {
);
}
private getConfig(host: string): { baseUrl?: string; token?: string } {
return {
baseUrl: this.getBaseUrl(host),
token: this.getToken(host),
};
}
private async createRemote(
values: RequiredTemplateValues & Record<string, JsonValue>,
) {
const pathElements = values.storePath.split('/');
const name = pathElements[pathElements.length - 1];
pathElements.pop();
const owner = pathElements.join('/');
const { resource: host } = gitUrlParse(values.storePath);
const config = this.getConfig(host);
if (!config.token) {
throw new Error(
'No authentication set for Gitlab publisher. Creating the remote repository is not possible without a token',
);
}
const client = new Gitlab({ host: config.baseUrl, token: config.token });
private async createRemote(opts: {
host: string;
name: string;
owner: string;
token: string;
}) {
const { owner, name, host, token } = opts;
const client = new Gitlab({ host: host, token: token });
let targetNamespace = ((await client.Namespaces.show(owner)) as {
id: number;
}).id;
@@ -15,13 +15,10 @@
*/
import { Logger } from 'winston';
import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api';
import { Config } from '@backstage/config';
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
import {
DeprecatedLocationTypeDetector,
makeDeprecatedLocationTypeDetector,
parseLocationAnnotation,
} from '../helpers';
import { PublisherBase, PublisherBuilder } from './types';
import { RemoteProtocol } from '../types';
@@ -39,32 +36,29 @@ export class Publishers implements PublisherBuilder {
this.publisherMap.set(protocol, publisher);
}
get(template: TemplateEntityV1alpha1): PublisherBase {
const { protocol, location } = parseLocationAnnotation(template);
const publisher = this.publisherMap.get(protocol);
get(storePath: string, { logger }: { logger: Logger }): PublisherBase {
const protocol = this.typeDetector?.(storePath);
if (!publisher) {
if ((protocol as string) === 'url') {
const type = this.typeDetector?.(location);
const detected = type && this.publisherMap.get(type as RemoteProtocol);
if (detected) {
return detected;
}
if (type) {
throw new Error(
`No publisher configuration available for type '${type}' with url "${location}". ` +
"Make sure you've added appropriate configuration in the 'scaffolder' configuration section",
);
} else {
throw new Error(
`Failed to detect publisher type. Unable to determine integration type for location "${location}". ` +
"Please add appropriate configuration to the 'integrations' configuration section",
);
}
}
throw new Error(`No publisher registered for type: "${protocol}"`);
if (!protocol) {
throw new Error(
`No matching publisher detected for "${storePath}". Please make sure this host is registered in the integration config`,
);
}
logger.info(
`Selected publisher ${protocol} for publishing to URL ${storePath}`,
);
const publisher = this.publisherMap.get(protocol as RemoteProtocol);
if (!publisher) {
throw new Error(
`Failed to detect publisher type. Unable to determine integration type for location "${location}". ` +
"Please add appropriate configuration to the 'integrations' configuration section",
);
}
logger.info(`Selected publisher for protocol ${protocol}`);
return publisher;
}
@@ -139,15 +133,7 @@ export class Publishers implements PublisherBuilder {
);
if (bitbucketConfig) {
try {
const baseUrl = bitbucketConfig.getString('host');
const bitbucketUsername = bitbucketConfig.getString('username');
const bitbucketToken = bitbucketConfig.getString('token');
const bitbucketPublisher = new BitbucketPublisher(
baseUrl,
bitbucketUsername,
bitbucketToken,
);
const bitbucketPublisher = new BitbucketPublisher(config, { logger });
publishers.register('bitbucket', bitbucketPublisher);
} catch (e) {
const providerName = 'bitbucket';
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
import { RequiredTemplateValues } from '../templater';
import { JsonValue } from '@backstage/config';
import { RemoteProtocol } from '../types';
@@ -46,5 +45,5 @@ export type PublisherResult = {
export type PublisherBuilder = {
register(protocol: RemoteProtocol, publisher: PublisherBase): void;
get(template: TemplateEntityV1alpha1): PublisherBase;
get(storePath: string, { logger }: { logger: Logger }): PublisherBase;
};
@@ -154,7 +154,9 @@ export async function createRouter(
{
name: 'Publish template',
handler: async (ctx: StageContext<{ resultDir: string }>) => {
const publisher = publishers.get(ctx.entity);
const publisher = publishers.get(ctx.values.storePath, {
logger: ctx.logger,
});
ctx.logger.info('Will now store the template');
const result = await publisher.publish({
values: ctx.values,