Publish to provider based on url
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user