Skip to content

Commit

Permalink
Chore: Added fixed implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
David Garcia committed May 18, 2022
1 parent 9360572 commit 7620920
Show file tree
Hide file tree
Showing 13 changed files with 3,706 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
node_modules/
*.theia
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# smartclide-deployment-interpreter-theia
# smartclide-pipeline-interpreter-plugin
smartclide-pipeline-interpreter-plugin Plugin that allows generating a GitlabCI translation from a declarative Jenkinsfile.
61 changes: 61 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "smartclide-pipeline-interpreter-plugin",
"publisher": "theia",
"keywords": [
"theia-plugin"
],
"version": "0.0.1",
"license": "none",

"files": [
"src"
],
"activationEvents": [
"*"
],
"dependencies": {
"form-data":"^4.0.0"
},
"devDependencies": {
"@theia/plugin": "next",
"@theia/plugin-packager": "latest",
"rimraf": "2.6.2",
"typescript-formatter": "7.2.2",
"typescript": "^3.7.0",
"ts-loader": "^4.1.0",
"clean-webpack-plugin": "^0.1.19",
"webpack": "^4.1.1",
"webpack-cli": "^3.1.1"
},
"scripts": {
"prepare": "yarn run clean && yarn run build",
"clean": "rimraf lib",
"format-code": "tsfmt -r",
"watch": "webpack-cli -w --config webpack.config.js",
"compile": "webpack-cli --config webpack.config.js",

"build": "yarn run format-code && yarn run compile && theia-plugin pack"
},
"engines": {
"theiaPlugin": "next"
},
"theiaPlugin": {

"frontend": "dist/smartclide-pipeline-interpreter-plugin-frontend.js"
}
,
"contributes": {
"configuration": {
"title" : "my config",
"properties": {
"smartclide.pipeline-interpreter.url": {
"default": "http://localhost:8080/convertFile",
"type": "string",
"pattern": "https?://.+",
"scope": "window",
"description": "SmartCLIDE Pipeline interpreter URL"
}
}
}
}
}
15 changes: 15 additions & 0 deletions src/Errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (C) 2021 KAIROS DS
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/

export class OperationCanceledError extends Error {
constructor(msg?: string) {
super("Operation cancelled: " + msg);
}
}
16 changes: 16 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// GLOB Patterns
export const JENKINSFILE_GLOB_PATTERN = '**/*[jJ]enkins[fF]ile*';
export const YAML_GLOB_PATTERN = '**/*.{[yY][aA][mM][lL],[yY][mM][lL]}';
export const JSON_GLOB_PATTERN = '**/*.{[jJ][sS][oO][nN]}';
export const JS_GLOB_PATTERN = '**/*.{[jJ][sS]}';
export const GITLABCI_GLOB_PATTERN = '**/.gitlab-ci.{[yY][aA][mM][lL],[yY][mM][lL]}';

export const EXCLUDE_GLOB_PATTERN = '{**,.*}/{theia,.*,**}/{.*,*.ts*,ts*,package*,settings,launch,lerna}.{json,yaml,yml}';
export const EXCLUDE_GLOB_PATTERN2 = '**/{.*,node_modules*,lib}/}{**,.*}';


// File search max ammout
export const FILE_SEARCH_MAX_RESULT = 100;

export const YES = "Yes";
export const CANCEL = "Cancel";
57 changes: 57 additions & 0 deletions src/pipelineHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as theia from "@theia/plugin";
import * as path from "path";
import { OperationCanceledError } from "./Errors";
import { quickPickJenkinsfileFileItem } from "./quickPickFile";
import { quickPickWorkspaceFolder } from './quickPickWorkspaceFolder';

export async function convertPipeline(gitlabFileUri: theia.Uri | undefined): Promise<void> {
try {
let rootFolder: theia.WorkspaceFolder;
if (gitlabFileUri) {
rootFolder = theia.workspace.getWorkspaceFolder(gitlabFileUri);
}

rootFolder = rootFolder || await quickPickWorkspaceFolder('Please first open a folder or workspace.');

let fileItem = await quickPickJenkinsfileFileItem(gitlabFileUri, rootFolder);
const fileUri = theia.Uri.file(fileItem.absoluteFilePath);

let outputFile = await doConvertPipeline(fileUri);

await theia.window.showTextDocument(outputFile);
theia.window.showInformationMessage("File conversion completed!");
} catch (error) {
theia.window.showErrorMessage('Error: ' + (error as Error).message); error.
console.error(error);
}
}

async function doConvertPipeline(gitlabFileUri: theia.Uri): Promise<theia.Uri> {
var pipelineConversionService = theia.workspace.getConfiguration("smartclide.pipeline-interpreter").get<string>("url");

var parentDir = path.dirname(gitlabFileUri.fsPath);
var outputFile = theia.Uri.file(path.resolve(parentDir, "converted.gitlab-ci.yml"));

var myForm = new FormData();
var fileContent = await theia.workspace.fs.readFile(gitlabFileUri);
myForm.append("file", new Blob([fileContent]));

await fetch(pipelineConversionService, {
method: 'post',
headers: {
'Accept': '*/*',
},
body: myForm
})
.then(res => {
if (!res.ok) {
throw Error("HTTP request returned status " + res.status + " - " + res.statusText);
}
return res;
})
.then(res => {
res.body.getReader().read().then(content => theia.workspace.fs.writeFile(outputFile, content.value));
})

return outputFile;
}
98 changes: 98 additions & 0 deletions src/quickPickFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as path from "path";
import * as theia from "@theia/plugin";
import { Disposable, QuickPick, window } from "@theia/plugin";
import { FILE_SEARCH_MAX_RESULT, YAML_GLOB_PATTERN, JENKINSFILE_GLOB_PATTERN, GITLABCI_GLOB_PATTERN, EXCLUDE_GLOB_PATTERN, EXCLUDE_GLOB_PATTERN2, JS_GLOB_PATTERN } from "./constants";
import { OperationCanceledError } from "./Errors";

export interface Item extends theia.QuickPickItem {
relativeFilePath: string;
relativeFolderPath: string;
absoluteFilePath: string;
absoluteFolderPath: string;
}

export async function quickPickJenkinsfileFileItem(fileUri: theia.Uri, rootFolder: theia.WorkspaceFolder): Promise<Item> {
if (fileUri) {
return createFileItem(rootFolder, fileUri);
}

const items: Item[] = await resolveFilesOfPattern(rootFolder, [JENKINSFILE_GLOB_PATTERN], EXCLUDE_GLOB_PATTERN);
const fileItem: Item = await quickPickFileItem({
title: "Jenkins pipeline descriptor file",
placeholder: "Select a Jenkins pipeline descriptor file:",
items: items
});

if (!fileItem) {
throw new Error('No item was selected');
}
return fileItem;
}

export function createFileItem(rootFolder: theia.WorkspaceFolder, uri: theia.Uri): Item {
const relativeFilePath = path.join(".", uri.fsPath.substr(rootFolder.uri.fsPath.length));

return <Item>{
description: undefined,
relativeFilePath: relativeFilePath,
label: relativeFilePath,
relativeFolderPath: path.dirname(relativeFilePath),
absoluteFilePath: uri.fsPath,
absoluteFolderPath: rootFolder.uri.fsPath
};
}

export async function resolveFilesOfPattern(rootFolder: theia.WorkspaceFolder, filePatterns: string[], excludePattern?: string)
: Promise<Item[] | undefined> {
let uris: theia.Uri[] = [];
await Promise.all(filePatterns.map(async (pattern: string) => {
uris.push(...await getFileUris(rootFolder, pattern, excludePattern));
}));
// remove possible duplicates
uris = uris.filter((uri, index) => uris.findIndex(uri2 => uri.toString() === uri2.toString()) === index);

if (!uris || uris.length === 0) {
return undefined;
} else {
return uris.map(uri => createFileItem(rootFolder, uri));
}
}

async function getFileUris(folder: theia.WorkspaceFolder, globPattern: string, excludePattern?: string): Promise<theia.Uri[]> {
return await theia.workspace.findFiles(new theia.RelativePattern(folder, globPattern), excludePattern ? new theia.RelativePattern(folder, excludePattern) : undefined, FILE_SEARCH_MAX_RESULT, undefined);
}

export interface IPickMetadata {
title: string;
placeholder: string;
items: Item[];
}

export async function quickPickFileItem(pickMetadata: IPickMetadata): Promise<Item> {
const disposables: Disposable[] = [];
const result: Item = await new Promise<Item>((resolve, reject) => {
const pickBox: QuickPick<Item> = window.createQuickPick<Item>();
pickBox.title = pickMetadata.title;
pickBox.placeholder = pickMetadata.placeholder;
pickBox.items = pickMetadata.items;
pickBox.ignoreFocusOut = true;

disposables.push(
pickBox.onDidAccept(() => {
if (!pickBox.selectedItems[0]) {
return;
}
return resolve(pickBox.selectedItems[0]);
}),
pickBox.onDidHide(() => {
return reject(new OperationCanceledError("No file selected"));
})
);
disposables.push(pickBox);
pickBox.show();
});
for (const d of disposables) {
d.dispose();
}
return result;
}
16 changes: 16 additions & 0 deletions src/quickPickWorkspaceFolder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as theia from '@theia/plugin';
import { OperationCanceledError } from './Errors';

export async function quickPickWorkspaceFolder(noWorkspacesMessage: string): Promise<theia.WorkspaceFolder> {
if (theia.workspace.workspaceFolders && theia.workspace.workspaceFolders.length === 1) {
return theia.workspace.workspaceFolders[0];
} else if (theia.workspace.workspaceFolders && theia.workspace.workspaceFolders.length > 1) {
const selected = await theia.window.showWorkspaceFolderPick();
if (!selected) {
throw new OperationCanceledError(noWorkspacesMessage);
}
return selected;
} else {
throw new Error(noWorkspacesMessage);
}
}
22 changes: 22 additions & 0 deletions src/smartclide-pipeline-interpreter-plugin-frontend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

/**
* Generated using theia-plugin-generator
*/

import * as theia from '@theia/plugin';
import { convertPipeline } from './pipelineHandler';

export function start(context: theia.PluginContext) {
const convertPipelineCommand = {
id: 'pipeline-convert-command',
label: "SmartCLIDE: Convert Jenkins pipeline..."
};
console.log("Registering convertPipelineCommand!!!!!!!!!!!");
context.subscriptions.push(theia.commands.registerCommand(convertPipelineCommand, (...args: any[]) => {
convertPipeline(undefined);
}));
}

export function stop() {

}
36 changes: 36 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"compilerOptions": {
"strict": false,
///////////////////////
"alwaysStrict": false,
"noFallthroughCasesInSwitch": true,
"noImplicitThis": true,
"noUnusedLocals": false, // ESLint takes care of this for us
"noUnusedParameters": false, // ESLint takes care of this for us
"noImplicitAny": false, // TODO
"noImplicitReturns": false, // TODO
"strictNullChecks": false, // TODO
///////////////////////
"experimentalDecorators": true,
//"noUnusedLocals": true,
"emitDecoratorMetadata": true,
"downlevelIteration": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es6",
"lib": [
"es6",


"webworker"

],
"sourceMap": true,
"rootDir": "src",
"outDir": "lib",
"skipLibCheck": true
},
"include": [
"src"
]
}
18 changes: 18 additions & 0 deletions tsfmt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"baseIndentSize": 0,
"newLineCharacter": "\n",
"indentSize": 4,
"tabSize": 4,
"indentStyle": 4,
"convertTabsToSpaces": true,
"insertSpaceAfterCommaDelimiter": true,
"insertSpaceAfterSemicolonInForStatements": true,
"insertSpaceBeforeAndAfterBinaryOperators": true,
"insertSpaceAfterKeywordsInControlFlowStatements": true,
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
"placeOpenBraceOnNewLineForFunctions": false,
"placeOpenBraceOnNewLineForControlBlocks": false
}
Loading

0 comments on commit 7620920

Please sign in to comment.