@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.
- package/lib/api/devfile-context.js +3 -0
- package/lib/api/devfile-context.js.map +1 -0
- package/lib/devfile/dev-container-component-finder.js +91 -0
- package/lib/devfile/dev-container-component-finder.js.map +1 -0
- package/lib/devfile/devfile-module.js +19 -0
- package/lib/devfile/devfile-module.js.map +1 -0
- package/lib/entrypoint.js +67 -0
- package/lib/entrypoint.js.map +1 -0
- package/lib/fetch/fetch-module.js +19 -0
- package/lib/fetch/fetch-module.js.map +1 -0
- package/lib/fetch/url-fetcher.js +114 -0
- package/lib/fetch/url-fetcher.js.map +1 -0
- package/lib/generate.js +177 -0
- package/lib/generate.js.map +1 -0
- package/lib/github/github-module.js +19 -0
- package/lib/github/github-module.js.map +1 -0
- package/lib/github/github-resolver.js +54 -0
- package/lib/github/github-resolver.js.map +1 -0
- package/lib/github/github-url.js +46 -0
- package/lib/github/github-url.js.map +1 -0
- package/lib/inversify/inversify-binding.js +78 -0
- package/lib/inversify/inversify-binding.js.map +1 -0
- package/lib/main.js +233 -0
- package/lib/main.js.map +1 -0
- package/lib/plugin-registry/plugin-registry-module.js +19 -0
- package/lib/plugin-registry/plugin-registry-module.js.map +1 -0
- package/lib/plugin-registry/plugin-registry-resolver.js +113 -0
- package/lib/plugin-registry/plugin-registry-resolver.js.map +1 -0
- package/package.json +91 -0
- package/src/api/devfile-context.ts +28 -0
- package/src/devfile/dev-container-component-finder.ts +43 -0
- package/src/devfile/devfile-module.ts +18 -0
- package/src/entrypoint.ts +22 -0
- package/src/fetch/fetch-module.ts +18 -0
- package/src/fetch/url-fetcher.ts +49 -0
- package/src/generate.ts +117 -0
- package/src/github/github-module.ts +18 -0
- package/src/github/github-resolver.ts +41 -0
- package/src/github/github-url.ts +47 -0
- package/src/inversify/inversify-binding.ts +46 -0
- package/src/main.ts +166 -0
- package/src/plugin-registry/plugin-registry-module.ts +18 -0
- 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
|
+
}
|
package/src/generate.ts
ADDED
|
@@ -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
|
+
}
|