From 9f8f20783813c133e8f689849d9d5d76a70f02ee Mon Sep 17 00:00:00 2001 From: Luca Stocchi <49404737+lstocchi@users.noreply.github.com> Date: Thu, 19 Jan 2023 20:07:40 +0100 Subject: [PATCH] feat: use v1 snippets when v1 is enabled on cluster (#725) (#735) * feat: use v1 snippets when v1 is enabled on cluster (#725) Signed-off-by: Luca Stocchi * fix trigger snippets Signed-off-by: Luca Stocchi Signed-off-by: Luca Stocchi --- package.json | 6 -- rawsnippets/EventListener.yaml | 2 +- rawsnippets/cluster-triggerbinding.yaml | 2 +- ....yaml => pipeline-with-multiple-task.yaml} | 0 rawsnippets/pipeline.yaml | 2 +- rawsnippets/pipelinerun.yaml | 2 +- rawsnippets/task.yaml | 2 +- rawsnippets/taskrun.yaml | 2 +- rawsnippets/triggerbinding.yaml | 2 +- rawsnippets/triggertemplate.yaml | 2 +- snippets/tekton.json | 8 +- src/extension.ts | 34 +++++++- .../tkn-completion-item-provider.ts | 78 +++++++++++++++++++ src/yaml-support/tkn-conditions-provider.ts | 27 ------- .../tkn-condition-provider.test.ts | 41 ---------- .../tkn-yaml-scheme-generator.test.ts | 2 - 16 files changed, 120 insertions(+), 92 deletions(-) rename rawsnippets/{Pipeline-with-multiple-task.yaml => pipeline-with-multiple-task.yaml} (100%) create mode 100644 src/yaml-support/tkn-completion-item-provider.ts delete mode 100644 src/yaml-support/tkn-conditions-provider.ts delete mode 100644 test/yaml-support/tkn-condition-provider.test.ts diff --git a/package.json b/package.json index 4777ba7f..72154515 100644 --- a/package.json +++ b/package.json @@ -137,12 +137,6 @@ } } }, - "snippets": [ - { - "language": "yaml", - "path": "./snippets/tekton.json" - } - ], "keybindings": [ { "command": "tekton.explorer.refresh", diff --git a/rawsnippets/EventListener.yaml b/rawsnippets/EventListener.yaml index a81898f7..c72c78df 100644 --- a/rawsnippets/EventListener.yaml +++ b/rawsnippets/EventListener.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: triggers.tekton.dev/v1beta1 kind: EventListener metadata: name: ${1:name} diff --git a/rawsnippets/cluster-triggerbinding.yaml b/rawsnippets/cluster-triggerbinding.yaml index 1ab2601a..ae9e535c 100644 --- a/rawsnippets/cluster-triggerbinding.yaml +++ b/rawsnippets/cluster-triggerbinding.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: triggers.tekton.dev/v1beta1 kind: ClusterTriggerBinding metadata: name: ${1:name} diff --git a/rawsnippets/Pipeline-with-multiple-task.yaml b/rawsnippets/pipeline-with-multiple-task.yaml similarity index 100% rename from rawsnippets/Pipeline-with-multiple-task.yaml rename to rawsnippets/pipeline-with-multiple-task.yaml diff --git a/rawsnippets/pipeline.yaml b/rawsnippets/pipeline.yaml index 385b1e17..3f2eec40 100644 --- a/rawsnippets/pipeline.yaml +++ b/rawsnippets/pipeline.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: kind: Pipeline metadata: name: ${1:app-deploy} diff --git a/rawsnippets/pipelinerun.yaml b/rawsnippets/pipelinerun.yaml index 12f9baab..bebdb334 100644 --- a/rawsnippets/pipelinerun.yaml +++ b/rawsnippets/pipelinerun.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: kind: PipelineRun metadata: name: ${1:foo-run} diff --git a/rawsnippets/task.yaml b/rawsnippets/task.yaml index e0eff77f..bb9050c6 100644 --- a/rawsnippets/task.yaml +++ b/rawsnippets/task.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: kind: Task metadata: name: ${1} diff --git a/rawsnippets/taskrun.yaml b/rawsnippets/taskrun.yaml index d03ff547..a2dc0f95 100644 --- a/rawsnippets/taskrun.yaml +++ b/rawsnippets/taskrun.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: kind: TaskRun metadata: name: ${1:echo-hello-world-run} diff --git a/rawsnippets/triggerbinding.yaml b/rawsnippets/triggerbinding.yaml index 753c634d..405d99e3 100644 --- a/rawsnippets/triggerbinding.yaml +++ b/rawsnippets/triggerbinding.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: ${1:name} diff --git a/rawsnippets/triggertemplate.yaml b/rawsnippets/triggertemplate.yaml index aedf681f..cbce3318 100644 --- a/rawsnippets/triggertemplate.yaml +++ b/rawsnippets/triggertemplate.yaml @@ -1,4 +1,4 @@ -apiVersion: tekton.dev/v1beta1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: ${1:name} diff --git a/snippets/tekton.json b/snippets/tekton.json index d3cac518..8c1642c9 100644 --- a/snippets/tekton.json +++ b/snippets/tekton.json @@ -16,7 +16,7 @@ "prefix": "Task", "description": "Create a Tekton Task Resource", "body": [ - "apiVersion: tekton.dev/v1beta1", + "apiVersion: ", "kind: Task", "metadata:", " name: ${1}", @@ -44,7 +44,7 @@ "prefix": "TaskRun", "description": "Create a Tekton TaskRun Resource", "body": [ - "apiVersion: tekton.dev/v1beta1", + "apiVersion: ", "kind: TaskRun", "metadata:", " name: ${1:echo-hello-world-run}", @@ -57,7 +57,7 @@ "prefix": "Pipeline", "description": "Create a Tekton Pipeline Resource", "body": [ - "apiVersion: tekton.dev/v1beta1", + "apiVersion: ", "kind: Pipeline", "metadata:", " name: ${1:app-deploy}", @@ -177,7 +177,7 @@ "prefix": "PipelineRun", "description": "Create a Tekton PipelineRun Resource", "body": [ - "apiVersion: tekton.dev/v1beta1", + "apiVersion: ", "kind: PipelineRun", "metadata:", " name: ${1:foo-run}", diff --git a/src/extension.ts b/src/extension.ts index f5e75135..cd478c03 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -27,7 +27,6 @@ import { TKN_RESOURCE_SCHEME, TKN_RESOURCE_SCHEME_READONLY, tektonVfsProvider } import { updateTektonResource } from './tekton/deploy'; import { deleteFromExplorer, deleteFromCustom } from './commands/delete'; import { addTrigger } from './tekton/trigger'; -import { triggerDetection } from './util/detection'; import { showDiagnosticData } from './tekton/diagnostic'; import { TriggerTemplate } from './tekton/triggertemplate'; import { TektonHubTasksViewProvider } from './hub/hub-view'; @@ -35,7 +34,7 @@ import { registerLogDocumentProvider } from './util/log-in-editor'; import { openPipelineRunTemplate, openTaskRunTemplate } from './tekton/generate-template'; import sendTelemetry, { startTelemetry, telemetryLogError, TelemetryProperties } from './telemetry'; import { cli, createCliCommand } from './cli'; -import { getVersion, tektonVersionType } from './util/tknversion'; +import { getVersion, tektonVersionType, TknVersion, version } from './util/tknversion'; import { TektonNode } from './tree-view/tekton-node'; import { checkClusterStatus } from './util/check-cluster-status'; import { getClusterVersions } from './cluster-version'; @@ -49,9 +48,11 @@ import { debugName, debugSessions, pipelineTriggerStatus } from './util/map-obje import { deleteDebugger } from './debugger/delete-debugger'; import { bundleWizard } from './tekton/bundle-pipeline-task'; import { ERR_CLUSTER_TIMED_OUT } from './constants'; +import TektonCompletionItemProvider from './yaml-support/tkn-completion-item-provider'; export let contextGlobalState: vscode.ExtensionContext; let k8sExplorer: k8s.ClusterExplorerV1 | undefined = undefined; +let tektonCompletionProvider: TektonCompletionItemProvider | undefined = undefined; export async function activate(context: vscode.ExtensionContext): Promise { @@ -134,8 +135,22 @@ export async function activate(context: vscode.ExtensionContext): Promise ]; disposables.forEach((e) => context.subscriptions.push(e)); - detectTknCli().then(() => { - triggerDetection(); + detectTknCli().then(async () => { + const tknVersionType: TknVersion = await version(); + setCommandContext(CommandContext.Trigger, tknVersionType && tknVersionType.trigger !== 'unknown'); + tektonCompletionProvider = new TektonCompletionItemProvider(tknVersionType ? tknVersionType.pipeline : undefined); + tektonCompletionProvider.init().then((result) => + { + if (!result.success) + { + console.log(`TektonCompletionItemProvider init failed: ${(result.error.message)}`); + vscode.window.showErrorMessage('Something went wrong when initializing tekton completion item provider. Please see the console for more infos!'); + } + else + { + context.subscriptions.push(vscode.languages.registerCompletionItemProvider({language: 'yaml'}, tektonCompletionProvider)); + } + }); }); detectKubectlCli().then(() => { checkClusterStatus(true); // watch Tekton resources when all required dependency are installed @@ -319,6 +334,9 @@ function refreshTreeView(): void { if (ToolsConfig.getToolLocation('kubectl')) { checkClusterStatus(true); } + if (ToolsConfig.getToolLocation('tkn')) { + updateTektonCompletionProvider(); + } pipelineExplorer.refresh(); debugName.clear(); if (debugSessions && debugSessions.size !== 0) { @@ -329,3 +347,11 @@ function refreshTreeView(): void { debugSessions.clear(); debugExplorer.refresh(); } + +function updateTektonCompletionProvider(): void { + version().then((v) => { + if (tektonCompletionProvider) { + tektonCompletionProvider.update(v.pipeline) + } + }); +} diff --git a/src/yaml-support/tkn-completion-item-provider.ts b/src/yaml-support/tkn-completion-item-provider.ts new file mode 100644 index 00000000..7b3a1de2 --- /dev/null +++ b/src/yaml-support/tkn-completion-item-provider.ts @@ -0,0 +1,78 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ + +import {CompletionItem, CompletionItemProvider as ICompletionItemProvider, SnippetString} from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { contextGlobalState } from '../extension'; +import { Snippet } from './snippet'; +import * as semver from 'semver'; + +export default class TektonCompletionItemProvider implements ICompletionItemProvider { + private newestVersion: string; + public readonly encoding = 'utf-8'; + private readonly tektonSnippetsFile = path.join(contextGlobalState.extensionPath, 'snippets/tekton.json'); + protected items: CompletionItem[]; + + constructor(newestVersion: string) { + this.newestVersion = newestVersion; + this.items = []; + } + + public init(): Thenable { + return this.generateItems(); + } + + public update(newestVersion: string): Thenable { + this.newestVersion = newestVersion; + this.items = []; + return this.generateItems(); + } + + private generateItems(): Thenable { + return new Promise((resolve, reject) => { + fs.readFile(this.tektonSnippetsFile, this.encoding, (readFileError, data) => { + if (readFileError) { + resolve({success: false, error: readFileError}); + } + else { + try { + let version = 'tekton.dev/v1beta1'; + if (this.newestVersion + && this.newestVersion !== 'unknown' + && semver.satisfies(this.newestVersion.replace('v',''), '>=0.43.0')) { + version = 'tekton.dev/v1'; + } + const dataAsJson: Map> = JSON.parse(data); + for (const key in dataAsJson) { + const body: string[] = dataAsJson[key].body; + const bodyAsString = body.join('\n').replace('', version); + const completionItem: CompletionItem = { + label: key, + detail: dataAsJson[key].description, + insertText: new SnippetString(bodyAsString), + }; + this.items.push(completionItem); + } + + resolve({success: true, error: undefined}); + } + catch (error) { + resolve({success: false, error: error}); + } + } + }); + }); + } + + public provideCompletionItems(): CompletionItem[] { + return this.items; + } +} + +export interface ICompletionItemProviderInitResult { + success: boolean; + error: Error; +} diff --git a/src/yaml-support/tkn-conditions-provider.ts b/src/yaml-support/tkn-conditions-provider.ts deleted file mode 100644 index b5ce7ed7..00000000 --- a/src/yaml-support/tkn-conditions-provider.ts +++ /dev/null @@ -1,27 +0,0 @@ -/*----------------------------------------------------------------------------------------------- - * Copyright (c) Red Hat, Inc. All rights reserved. - * Licensed under the MIT License. See LICENSE file in the project root for license information. - *-----------------------------------------------------------------------------------------------*/ - -import { Command } from '../cli-command'; -import { tkn } from '../tkn'; -import { ToolsConfig } from '../tools'; - - -export async function getTknConditionsSnippets(): Promise { - if (!ToolsConfig.getToolLocation('kubectl')) return []; - const result = await tkn.execute(Command.listConditions()); - let data = []; - if (result.error) { - return []; - } - try { - data = JSON.parse(result.stdout).items; - } catch (ignore) { - //show no pipelines if output is not correct json - } - - let condition: string[] = data.map((value) => value.metadata.name); - condition = [...new Set(condition)]; - return condition; -} diff --git a/test/yaml-support/tkn-condition-provider.test.ts b/test/yaml-support/tkn-condition-provider.test.ts deleted file mode 100644 index 015d7881..00000000 --- a/test/yaml-support/tkn-condition-provider.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*----------------------------------------------------------------------------------------------- - * 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 chai from 'chai'; -import * as sinonChai from 'sinon-chai'; -import * as sinon from 'sinon'; -import { tkn } from '../../src/tkn'; -import { getTknConditionsSnippets } from '../../src/yaml-support/tkn-conditions-provider'; -import { ToolsConfig } from '../../src/tools'; - -const expect = chai.expect; -chai.use(sinonChai); - -suite('ConditionRef provider', () => { - - const sandbox = sinon.createSandbox(); - let tknExecute: sinon.SinonStub; - - setup(() => { - tknExecute = sandbox.stub(tkn, 'execute'); - sandbox.stub(ToolsConfig, 'getToolLocation').returns('kubectl'); - }); - - teardown(() => { - sandbox.restore(); - }); - - test('should return empty array if any error on getting condition list', async () => { - tknExecute.resolves({ error: 'Some error' }); - const result = await getTknConditionsSnippets(); - expect(result).eql([]); - }); - - test('should array with condition names', async () => { - tknExecute.resolves({ stdout: '{"items": [{"metadata": {"name": "foo"}}, {"metadata": {"name": "bar"}}]}' }); - const result = await getTknConditionsSnippets(); - expect(result).eql(['foo', 'bar']); - }); - -}); diff --git a/test/yaml-support/tkn-yaml-scheme-generator.test.ts b/test/yaml-support/tkn-yaml-scheme-generator.test.ts index 75c0f92b..582ef94d 100644 --- a/test/yaml-support/tkn-yaml-scheme-generator.test.ts +++ b/test/yaml-support/tkn-yaml-scheme-generator.test.ts @@ -12,7 +12,6 @@ import * as path from 'path'; import { generateScheme } from '../../src/yaml-support/tkn-yaml-scheme-generator'; import { TestTextDocument } from '../text-document-mock'; import * as tasksProvider from '../../src/yaml-support/tkn-tasks-provider'; -import * as conditionsProvider from '../../src/yaml-support/tkn-conditions-provider'; import { TknTask } from '../../src/tekton'; const expect = chai.expect; @@ -25,7 +24,6 @@ suite('Pipeline scheme generator', () => { setup(() => { sandbox.stub(tasksProvider, 'getTknTasksSnippets').resolves([]); - sandbox.stub(conditionsProvider, 'getTknConditionsSnippets').resolves([]); getRawTasksStub = sandbox.stub(tasksProvider, 'getRawTasks'); getRawTasksStub.resolves([]); });