Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pipeline wizard #338

Merged
merged 38 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
dee895e
Initial implementation of pipeline start wizard
evidolob Jul 3, 2020
a89a8b0
include wizard build
evidolob Jul 3, 2020
1e57cc3
Creating Ui for stating pipeline
sudhirverma Jul 8, 2020
c96a7bc
working on pipeline wizard
sudhirverma Jul 9, 2020
4270036
create required field for git and image resource
sudhirverma Jul 9, 2020
4bfadc6
Added secret, configmap and pvc in json object
sudhirverma Jul 10, 2020
e25bd0f
Creating UI for workspace
sudhirverma Jul 13, 2020
d3b95fa
added items field in workspace.
sudhirverma Jul 13, 2020
cb97ac1
Fix vulnerabilities issue
sudhirverma Jul 14, 2020
0b42d7b
improving css style
sudhirverma Jul 14, 2020
821a651
Fix css for UI
sudhirverma Jul 14, 2020
ac894e5
fix Ui for Item workspace
sudhirverma Jul 14, 2020
2b0ff77
Adding Add item functionality
sudhirverma Jul 14, 2020
44e8a54
Fix add item functionality
sudhirverma Jul 15, 2020
b9a01d9
Creating JSON for starting pipeline
sudhirverma Jul 17, 2020
1cd65fb
store value for git, image and param resource
sudhirverma Jul 18, 2020
2b759dd
creating Json for workspace
sudhirverma Jul 18, 2020
710f193
added remove item icon
sudhirverma Jul 20, 2020
6304c65
validation for input
sudhirverma Jul 20, 2020
bd4db04
refactor some code
sudhirverma Jul 20, 2020
442489f
Added spaces while adding item in workspace while innvalid input
sudhirverma Jul 20, 2020
11f1364
added disable remove item functionality
sudhirverma Jul 21, 2020
7995222
disable button if no input provided
sudhirverma Jul 21, 2020
883c33b
Aded validation to start pipeline
sudhirverma Jul 22, 2020
f2f00e9
fix bug for storing value for resource
sudhirverma Jul 22, 2020
cb87295
Fix starting up pipeline from webview
sudhirverma Jul 23, 2020
108a66a
compile webview when starting pipeline
sudhirverma Jul 24, 2020
2af88ac
Fixing css
sudhirverma Jul 24, 2020
15669f0
Fix css
sudhirverma Jul 26, 2020
e4440ca
Fix review chaqnges
sudhirverma Jul 28, 2020
64a29de
Fix css button alignment
sudhirverma Jul 28, 2020
fc45a99
Fix css and minor error
sudhirverma Jul 28, 2020
6be0f74
added disable color for input field
sudhirverma Jul 29, 2020
df94eb9
rebase
sudhirverma Jul 29, 2020
9b03617
show pipeline run diagram after starting pipeline
sudhirverma Jul 30, 2020
123b01e
Fix test
sudhirverma Jul 30, 2020
d007f29
Fix review changes
sudhirverma Jul 30, 2020
a1eac86
Fix review changes
sudhirverma Jul 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
956 changes: 484 additions & 472 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@
"default": "yaml",
"description": "Output format for Tekton specs. One of 'json' or 'yaml' (default)."
},
"vs-tekton.start": {
"Title": "Start Pipeline",
"type": "boolean",
"default": false,
"description": "Start pipeline from QuickPick"
},
"vs-tekton.pipelineRun": {
"Title": "Show PipelineRun on staring Pipeline",
"type": "boolean",
"default": true,
"description": "Show PipelineRun on staring Pipeline from webview"
},
"vs-tekton.showChannelOutput": {
"title": "Show channel on output",
"type": "boolean",
Expand Down Expand Up @@ -766,19 +778,21 @@
"scripts": {
"verify": "node ./out/build/verify-tools.js",
"vscode:prepublish": "npm run build",
"compile": "tsc -p ./ && npm run build-preview",
"compile": "tsc -p ./ && npm run build-preview && npm run build:webview",
"watch": "tsc -watch -p ./",
"clean": "rimraf out && rimraf preview",
"test": "npm run clean && npm run compile && npm run verify && node ./out/build/install-vscode.js && node ./out/build/unit-tests.js",
"update-deps": "node_modules/.bin/ncu --upgrade --loglevel verbose --packageFile package.json && npm update",
"coverage:upload": "codecov -f coverage/coverage-final.json",
"build": "npm run clean && npm run lint && npm run compile",
"build": "npm run clean && npm run lint && npm run compile && npm run build:webview",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"build-preview": "webpack-cli --mode production",
"preview-watch": "webpack-cli -w --mode development",
"snippets-build": "node ./out/build/build-snippets.js",
"icons-build": "node ./out/build/build-svg-icons.js"
"icons-build": "node ./out/build/build-svg-icons.js",
"build:webview": "webpack-cli --mode production --config ./src/webview/pipeline/webpack.config.js",
"watch:webview": "webpack-cli -w --mode development --config ./src/webview/pipeline/webpack.config.js"
},
"devDependencies": {
"@types/byline": "^4.2.31",
Expand All @@ -805,11 +819,13 @@
"@typescript-eslint/parser": "^2.12.0",
"chai": "^4.2.0",
"codecov": "^3.7.1",
"css-loader": "^3.6.0",
"cytoscape": "^3.14.0",
"cytoscape-dagre": "^2.2.2",
"decache": "^4.5.1",
"eslint": "^6.7.2",
"eslint-plugin-header": "^3.0.0",
"file-loader": "^6.0.0",
"glob": "^7.1.3",
"istanbul": "^0.4.5",
"mocha": "^6.2.2",
Expand All @@ -821,9 +837,11 @@
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"source-map-support": "^0.5.16",
"style-loader": "^1.2.1",
"tmp": "0.1.0",
"ts-loader": "^6.2.1",
"typescript": "^3.5.2",
"vscode-codicons": "0.0.4",
"vscode-test": "^1.3.0",
"walker": "^1.0.7",
"webpack": "^4.41.6",
Expand Down
1 change: 1 addition & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
vscode.commands.registerCommand('tekton.open.task', (context) => execute(TaskRun.openDefinition, context)),
vscode.commands.registerCommand('tekton.open.task.palette', (context) => execute(TaskRun.openDefinition, context)),
vscode.commands.registerCommand('tekton.view.pipelinerun.diagram', (context) => execute(PipelineRun.showDiagram, context)),
vscode.commands.registerCommand('tekton.pipeline.wizard.start', (context) => execute(Pipeline.startWizard, context)),

pipelineExplorer,
// Temporarily loaded resource providers
Expand Down
168 changes: 168 additions & 0 deletions src/pipeline/wizard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { contextGlobalState } from '../extension';
import * as path from 'path';
import { Disposable } from '../util/disposable';
import { debounce } from 'debounce';
import { TknResourceItem } from '../tekton/webviewstartpipeline';
import { startPipeline } from '../tekton/startpipeline';

export interface PipelineWizardInput {
readonly resourceColumn: vscode.ViewColumn;
readonly trigger: TknResourceItem;
}

export class PipelineWizard extends Disposable {
static viewType = 'tekton.pipeline.start.wizard';

public static create(input: PipelineWizardInput, previewColumn: vscode.ViewColumn): PipelineWizard {
const webview = vscode.window.createWebviewPanel(
PipelineWizard.viewType,
`Start Pipeline: ${path.basename(input.trigger.name)}`,
previewColumn,
{
enableFindWidget: true,
...getWebviewOptions()
}
);
return new PipelineWizard(webview, input);
}

private editor: vscode.WebviewPanel;
private updateFunc = debounce(() => this.doUpdate(), 500);
private readonly onDisposeEmitter = new vscode.EventEmitter<void>();
public readonly onDispose = this.onDisposeEmitter.event;
private readonly onDidChangeViewStateEmitter = new vscode.EventEmitter<vscode.WebviewPanelOnDidChangeViewStateEvent>();
public readonly onDidChangeViewState = this.onDidChangeViewStateEmitter.event;

constructor(webview: vscode.WebviewPanel, private input: PipelineWizardInput) {
super();
this.editor = webview;
this.register(this.editor.onDidDispose(() => {
this.dispose();
}));


this.register(this.editor.webview.onDidReceiveMessage(async e => {
switch (e.type) {
case 'startPipeline':
// eslint-disable-next-line no-case-declarations
const inputStartPipeline = e.body;
this.dispose();
return await startPipeline(inputStartPipeline);
}
}));

this.updateFunc();
}

dispose(): void {
if (this.disposed) {
return;
}

this.onDisposeEmitter.fire();
this.onDisposeEmitter.dispose();

this.editor.dispose();
super.dispose();
}

reveal(viewColumn: vscode.ViewColumn): void {
this.editor.reveal(viewColumn);
}

update(): void {
// TODO:

}


private async doUpdate(): Promise<void> {
if (this.disposed) {
return;
}
const html = this.getHmlContent();
this.setContent(html);

try {
this.postMessage({ type: 'trigger', data: this.input.trigger });
} catch (err) {
console.error(err);
}
}

private postMessage(msg: {}): void {
if (!this.disposed) {
this.editor.webview.postMessage(msg);
}
}

private setContent(html: string): void {
this.editor.title = 'Start Pipeline';
// this.editor.iconPath = this.iconPath; //TODO: implement
this.editor.webview.options = getWebviewOptions();
this.editor.webview.html = html;
}

private getHmlContent(): string {
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const rule = this.editor.webview.cspSource;
return `
<!DOCTYPE html>
<html lang="en" class="pf-m-redhat-font">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} http: https: data:; media-src 'self' ${rule} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' http: https: data:; font-src 'self' ${rule} http: https: data:;">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css" media="screen, print">
@font-face {
font-family: "codicon";
src: url("${this.getFontPath()}") format("truetype");
}
</style>
</head>
<body>
<div id="root" style="width: 100%; height: 100%;"></div>
${this.getScripts(nonce)}
</body>
</html>`;
}

private getScripts(nonce: string): string {
const out: string[] = [];
out.push(`<script
src="${escapeAttribute(this.editor.webview.asWebviewUri(vscode.Uri.file(path.join(contextGlobalState.extensionPath, '/out/webview/pipeline/index.js'))))}"
nonce="${nonce}"
charset="UTF-8"></script>`);
return out.join('\n');
}

private getFontPath(): string {
return this.editor.webview.asWebviewUri(vscode.Uri.file(path.join(contextGlobalState.extensionPath, '/out/webview/pipeline/assets/codicon.ttf'))).toString();
}

}

function escapeAttribute(value: string | vscode.Uri): string {
return value.toString().replace(/"/g, '&quot;');
}


function getWebviewOptions(): vscode.WebviewOptions {
return {
enableScripts: true,
localResourceRoots: getLocalResourceRoots()
};
}

function getLocalResourceRoots(): vscode.Uri[] {
return [
vscode.Uri.file(path.join(contextGlobalState.extensionPath, '/out/webview/pipeline')),
vscode.Uri.file(path.join(contextGlobalState.extensionPath, '/images'))
];
}
4 changes: 4 additions & 0 deletions src/tekton.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface TknSpec {
resources?: TknResource[];
params?: TknParams[];
serviceAccount?: string;
workspaces?: TknWorkspaces[];
}

export interface TknTaskSpec {
Expand Down Expand Up @@ -65,6 +66,9 @@ export interface TaskResource {
optional?: boolean;
}

export interface TknWorkspaces {
name: string;
}

export interface TknPipelineTrigger {
metadata: TknMetadata;
Expand Down
83 changes: 54 additions & 29 deletions src/tekton/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,50 @@
import { TektonItem } from './tektonitem';
import { TektonNode, Command } from '../tkn';
import { Progress } from '../util/progress';
import { window } from 'vscode';
import { window, ViewColumn } from 'vscode';
import * as cliInstance from '../cli';
import { cli } from '../cli';
import { TknPipelineTrigger } from '../tekton';
import { Trigger, PipelineContent } from './pipelinecontent';
import { PipelineWizard } from '../pipeline/wizard';
import { pipelineData } from './webviewstartpipeline';
import { startPipeline } from './startpipeline';

export class Pipeline extends TektonItem {

static async start(pipeline: TektonNode): Promise<string> {
if (!pipeline) {
pipeline = await window.showQuickPick(await Pipeline.getPipelineNames(), { placeHolder: 'Select Pipeline to start', ignoreFocusOut: true });
}
if (!pipeline) return null;
const result: cliInstance.CliExitData = await Pipeline.tkn.execute(Command.listPipelines(), process.cwd(), false);
let data: TknPipelineTrigger[] = [];
if (result.error) {
console.log(result + ' Std.err when processing pipelines');
}
try {
data = JSON.parse(result.stdout).items;
} catch (ignore) {
//show no pipelines if output is not correct json
}
if (Pipeline.startQuickPick()) {
if (!pipeline) {
pipeline = await window.showQuickPick(await Pipeline.getPipelineNames(), { placeHolder: 'Select Pipeline to start', ignoreFocusOut: true });
}
if (!pipeline) return null;
const result: cliInstance.CliExitData = await Pipeline.tkn.execute(Command.listPipelines(), process.cwd(), false);
let data: TknPipelineTrigger[] = [];
if (result.error) {
console.log(result + ' Std.err when processing pipelines');
}
try {
data = JSON.parse(result.stdout).items;
} catch (ignore) {
//show no pipelines if output is not correct json
}

const pipelineTrigger = data.map<Trigger>(value => ({
name: value.metadata.name,
resources: value.spec.resources,
params: value.spec.params ? value.spec.params : undefined,
workspaces: value.spec['workspaces'] ? value.spec['workspaces'] : undefined,
serviceAcct: value.spec.serviceAccount ? value.spec.serviceAccount : undefined
})).filter((obj) => obj.name === pipeline.getName());
const inputStartPipeline = await PipelineContent.startObject(pipelineTrigger, 'Pipeline');
const pipelineTrigger = data.filter((value) => {
return value.metadata.name === pipeline.getName()
}).map<Trigger>(value => ({
name: value.metadata.name,
resources: value.spec.resources,
params: value.spec.params ? value.spec.params : undefined,
workspaces: value.spec['workspaces'] ? value.spec['workspaces'] : undefined,
serviceAcct: value.spec.serviceAccount ? value.spec.serviceAccount : undefined
}));

return Progress.execFunctionWithProgress(`Starting Pipeline '${inputStartPipeline.name}'.`, () =>
Pipeline.tkn.startPipeline(inputStartPipeline)
.then(() => Pipeline.explorer.refresh())
.then(() => `Pipeline '${inputStartPipeline.name}' successfully started`)
.catch((error) => Promise.reject(`Failed to start Pipeline with error '${error}'`))
);
const inputStartPipeline = await PipelineContent.startObject(pipelineTrigger, 'Pipeline');

return await startPipeline(inputStartPipeline);
} else {
Pipeline.startWizard(pipeline);
}
}

static async restart(pipeline: TektonNode): Promise<string> {
Expand Down Expand Up @@ -92,4 +97,24 @@ export class Pipeline extends TektonItem {
static getDeleteCommand(pipeline: TektonNode): cliInstance.CliCommand {
return Command.deletePipeline(pipeline.getName());
}

static async startWizard(pipeline: TektonNode): Promise<void> {
if (!pipeline) return null;
const result: cliInstance.CliExitData = await Pipeline.tkn.execute(Command.getPipeline(pipeline.getName()), process.cwd(), false);
let data: TknPipelineTrigger;
if (result.error) {
console.log(result + ' Std.err when processing pipelines');
}
try {
data = JSON.parse(result.stdout);
} catch (ignore) {
//show no pipelines if output is not correct json
}
const trigger = await pipelineData(data);
if (!trigger.workspaces && !trigger.resources && !trigger.params) {
await startPipeline(trigger);
} else {
PipelineWizard.create({ trigger, resourceColumn: ViewColumn.Active }, ViewColumn.Active);
}
}
}
Loading