@eclipse-che/che-devworkspace-generator 0.0.1-1671620831

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.
Files changed (43) hide show
  1. package/lib/api/devfile-context.js +3 -0
  2. package/lib/api/devfile-context.js.map +1 -0
  3. package/lib/devfile/dev-container-component-finder.js +91 -0
  4. package/lib/devfile/dev-container-component-finder.js.map +1 -0
  5. package/lib/devfile/devfile-module.js +19 -0
  6. package/lib/devfile/devfile-module.js.map +1 -0
  7. package/lib/entrypoint.js +67 -0
  8. package/lib/entrypoint.js.map +1 -0
  9. package/lib/fetch/fetch-module.js +19 -0
  10. package/lib/fetch/fetch-module.js.map +1 -0
  11. package/lib/fetch/url-fetcher.js +114 -0
  12. package/lib/fetch/url-fetcher.js.map +1 -0
  13. package/lib/generate.js +177 -0
  14. package/lib/generate.js.map +1 -0
  15. package/lib/github/github-module.js +19 -0
  16. package/lib/github/github-module.js.map +1 -0
  17. package/lib/github/github-resolver.js +54 -0
  18. package/lib/github/github-resolver.js.map +1 -0
  19. package/lib/github/github-url.js +46 -0
  20. package/lib/github/github-url.js.map +1 -0
  21. package/lib/inversify/inversify-binding.js +78 -0
  22. package/lib/inversify/inversify-binding.js.map +1 -0
  23. package/lib/main.js +233 -0
  24. package/lib/main.js.map +1 -0
  25. package/lib/plugin-registry/plugin-registry-module.js +19 -0
  26. package/lib/plugin-registry/plugin-registry-module.js.map +1 -0
  27. package/lib/plugin-registry/plugin-registry-resolver.js +113 -0
  28. package/lib/plugin-registry/plugin-registry-resolver.js.map +1 -0
  29. package/package.json +91 -0
  30. package/src/api/devfile-context.ts +28 -0
  31. package/src/devfile/dev-container-component-finder.ts +43 -0
  32. package/src/devfile/devfile-module.ts +18 -0
  33. package/src/entrypoint.ts +22 -0
  34. package/src/fetch/fetch-module.ts +18 -0
  35. package/src/fetch/url-fetcher.ts +49 -0
  36. package/src/generate.ts +117 -0
  37. package/src/github/github-module.ts +18 -0
  38. package/src/github/github-resolver.ts +41 -0
  39. package/src/github/github-url.ts +47 -0
  40. package/src/inversify/inversify-binding.ts +46 -0
  41. package/src/main.ts +166 -0
  42. package/src/plugin-registry/plugin-registry-module.ts +18 -0
  43. package/src/plugin-registry/plugin-registry-resolver.ts +36 -0
@@ -0,0 +1,43 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ import { DevfileContext } from '../api/devfile-context';
12
+ import { V1alpha2DevWorkspaceSpecTemplateComponents } from '@devfile/api';
13
+ import { injectable } from 'inversify';
14
+
15
+ /**
16
+ * Need to find dev container from main dev workspace
17
+ */
18
+ @injectable()
19
+ export class DevContainerComponentFinder {
20
+ async find(devfileContext: DevfileContext): Promise<V1alpha2DevWorkspaceSpecTemplateComponents | undefined> {
21
+ // search in main devWorkspace
22
+ const devComponents = devfileContext.devWorkspace.spec?.template?.components
23
+ ?.filter(component => component.container)
24
+ .filter(
25
+ // we should ignore component that do not mount the sources
26
+ component => component.container && component.container.mountSources !== false
27
+ );
28
+
29
+ // only one, fine, else error
30
+ if (!devComponents || devComponents.length === 0) {
31
+ throw new Error('Not able to find any dev container component in DevWorkspace');
32
+ } else if (devComponents.length === 1) {
33
+ return devComponents[0];
34
+ } else {
35
+ console.warn(
36
+ `More than one dev container component has been potentially found, taking the first one of ${devComponents.map(
37
+ component => component.name
38
+ )}`
39
+ );
40
+ return devComponents[0];
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,18 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import { ContainerModule, interfaces } from 'inversify';
11
+
12
+ import { DevContainerComponentFinder } from './dev-container-component-finder';
13
+
14
+ const devfileModule = new ContainerModule((bind: interfaces.Bind) => {
15
+ bind(DevContainerComponentFinder).toSelf().inSingletonScope();
16
+ });
17
+
18
+ export { devfileModule };
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**********************************************************************
3
+ * Copyright (c) 2022 Red Hat, Inc.
4
+ *
5
+ * This program and the accompanying materials are made
6
+ * available under the terms of the Eclipse Public License 2.0
7
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
8
+ *
9
+ * SPDX-License-Identifier: EPL-2.0
10
+ ***********************************************************************/
11
+
12
+ import 'reflect-metadata';
13
+
14
+ import { Main } from './main';
15
+
16
+ (async (): Promise<void> => {
17
+ const main = new Main();
18
+ const success = await main.start();
19
+ if (!success) {
20
+ process.exit(1);
21
+ }
22
+ })();
@@ -0,0 +1,18 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import { ContainerModule, interfaces } from 'inversify';
11
+
12
+ import { UrlFetcher } from './url-fetcher';
13
+
14
+ const fetchModule = new ContainerModule((bind: interfaces.Bind) => {
15
+ bind(UrlFetcher).toSelf().inSingletonScope();
16
+ });
17
+
18
+ export { fetchModule };
@@ -0,0 +1,49 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import { inject, injectable } from 'inversify';
11
+
12
+ import { AxiosInstance } from 'axios';
13
+
14
+ /**
15
+ * Allow to grab external content
16
+ * if browser support is enabled, it will update URLs to make them work on browser side.
17
+ */
18
+ @injectable()
19
+ export class UrlFetcher {
20
+ // Can't use AxiosInstance interface there
21
+ @inject(Symbol.for('AxiosInstance'))
22
+ private axiosInstance: AxiosInstance;
23
+
24
+ // fetch content optionally, if the URL is not found, we return undefined without throwing errors
25
+ async fetchTextOptionalContent(url: string): Promise<string | undefined> {
26
+ try {
27
+ const response = await this.axiosInstance.get<string>(url, {
28
+ responseType: 'text',
29
+ });
30
+ return response.data;
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ } catch (error: any) {
33
+ // not found then we return undefined
34
+ if (error.response && error.response.status === 404) {
35
+ return undefined;
36
+ }
37
+ throw error;
38
+ }
39
+ }
40
+
41
+ // fetch content
42
+ async fetchText(url: string): Promise<string> {
43
+ const response = await this.axiosInstance.get<string>(url, {
44
+ responseType: 'text',
45
+ });
46
+ return response.data;
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ }
49
+ }
@@ -0,0 +1,117 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ import {
12
+ V1alpha2DevWorkspace,
13
+ V1alpha2DevWorkspaceSpecContributions,
14
+ V1alpha2DevWorkspaceSpecTemplateComponents,
15
+ V1alpha2DevWorkspaceTemplate,
16
+ V1alpha2DevWorkspaceTemplateSpec,
17
+ } from '@devfile/api';
18
+ import { injectable, inject } from 'inversify';
19
+ import * as jsYaml from 'js-yaml';
20
+ import * as fs from 'fs-extra';
21
+ import { DevfileContext } from './api/devfile-context';
22
+ import { DevContainerComponentFinder } from './devfile/dev-container-component-finder';
23
+
24
+ @injectable()
25
+ export class Generate {
26
+ static readonly MERGE_CONTRIBUTION = 'controller.devfile.io/merge-contribution';
27
+
28
+ @inject(DevContainerComponentFinder)
29
+ private devContainerComponentFinder: DevContainerComponentFinder;
30
+
31
+ async generate(devfileContent: string, editorContent: string, outputFile: string): Promise<DevfileContext> {
32
+ const context = await this.generateContent(devfileContent, editorContent);
33
+
34
+ // write the result
35
+ // write templates and then DevWorkspace in a single file
36
+ const allContentArray = context.devWorkspaceTemplates.map(template => jsYaml.dump(template));
37
+ allContentArray.push(jsYaml.dump(context.devWorkspace));
38
+
39
+ const generatedContent = allContentArray.join('---\n');
40
+
41
+ await fs.writeFile(outputFile, generatedContent, 'utf-8');
42
+
43
+ console.log(`DevWorkspace ${context.devWorkspaceTemplates[0].metadata.name} was generated.`);
44
+ return context;
45
+ }
46
+
47
+ async generateContent(devfileContent: string, editorContent: string): Promise<DevfileContext> {
48
+ const devfile = jsYaml.load(devfileContent);
49
+
50
+ // const originalDevfile = Object.assign({}, devfile);
51
+ // sets the suffix to the devfile name
52
+ const suffix = devfile.metadata.name || '';
53
+
54
+ // devfile of the editor
55
+ const editorDevfile = jsYaml.load(editorContent);
56
+
57
+ // transform it into a devWorkspace template
58
+ const metadata = editorDevfile.metadata;
59
+ // add sufix
60
+ metadata.name = `${metadata.name}-${suffix}`;
61
+ delete editorDevfile.metadata;
62
+ delete editorDevfile.schemaVersion;
63
+ const editorDevWorkspaceTemplate: V1alpha2DevWorkspaceTemplate = {
64
+ apiVersion: 'workspace.devfile.io/v1alpha2',
65
+ kind: 'DevWorkspaceTemplate',
66
+ metadata,
67
+ spec: editorDevfile as V1alpha2DevWorkspaceTemplateSpec,
68
+ };
69
+
70
+ // transform it into a devWorkspace
71
+ const devfileMetadata = devfile.metadata;
72
+ const devfileCopy = Object.assign({}, devfile);
73
+ delete devfileCopy.schemaVersion;
74
+ delete devfileCopy.metadata;
75
+ const editorSpecContribution: V1alpha2DevWorkspaceSpecContributions = {
76
+ name: 'editor',
77
+ kubernetes: {
78
+ name: editorDevWorkspaceTemplate.metadata.name,
79
+ },
80
+ };
81
+ const devWorkspace: V1alpha2DevWorkspace = {
82
+ apiVersion: 'workspace.devfile.io/v1alpha2',
83
+ kind: 'DevWorkspace',
84
+ metadata: devfileMetadata,
85
+ spec: {
86
+ started: true,
87
+ template: devfileCopy,
88
+ contributions: [editorSpecContribution],
89
+ },
90
+ };
91
+
92
+ // for now the list of devWorkspace templates is only the editor template
93
+ const devWorkspaceTemplates = [editorDevWorkspaceTemplate];
94
+
95
+ const context = {
96
+ devfile,
97
+ devWorkspace,
98
+ devWorkspaceTemplates,
99
+ suffix,
100
+ };
101
+
102
+ // grab container where to inject controller.devfile.io/merge-contribution attribute
103
+ let devContainer: V1alpha2DevWorkspaceSpecTemplateComponents = await this.devContainerComponentFinder.find(context);
104
+
105
+ // add attributes
106
+ let devContainerAttributes = devContainer.attributes;
107
+ if (!devContainerAttributes) {
108
+ devContainerAttributes = {};
109
+ devContainerAttributes[Generate.MERGE_CONTRIBUTION] = true;
110
+ devContainer.attributes = devContainerAttributes;
111
+ } else {
112
+ devContainerAttributes[Generate.MERGE_CONTRIBUTION] = true;
113
+ }
114
+
115
+ return context;
116
+ }
117
+ }
@@ -0,0 +1,18 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import { ContainerModule, interfaces } from 'inversify';
11
+
12
+ import { GithubResolver } from './github-resolver';
13
+
14
+ const githubModule = new ContainerModule((bind: interfaces.Bind) => {
15
+ bind(GithubResolver).toSelf().inSingletonScope();
16
+ });
17
+
18
+ export { githubModule };
@@ -0,0 +1,41 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ import { GithubUrl } from './github-url';
12
+ import { injectable } from 'inversify';
13
+
14
+ /**
15
+ * Provides a github URL object allowing to interact
16
+ */
17
+ @injectable()
18
+ export class GithubResolver {
19
+ // eslint-disable-next-line max-len
20
+ static readonly GITHUB_URL_PATTERN =
21
+ /^(?:http)(?:s)?(?:\:\/\/)github\.com\/(?<repoUser>[^\/]+)\/(?<repoName>[^\/]+)((\/)|(?:\/(blob|tree)\/(?<branchName>[^\/]+)(?:\/(?<subFolder>.*))?))?$/;
22
+
23
+ resolve(link: string): GithubUrl {
24
+ const match = GithubResolver.GITHUB_URL_PATTERN.exec(link);
25
+ if (!match) {
26
+ throw new Error(`Invalid github URL: ${link}`);
27
+ }
28
+ const repoUser = this.getGroup(match, 'repoUser');
29
+ const repoName = this.getGroup(match, 'repoName');
30
+ const branchName = this.getGroup(match, 'branchName', 'HEAD');
31
+ const subFolder = this.getGroup(match, 'subFolder');
32
+ return new GithubUrl(repoUser, repoName, branchName, subFolder);
33
+ }
34
+
35
+ getGroup(match: RegExpExecArray, groupName: string, defaultValue?: string) {
36
+ if (match.groups && match.groups[groupName]) {
37
+ return match.groups[groupName];
38
+ }
39
+ return defaultValue || '';
40
+ }
41
+ }
@@ -0,0 +1,47 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ /**
12
+ * Provides helper methods on top of github URL to get for example raw content of get relative links
13
+ */
14
+ export class GithubUrl {
15
+ // raw link
16
+ static readonly RAW_LINK = 'https://raw.githubusercontent.com';
17
+
18
+ constructor(
19
+ private readonly repoUser: string,
20
+ private readonly repoName: string,
21
+ private readonly branchName: string,
22
+ private readonly subFolder: string
23
+ ) {}
24
+
25
+ /**
26
+ * Provides the raw link to the given path based on the current repository information
27
+ */
28
+ getContentUrl(path: string): string {
29
+ return `${GithubUrl.RAW_LINK}/${this.repoUser}/${this.repoName}/${this.branchName}/${path}`;
30
+ }
31
+
32
+ getUrl(): string {
33
+ return `https://github.com/${this.repoUser}/${this.repoName}/tree/${this.branchName}/${this.subFolder}`;
34
+ }
35
+
36
+ getCloneUrl(): string {
37
+ return `https://github.com/${this.repoUser}/${this.repoName}.git`;
38
+ }
39
+
40
+ getRepoName(): string {
41
+ return this.repoName;
42
+ }
43
+
44
+ getBranchName(): string {
45
+ return this.branchName;
46
+ }
47
+ }
@@ -0,0 +1,46 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import 'reflect-metadata';
11
+
12
+ import { AxiosInstance } from 'axios';
13
+ import { Container } from 'inversify';
14
+ import { devfileModule } from '../devfile/devfile-module';
15
+ import { fetchModule } from '../fetch/fetch-module';
16
+ import { githubModule } from '../github/github-module';
17
+ import { pluginRegistryModule } from '../plugin-registry/plugin-registry-module';
18
+
19
+ /**
20
+ * Manage all bindings for inversify
21
+ */
22
+ export class InversifyBinding {
23
+ private container: Container;
24
+
25
+ public async initBindings(options: InversifyBindingOptions): Promise<Container> {
26
+ this.container = new Container();
27
+
28
+ this.container.load(devfileModule);
29
+ this.container.load(fetchModule);
30
+ this.container.load(githubModule);
31
+ this.container.load(pluginRegistryModule);
32
+
33
+ this.container.bind(Symbol.for('AxiosInstance')).toConstantValue(options.axiosInstance);
34
+ this.container.bind('string').toConstantValue(options.pluginRegistryUrl).whenTargetNamed('PLUGIN_REGISTRY_URL');
35
+
36
+ return this.container;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Options for inversify bindings
42
+ */
43
+ export interface InversifyBindingOptions {
44
+ pluginRegistryUrl: string;
45
+ axiosInstance: AxiosInstance;
46
+ }
package/src/main.ts ADDED
@@ -0,0 +1,166 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ import * as axios from 'axios';
12
+ import * as fs from 'fs-extra';
13
+ import { Generate } from './generate';
14
+ import { GithubResolver } from './github/github-resolver';
15
+ import * as jsYaml from 'js-yaml';
16
+ import { InversifyBinding } from './inversify/inversify-binding';
17
+ import { UrlFetcher } from './fetch/url-fetcher';
18
+ import { PluginRegistryResolver } from './plugin-registry/plugin-registry-resolver';
19
+ import { V1alpha2DevWorkspaceSpecTemplate } from '@devfile/api';
20
+ import { DevfileContext } from './api/devfile-context';
21
+
22
+ export class Main {
23
+ protected async doStart(): Promise<DevfileContext> {
24
+ let devfilePath: string | undefined;
25
+ let devfileUrl: string | undefined;
26
+ let outputFile: string | undefined;
27
+ let editorPath: string | undefined;
28
+ let pluginRegistryUrl: string | undefined;
29
+ let editorEntry: string | undefined;
30
+ const projects: { name: string; location: string }[] = [];
31
+
32
+ const args = process.argv.slice(2);
33
+ args.forEach(arg => {
34
+ if (arg.startsWith('--devfile-path:')) {
35
+ devfilePath = arg.substring('--devfile-path:'.length);
36
+ }
37
+ if (arg.startsWith('--devfile-url:')) {
38
+ devfileUrl = arg.substring('--devfile-url:'.length);
39
+ }
40
+ if (arg.startsWith('--plugin-registry-url:')) {
41
+ pluginRegistryUrl = arg.substring('--plugin-registry-url:'.length);
42
+ }
43
+ if (arg.startsWith('--editor-entry:')) {
44
+ editorEntry = arg.substring('--editor-entry:'.length);
45
+ }
46
+ if (arg.startsWith('--editor-path:')) {
47
+ editorPath = arg.substring('--editor-path:'.length);
48
+ }
49
+ if (arg.startsWith('--output-file:')) {
50
+ outputFile = arg.substring('--output-file:'.length);
51
+ }
52
+ if (arg.startsWith('--project.')) {
53
+ const name = arg.substring('--project.'.length, arg.indexOf('='));
54
+ let location = arg.substring(arg.indexOf('=') + 1);
55
+ location = location.replace('{{_INTERNAL_URL_}}', '{{ INTERNAL_URL }}');
56
+
57
+ projects.push({ name, location });
58
+ }
59
+ });
60
+ if (!editorPath && !editorEntry) {
61
+ throw new Error('missing --editor-path: or --editor-entry: parameter');
62
+ }
63
+ if (editorEntry && !pluginRegistryUrl) {
64
+ pluginRegistryUrl = 'https://eclipse-che.github.io/che-plugin-registry/main/v3';
65
+ console.log(`No plug-in registry url. Setting to ${pluginRegistryUrl}`);
66
+ }
67
+ if (!devfilePath && !devfileUrl) {
68
+ throw new Error('missing --devfile-path: or --devfile-url: parameter');
69
+ }
70
+ if (!outputFile) {
71
+ throw new Error('missing --output-file: parameter');
72
+ }
73
+
74
+ const axiosInstance = axios.default;
75
+ const inversifyBinbding = new InversifyBinding();
76
+ const container = await inversifyBinbding.initBindings({
77
+ pluginRegistryUrl,
78
+ axiosInstance,
79
+ });
80
+ container.bind(Generate).toSelf().inSingletonScope();
81
+
82
+ let devfileContent;
83
+ let editorContent;
84
+
85
+ // gets the github URL
86
+ if (devfileUrl) {
87
+ const githubResolver = container.get(GithubResolver);
88
+ const githubUrl = githubResolver.resolve(devfileUrl);
89
+ // user devfile
90
+ devfileContent = await container.get(UrlFetcher).fetchText(githubUrl.getContentUrl('devfile.yaml'));
91
+
92
+ // load content
93
+ const devfileParsed = jsYaml.load(devfileContent);
94
+
95
+ // is there projects in the devfile ?
96
+ if (devfileParsed && !devfileParsed.projects) {
97
+ // no, so add the current project being cloned
98
+ devfileParsed.projects = [
99
+ {
100
+ name: githubUrl.getRepoName(),
101
+ git: {
102
+ remotes: { origin: githubUrl.getCloneUrl() },
103
+ checkoutFrom: { revision: githubUrl.getBranchName() },
104
+ },
105
+ },
106
+ ];
107
+ }
108
+ // get back the content
109
+ devfileContent = jsYaml.dump(devfileParsed);
110
+ } else {
111
+ devfileContent = await fs.readFile(devfilePath);
112
+ }
113
+
114
+ // enhance projects
115
+ devfileContent = this.replaceIfExistingProjects(devfileContent, projects);
116
+
117
+ if (editorEntry) {
118
+ // devfile of the editor
119
+ const editorDevfile = await container.get(PluginRegistryResolver).loadDevfilePlugin(editorEntry);
120
+ editorContent = jsYaml.dump(editorDevfile);
121
+ } else {
122
+ editorContent = await fs.readFile(editorPath);
123
+ }
124
+
125
+ const generate = container.get(Generate);
126
+ return generate.generate(devfileContent, editorContent, outputFile);
127
+ }
128
+
129
+ // Update project entry based on the projects passed as parameter
130
+ public replaceIfExistingProjects(devfileContent: string, projects: { name: string; location: string }[]): string {
131
+ // do nothing if no override
132
+ if (projects.length === 0) {
133
+ return devfileContent;
134
+ }
135
+ const devfileParsed: V1alpha2DevWorkspaceSpecTemplate = jsYaml.load(devfileContent);
136
+
137
+ if (!devfileParsed || !devfileParsed.projects) {
138
+ return devfileContent;
139
+ }
140
+ devfileParsed.projects = devfileParsed.projects.map(project => {
141
+ const userProjectConfiguration = projects.find(p => p.name === project.name);
142
+ if (userProjectConfiguration) {
143
+ if (userProjectConfiguration.location.endsWith('.zip')) {
144
+ // delete git section and use instead zip
145
+ delete project.git;
146
+ project.zip = { location: userProjectConfiguration.location };
147
+ } else {
148
+ project.git.remotes.origin = userProjectConfiguration.location;
149
+ }
150
+ }
151
+ return project;
152
+ });
153
+ return jsYaml.dump(devfileParsed);
154
+ }
155
+
156
+ async start(): Promise<boolean> {
157
+ try {
158
+ await this.doStart();
159
+ return true;
160
+ } catch (error) {
161
+ console.error('stack=' + error.stack);
162
+ console.error('Unable to start', error);
163
+ return false;
164
+ }
165
+ }
166
+ }
@@ -0,0 +1,18 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+ import { ContainerModule, interfaces } from 'inversify';
11
+
12
+ import { PluginRegistryResolver } from './plugin-registry-resolver';
13
+
14
+ const pluginRegistryModule = new ContainerModule((bind: interfaces.Bind) => {
15
+ bind(PluginRegistryResolver).toSelf().inSingletonScope();
16
+ });
17
+
18
+ export { pluginRegistryModule };
@@ -0,0 +1,36 @@
1
+ /**********************************************************************
2
+ * Copyright (c) 2022 Red Hat, Inc.
3
+ *
4
+ * This program and the accompanying materials are made
5
+ * available under the terms of the Eclipse Public License 2.0
6
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
7
+ *
8
+ * SPDX-License-Identifier: EPL-2.0
9
+ ***********************************************************************/
10
+
11
+ import * as jsYaml from 'js-yaml';
12
+
13
+ import { inject, injectable, named } from 'inversify';
14
+
15
+ import { UrlFetcher } from '../fetch/url-fetcher';
16
+
17
+ /**
18
+ * Resolve plug-ins by grabbing the definition from the plug-in registry.
19
+ */
20
+ @injectable()
21
+ export class PluginRegistryResolver {
22
+ @inject('string')
23
+ @named('PLUGIN_REGISTRY_URL')
24
+ private pluginRegistryUrl: string;
25
+
26
+ @inject(UrlFetcher)
27
+ private urlFetcher: UrlFetcher;
28
+
29
+ // FQN id (like eclipse/che-theia/next)
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ async loadDevfilePlugin(devfileId: string): Promise<any> {
32
+ const devfileUrl = `${this.pluginRegistryUrl}/plugins/${devfileId}/devfile.yaml`;
33
+ const devfileContent = await this.urlFetcher.fetchText(devfileUrl);
34
+ return jsYaml.load(devfileContent);
35
+ }
36
+ }