├── .gitignore ├── images ├── icon.png ├── jenkins-commands.png ├── jenkins-screenshot.png ├── jenkins-status-tooltip.png └── vscode-jenkins-status-logo-readme.png ├── .gitmodules ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── question.md │ └── bug_report.md ├── workflows │ └── main.yml └── copilot-instructions.md ├── .vscodeignore ├── tsconfig.json ├── .vscode ├── settings.json ├── extensions.json ├── tasks.json └── launch.json ├── src ├── setting.ts ├── remote.ts ├── container.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── index.ts │ │ ├── extension.test.ts │ │ └── jenkins.test.ts ├── whats-new │ ├── commands.ts │ └── contentProvider.ts ├── fs.ts ├── Jenkins.ts ├── JenkinsIndicator.ts └── extension.ts ├── package.nls.json ├── LICENSE.md ├── l10n ├── bundle.l10n.json └── bundle.l10n.pt-br.json ├── webpack.config.js ├── CONTRIBUTING.md ├── package.json ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | dist -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefragnani/vscode-jenkins-status/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/jenkins-commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefragnani/vscode-jenkins-status/HEAD/images/jenkins-commands.png -------------------------------------------------------------------------------- /images/jenkins-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefragnani/vscode-jenkins-status/HEAD/images/jenkins-screenshot.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vscode-whats-new"] 2 | path = vscode-whats-new 3 | url = https://github.com/alefragnani/vscode-whats-new 4 | -------------------------------------------------------------------------------- /images/jenkins-status-tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefragnani/vscode-jenkins-status/HEAD/images/jenkins-status-tooltip.png -------------------------------------------------------------------------------- /images/vscode-jenkins-status-logo-readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alefragnani/vscode-jenkins-status/HEAD/images/vscode-jenkins-status-logo-readme.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: alefragnani 2 | patreon: alefragnani 3 | custom: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=EP57F3B6FXKTU&lc=US&item_name=Alessandro%20Fragnani&item_number=vscode%20extensions¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted 4 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | typings/** 4 | **/*.ts 5 | **/*.map 6 | out/** 7 | node_modules/** 8 | .gitignore 9 | tsconfig.json 10 | test/** 11 | *.vsix 12 | package-lock.json 13 | webpack.config.js 14 | **/.github/ 15 | **/.git/** 16 | **/.git 17 | **/.gitmodules 18 | .devcontainer/ -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for the extension 4 | title: "[FEATURE] - " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2020", "DOM" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "alwaysStrict": true 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ] 17 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question about the extension 4 | title: "[QUESTION] - " 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help the extension improve 4 | title: "[BUG] - " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | **Environment/version** 14 | 15 | - Extension version: 16 | - VSCode version: 17 | - OS version: 18 | 19 | **Steps to reproduce** 20 | 21 | 1. 22 | 2. 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version 10 | } -------------------------------------------------------------------------------- /src/setting.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | export interface Setting { 7 | url: string; 8 | username?: string; 9 | password?: string; 10 | name?: string; 11 | strictTls?: number; 12 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "dbaeumer.vscode-eslint", 8 | "eamodio.tsl-problem-matcher", 9 | ], 10 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 11 | "unwantedRecommendations": [ 12 | 13 | ] 14 | } -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "jenkins-status.commands.updateStatus.title": "Jenkins: Update Status", 3 | "jenkins-status.commands.openInJenkins.title": "Jenkins: Open in Jenkins", 4 | "jenkins-status.commands.openInJenkinsConsoleOutput.title": "Jenkins: Open in Jenkins (Console Output)", 5 | "jenkins-status.commands.whatsNew.title": "Jenkins: What's New", 6 | "jenkins-status.commands._whatsNewContextMenu.title": "What's New", 7 | "jenkins-status.configuration.title": "Jenkins Status", 8 | "jenkins-status.configuration.jenkins.polling.description": "Defines a polling (in minutes) for automatic status update. 0 (zero) means 'no update'" 9 | } -------------------------------------------------------------------------------- /src/remote.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri } from "vscode"; 7 | 8 | export const REMOTE_PREFIX = "vscode-remote"; 9 | export const VIRTUAL_WORKSPACE_PREFIX = "vscode-vfs"; 10 | 11 | export function isRemoteUri(uri: Uri): boolean { 12 | return uri.scheme === REMOTE_PREFIX || uri.scheme === VIRTUAL_WORKSPACE_PREFIX; 13 | } 14 | -------------------------------------------------------------------------------- /src/container.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { ExtensionContext } from "vscode"; 7 | 8 | export class Container { 9 | private static _extContext: ExtensionContext; 10 | 11 | public static get context(): ExtensionContext { 12 | return this._extContext; 13 | } 14 | 15 | public static set context(ec: ExtensionContext) { 16 | this._extContext = ec; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "presentation": { 4 | "echo": false, 5 | "reveal": "always", 6 | "focus": false, 7 | "panel": "dedicated", 8 | "showReuseMessage": false 9 | }, 10 | "tasks": [ 11 | { 12 | "type": "npm", 13 | "script": "build", 14 | "group": "build", 15 | "problemMatcher": ["$tsc", "$tslint5"] 16 | }, 17 | { 18 | "type": "npm", 19 | "script": "watch", 20 | "group": { 21 | "kind": "build", 22 | "isDefault": true 23 | }, 24 | "isBackground": true, 25 | "problemMatcher": ["$tsc-watch", "$tslint5"] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error(err); 19 | console.error('Failed to run tests'); 20 | process.exit(1); 21 | } 22 | } 23 | 24 | main(); 25 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import { glob } from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }).then(files => { 16 | // Add files to the test suite 17 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 18 | 19 | try { 20 | // Run the mocha test 21 | mocha.run(failures => { 22 | if (failures > 0) { 23 | e(new Error(`${failures} tests failed.`)); 24 | } else { 25 | c(); 26 | } 27 | }); 28 | } catch (err) { 29 | console.error(err); 30 | e(err); 31 | } 32 | }).catch(err => { 33 | return e(err); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | 7 | const timeout = async (ms = 200) => new Promise(resolve => setTimeout(resolve, ms)); 8 | 9 | suite('Extension Test Suite', () => { 10 | let extension: vscode.Extension; 11 | vscode.window.showInformationMessage('Start all tests.'); 12 | 13 | suiteSetup(() => { 14 | extension = vscode.extensions.getExtension('alefragnani.jenkins-status') as vscode.Extension; 15 | }); 16 | 17 | test('Sample test', () => { 18 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 19 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 20 | }); 21 | 22 | test('Activation test', async () => { 23 | await extension.activate(); 24 | assert.strictEqual(extension.isActive, true); 25 | }); 26 | 27 | test('Extension loads in VSCode and is active', async () => { 28 | await timeout(1500); 29 | assert.strictEqual(extension.isActive, true); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 13 | // "skipFiles": ["/**", "**/node_modules/**", "**/app/out/vs/**", "**/extensions/**"], 14 | "smartStep": true, 15 | "sourceMaps": true 16 | }, 17 | { 18 | "name": "Extension Tests", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": [ 23 | "--disable-extensions", 24 | "--extensionDevelopmentPath=${workspaceFolder}", 25 | "--extensionTestsPath=${workspaceFolder}/out/src/test/suite/index" 26 | ], 27 | "outFiles": [ 28 | "${workspaceFolder}/out/src/test/**/*.js" 29 | ], 30 | "preLaunchTask": "npm: test-compile" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alessandro Fragnani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/whats-new/commands.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { commands } from "vscode"; 7 | import { Container } from "../container"; 8 | import { WhatsNewManager } from "../../vscode-whats-new/src/Manager"; 9 | import { JenkinsStatusContentProvider, JenkinsStatusSocialMediaProvider } from "./contentProvider"; 10 | 11 | export async function registerWhatsNew() { 12 | const provider = new JenkinsStatusContentProvider(); 13 | const viewer = new WhatsNewManager(Container.context) 14 | .registerContentProvider("alefragnani", "jenkins-status", provider) 15 | .registerSocialMediaProvider(new JenkinsStatusSocialMediaProvider()) 16 | await viewer.showPageInActivation(); 17 | Container.context.subscriptions.push(commands.registerCommand("jenkins.whatsNew", () => viewer.showPage())); 18 | Container.context.subscriptions.push(commands.registerCommand("jenkins._whatsNewContextMenu", () => viewer.showPage())); 19 | } -------------------------------------------------------------------------------- /src/test/suite/jenkins.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { colorToBuildStatus, colorToBuildStatusName, BuildStatus } from '../../Jenkins'; 3 | 4 | suite('Jenkins Status Tests', () => { 5 | test('colorToBuildStatus should return correct status for blue', () => { 6 | assert.strictEqual(colorToBuildStatus('blue'), BuildStatus.Success); 7 | }); 8 | 9 | test('colorToBuildStatus should return correct status for red', () => { 10 | assert.strictEqual(colorToBuildStatus('red'), BuildStatus.Failed); 11 | }); 12 | 13 | test('colorToBuildStatus should return InProgress for anime colors', () => { 14 | assert.strictEqual(colorToBuildStatus('blue_anime'), BuildStatus.InProgress); 15 | assert.strictEqual(colorToBuildStatus('red_anime'), BuildStatus.InProgress); 16 | }); 17 | 18 | test('colorToBuildStatus should return Disabled for unknown colors', () => { 19 | assert.strictEqual(colorToBuildStatus('unknown'), BuildStatus.Disabled); 20 | }); 21 | 22 | test('colorToBuildStatusName should return localized status names', () => { 23 | // These will be localized strings, so we just check they're not empty 24 | assert.ok(colorToBuildStatusName('blue').length > 0); 25 | assert.ok(colorToBuildStatusName('red').length > 0); 26 | assert.ok(colorToBuildStatusName('yellow').length > 0); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/fs.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import path = require("path"); 7 | import { Uri, workspace } from "vscode"; 8 | 9 | export function appendPath(uri: Uri, pathSuffix: string): Uri { 10 | const pathPrefix = uri.path.endsWith("/") ? uri.path : `${uri.path}/`; 11 | const filePath = `${pathPrefix}${pathSuffix}`; 12 | 13 | return uri.with({ 14 | path: filePath 15 | }); 16 | } 17 | 18 | export function uriJoin(uri: Uri, ...paths: string[]): string { 19 | return path.join(uri.fsPath, ...paths); 20 | } 21 | 22 | export async function uriExists(uri: Uri): Promise { 23 | 24 | try { 25 | await workspace.fs.stat(uri); 26 | return true; 27 | } catch { 28 | return false; 29 | } 30 | } 31 | 32 | export async function readFile(filePath: string): Promise { 33 | const bytes = await workspace.fs.readFile(Uri.parse(filePath)); 34 | return JSON.parse(Buffer.from(bytes).toString('utf8')); 35 | } 36 | 37 | export async function readFileUri(uri: Uri): Promise { 38 | const bytes = await workspace.fs.readFile(uri); 39 | return JSON.parse(Buffer.from(bytes).toString('utf8')); 40 | } -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 12 | jobs: 13 | # This workflow contains a single job called "build" 14 | build: 15 | # The type of runner that the job will run on 16 | strategy: 17 | matrix: 18 | os: [macos-latest, ubuntu-latest, windows-latest] 19 | runs-on: ${{ matrix.os }} 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | with: 27 | submodules: true 28 | 29 | # Runs a set of commands using the runners shell 30 | - name: Install Node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: 20.x 34 | 35 | # Cache VS Code downloads for better performance 36 | - name: Cache VS Code downloads 37 | uses: actions/cache@v4 38 | with: 39 | path: .vscode-test 40 | key: vscode-test-${{ matrix.os }}-${{ hashFiles('**/package.json') }} 41 | restore-keys: | 42 | vscode-test-${{ matrix.os }}- 43 | 44 | # Install dependencies 45 | - run: npm ci 46 | 47 | # Run tests with native xvfb on Linux, direct on other platforms 48 | - run: xvfb-run -a npm test 49 | if: runner.os == 'Linux' 50 | - run: npm test 51 | if: runner.os != 'Linux' 52 | -------------------------------------------------------------------------------- /l10n/bundle.l10n.json: -------------------------------------------------------------------------------- 1 | { 2 | "The project is not enabled for Jenkins. Missing .jenkins file.":"The project is not enabled for Jenkins. Missing .jenkins file.", 3 | "The current project is not enabled for Jenkins. Please review .jenkins file.":"The current project is not enabled for Jenkins. Please review .jenkins file.", 4 | "Select the Jenkins job to open in browser":"Select the Jenkins job to open in browser", 5 | "Error while retrieving Jenkins settings":"Error while retrieving Jenkins settings", 6 | "The current workspace must be Trusted in order to load settings from .jenkinsrc.js files.":"The current workspace must be Trusted in order to load settings from .jenkinsrc.js files.", 7 | "This workspace contains a `.jenkinsrc.js` file, which requires the Jenkins Status extension to be installed on the remote.":"This workspace contains a `.jenkinsrc.js` file, which requires the Jenkins Status extension to be installed on the remote.", 8 | "Success":"Success", 9 | "Failed":"Failed", 10 | "Unstable":"Unstable", 11 | "Pending":"Pending", 12 | "Aborted":"Aborted", 13 | "Not built":"Not built", 14 | "Disabled":"Disabled", 15 | "Connected":"Connected", 16 | "Invalid Address":"Invalid Address", 17 | "Error":"Error", 18 | "Authentication Required":"Authentication Required", 19 | "{0} (in progress)":"{0} (in progress)", 20 | "The Jenkins job has some connection issues. Please check the status bar for more information.":"The Jenkins job has some connection issues. Please check the status bar for more information.", 21 | "No URL Defined":"No URL Defined", 22 | "Job Name: {0}":"Job Name: {0}", 23 | "Status: {0}":"Status: {0}", 24 | "URL: {0}":"URL: {0}", 25 | "Connection Status: {0}":"Connection Status: {0}", 26 | "Build #: {0}":"Build #: {0}", 27 | "Code #: {0}":"Code #: {0}" 28 | } -------------------------------------------------------------------------------- /l10n/bundle.l10n.pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "The project is not enabled for Jenkins. Missing .jenkins file.":"Este projeto não está habilitado para Jenkins. Faltando arquivo .jenkins.", 3 | "The current project is not enabled for Jenkins. Please review .jenkins file.":"O projeto corrente não está habilitado para Jenkins. Por favor revise o arquivo .jenkins.", 4 | "Select the Jenkins job to open in browser":"Selecione a tarefa Jenkins para abrir no navegador", 5 | "Error while retrieving Jenkins settings":"Erro ao recuperar as configurações Jenkins", 6 | "The current workspace must be Trusted in order to load settings from .jenkinsrc.js files.":"A pasta de trabalho corrente deve ser Confiável para que seja possível ler as configurações de arquivos .jenkinsrc.js.", 7 | "This workspace contains a `.jenkinsrc.js` file, which requires the Jenkins Status extension to be installed on the remote.":"Esta pasta de trabalho contém um arquivo .jenkinsrc.js, o que requer que a extensão Jenkins Status seja instalado no ambiente remoto.", 8 | "Success":"Sucesso", 9 | "Failed":"Falhou", 10 | "Unstable":"Instável", 11 | "Pending":"Pendente", 12 | "Aborted":"Abortado", 13 | "Not built":"Não construído", 14 | "Disabled":"Desabilitado", 15 | "Connected":"Conectado", 16 | "Invalid Address":"Endereço Inválido", 17 | "Error":"Erro", 18 | "Authentication Required":"Antenticação Requerida", 19 | "{0} (in progress)":"{0} (em progresso)", 20 | "The Jenkins job has some connection issues. Please check the status bar for more information.":"A tarefa Jenkins apresenta alguns problemas de conexão. Por favor verifique a barra de status para mais informações.", 21 | "No URL Defined":"Nenhuma URL definida", 22 | "Job Name: {0}":"Nome da Tarefa: {0}", 23 | "Status: {0}":"Status: {0}", 24 | "URL: {0}":"URL: {0}", 25 | "Connection Status: {0}":"Status da Conexão: {0}", 26 | "Build #: {0}":"Construção #: {0}", 27 | "Code #: {0}":"Código #: {0}" 28 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | //@ts-check 8 | 9 | 'use strict'; 10 | 11 | const path = require('path'); 12 | const TerserPlugin = require('terser-webpack-plugin'); 13 | 14 | /**@type {import('webpack').Configuration}*/ 15 | const config = { 16 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 17 | 18 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 19 | output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 20 | path: path.resolve(__dirname, 'dist'), 21 | filename: 'extension.js', 22 | libraryTarget: "commonjs2", 23 | devtoolModuleFilenameTemplate: "../[resource-path]", 24 | }, 25 | optimization: { 26 | minimizer: [new TerserPlugin({ 27 | parallel: true, 28 | extractComments: false, 29 | terserOptions: { 30 | ecma: 2020, 31 | keep_classnames: false, 32 | mangle: true, 33 | module: true, 34 | format: { 35 | comments: false 36 | } 37 | } 38 | })], 39 | }, 40 | 41 | devtool: 'source-map', 42 | externals: { 43 | vscode: "commonjs vscode" // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 44 | }, 45 | resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 46 | extensions: ['.ts', '.js'] 47 | }, 48 | module: { 49 | rules: [{ 50 | test: /\.ts$/, 51 | exclude: /node_modules/, 52 | use: [{ 53 | loader: 'ts-loader', 54 | }] 55 | }] 56 | }, 57 | } 58 | 59 | module.exports = config; 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off all, thank you for taking the time to contribute! 4 | 5 | When contributing to this project, please first discuss the changes you wish to make via an issue before making changes. 6 | 7 | ## Your First Code Contribution 8 | 9 | Unsure where to begin contributing? You can start by looking through the [`help wanted`](https://github.com/alefragnani/vscode-jenkins-status/labels/help%20wanted) issues. 10 | 11 | ### Getting the code 12 | 13 | ``` 14 | git clone https://github.com/alefragnani/vscode-jenkins-status.git 15 | ``` 16 | 17 | Prerequisites 18 | 19 | - [Git](https://git-scm.com/), `>= 2.22.0` 20 | - [NodeJS](https://nodejs.org/), `>= 14.17.27` 21 | 22 | ### Dependencies 23 | 24 | From a terminal, where you have cloned the repository, execute the following command to install the required dependencies: 25 | 26 | ``` 27 | git submodule init 28 | git submodule update 29 | npm install 30 | ``` 31 | 32 | ### Build / Watch 33 | 34 | From inside VS Code, run `Tasks: Run Task Build`. It **Builds** the extension in **Watch Mode**. 35 | 36 | This will first do an initial full build and then watch for file changes, compiling those changes incrementally, enabling a fast, iterative coding experience. 37 | 38 | > **Tip!** You can press Cmd+Shift+B (Ctrl+Shift+B on Windows, Linux) to start the watch task. 39 | 40 | > **Tip!** You don't need to stop and restart the development version of Code after each change. You can just execute `Reload Window` from the command palette. 41 | 42 | ### Linting 43 | 44 | This project uses [ESLint](https://eslint.org/) for code linting. You can run ESLint across the code by calling `npm run lint` from a terminal. Warnings from ESLint show up in the `Errors and Warnings` quick box and you can navigate to them from inside VS Code. 45 | 46 | To lint the code as you make changes you can install the [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension. 47 | 48 | ### Debugging 49 | 50 | 1. Open the `vscode-jenkins-status` folder 51 | 2. Ensure the required [dependencies](#dependencies) are installed 52 | 3. Choose the `Launch Extension` launch configuration from the launch dropdown in the Run and Debug viewlet and press `F5`. 53 | 54 | ## Submitting a Pull Request 55 | 56 | Be sure your branch is up to date (relative to `master`) and submit your PR. Also add reference to the issue the PR refers to. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jenkins-status", 3 | "displayName": "Jenkins Status", 4 | "description": "View your project's Jenkins status inside Visual Studio Code", 5 | "version": "4.5.0", 6 | "publisher": "alefragnani", 7 | "galleryBanner": { 8 | "color": "#168bb9", 9 | "theme": "dark" 10 | }, 11 | "engines": { 12 | "vscode": "^1.74.0" 13 | }, 14 | "categories": [ 15 | "Other" 16 | ], 17 | "keywords": [ 18 | "jenkins", 19 | "status", 20 | "multi-root ready" 21 | ], 22 | "capabilities": { 23 | "virtualWorkspaces": true, 24 | "untrustedWorkspaces": { 25 | "supported": "limited", 26 | "description": "Trust is required to be able to load settings from .jenkinsrc.js files." 27 | } 28 | }, 29 | "extensionKind": [ 30 | "ui", 31 | "workspace" 32 | ], 33 | "activationEvents": [ 34 | "workspaceContains:.jenkins", 35 | "workspaceContains:.jenkinsrc.js" 36 | ], 37 | "main": "./dist/extension", 38 | "l10n": "./l10n", 39 | "icon": "images/icon.png", 40 | "license": "SEE LICENSE IN LICENSE.md", 41 | "homepage": "https://github.com/alefragnani/vscode-jenkins-status/blob/master/README.md", 42 | "repository": { 43 | "type": "git", 44 | "url": "https://github.com/alefragnani/vscode-jenkins-status.git" 45 | }, 46 | "bugs": { 47 | "url": "https://github.com/alefragnani/vscode-jenkins-status/issues" 48 | }, 49 | "sponsor": { 50 | "url": "https://github.com/sponsors/alefragnani" 51 | }, 52 | "contributes": { 53 | "commands": [ 54 | { 55 | "command": "jenkins.updateStatus", 56 | "title": "%jenkins-status.commands.updateStatus.title%" 57 | }, 58 | { 59 | "command": "jenkins.openInJenkins", 60 | "title": "%jenkins-status.commands.openInJenkins.title%" 61 | }, 62 | { 63 | "command": "jenkins.openInJenkinsConsoleOutput", 64 | "title": "%jenkins-status.commands.openInJenkinsConsoleOutput.title%" 65 | }, 66 | { 67 | "command": "jenkins.whatsNew", 68 | "title": "%jenkins-status.commands.whatsNew.title%" 69 | }, 70 | { 71 | "command": "jenkins._whatsNewContextMenu", 72 | "title": "%jenkins-status.commands._whatsNewContextMenu.title%" 73 | } 74 | ], 75 | "menus": { 76 | "commandPalette": [ 77 | { 78 | "command": "jenkins._whatsNewContextMenu", 79 | "when": "false" 80 | } 81 | ], 82 | "extension/context": [ 83 | { 84 | "command": "jenkins._whatsNewContextMenu", 85 | "group": "jenkins", 86 | "when": "extension == alefragnani.jenkins-status && extensionStatus == installed" 87 | } 88 | ] 89 | }, 90 | "configuration": { 91 | "type": "object", 92 | "title": "%jenkins-status.configuration.title%", 93 | "properties": { 94 | "jenkins.polling": { 95 | "type": "integer", 96 | "default": 0, 97 | "description": "%jenkins-status.configuration.jenkins.polling.description%" 98 | } 99 | } 100 | } 101 | }, 102 | "eslintConfig": { 103 | "extends": [ 104 | "vscode-ext" 105 | ] 106 | }, 107 | "scripts": { 108 | "build": "webpack --mode development", 109 | "watch": "webpack --watch --mode development", 110 | "vscode:prepublish": "webpack --mode production", 111 | "webpack": "webpack --mode development", 112 | "webpack-dev": "webpack --mode development --watch", 113 | "compile": "tsc -p ./", 114 | "lint": "eslint -c package.json --ext .ts src vscode-whats-new", 115 | "pretest": "npm run compile && npm run lint", 116 | "test-compile": "tsc -p ./ && npm run webpack", 117 | "just-test": "node ./out/src/test/runTest.js", 118 | "test": "npm run test-compile && npm run just-test" 119 | }, 120 | "dependencies": { 121 | "request": "^2.88.0", 122 | "vscode-ext-codicons": "^1.0.1" 123 | }, 124 | "devDependencies": { 125 | "@types/node": "^18.17.0", 126 | "@types/vscode": "^1.73.0", 127 | "@types/mocha": "^9.0.0", 128 | "@types/glob": "^7.1.4", 129 | "@vscode/test-electron": "^2.5.2", 130 | "@typescript-eslint/eslint-plugin": "^7.0.0", 131 | "@typescript-eslint/parser": "^7.0.0", 132 | "eslint": "^8.1.0", 133 | "eslint-config-vscode-ext": "^1.1.0", 134 | "terser-webpack-plugin": "^5.2.4", 135 | "ts-loader": "^9.2.5", 136 | "typescript": "^5.3.3", 137 | "webpack": "^5.94.0", 138 | "webpack-cli": "^4.8.0", 139 | "mocha": "^11.1.0" 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Jenkins.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import request = require("request"); 6 | import { l10n } from "vscode"; 7 | 8 | export enum BuildStatus { 9 | Success, Failed, Disabled, InProgress 10 | } 11 | 12 | export enum ConnectionStatus { 13 | Connected, InvalidAddress, AuthenticationRequired, Error 14 | } 15 | 16 | export interface JenkinsStatus { 17 | jobName: string; 18 | url: string; 19 | buildNr: number; 20 | status: BuildStatus; 21 | statusName: string; 22 | connectionStatus: ConnectionStatus; 23 | connectionStatusName: string; 24 | code: number; 25 | } 26 | 27 | /**s 28 | * colorToBuildStatus 29 | */ 30 | export function colorToBuildStatus(color: string): BuildStatus { 31 | 32 | if(color.endsWith('_anime')) { return BuildStatus.InProgress; } 33 | 34 | switch (color) { 35 | case "blue" : 36 | return BuildStatus.Success; 37 | 38 | case "red" : 39 | return BuildStatus.Failed; 40 | 41 | 42 | 43 | default: 44 | return BuildStatus.Disabled; 45 | } 46 | } 47 | 48 | export function colorToBuildStatusName(color: string): string { 49 | 50 | switch (color) { 51 | case "blue" : 52 | return l10n.t("Success"); 53 | case "blue_anime": 54 | return l10n.t("Success"); 55 | 56 | case "red" : 57 | return l10n.t("Failed"); 58 | case "red_anime": 59 | return l10n.t("Failed"); 60 | 61 | 62 | case "yellow": 63 | return l10n.t("Unstable"); 64 | case "yellow_anime": 65 | return l10n.t("Unstable"); 66 | 67 | case "grey": 68 | return l10n.t("Pending"); 69 | case "grey_anime": 70 | return l10n.t("Pending"); 71 | 72 | case "aborted": 73 | return l10n.t("Aborted"); 74 | case "aborted_anime": 75 | return l10n.t("Aborted"); 76 | 77 | case "notbuilt": 78 | return l10n.t("Not built"); 79 | case "notbuilt_anime": 80 | return l10n.t("Not built"); 81 | 82 | default: 83 | return l10n.t("Disabled"); 84 | } 85 | } 86 | 87 | export function getConnectionStatusName(status: ConnectionStatus): string { 88 | 89 | switch (status) { 90 | case ConnectionStatus.Connected: 91 | return l10n.t("Connected"); 92 | 93 | case ConnectionStatus.InvalidAddress: 94 | return l10n.t("Invalid Address"); 95 | 96 | case ConnectionStatus.Error: 97 | return l10n.t("Error"); 98 | 99 | default: 100 | return l10n.t("Authentication Required") 101 | } 102 | } 103 | 104 | export class Jenkins { 105 | 106 | public getStatus(url: string, username: string, password: string) { 107 | 108 | return new Promise((resolve, reject) => { 109 | 110 | let data = ""; 111 | let statusCode: number; 112 | let result: JenkinsStatus; 113 | 114 | let authInfo: any; 115 | if (username) { 116 | authInfo = { 117 | auth: { 118 | user: username, 119 | pass: password 120 | } 121 | }; 122 | } else { 123 | authInfo = {}; 124 | } 125 | 126 | request 127 | .get(url + "/api/json", authInfo) 128 | .on("response", function(response) { 129 | statusCode = response.statusCode; 130 | }) 131 | .on("data", function(chunk) { 132 | data += chunk; 133 | }) 134 | .on("end", function() { 135 | switch (statusCode) { 136 | case 200: { 137 | const myArr = JSON.parse(data); 138 | result = { 139 | jobName: myArr.displayName, 140 | url: myArr.url, 141 | status: colorToBuildStatus(myArr.color), 142 | statusName: colorToBuildStatusName(myArr.color), 143 | buildNr: myArr.lastBuild ? myArr.lastBuild.number : 0, 144 | connectionStatus: ConnectionStatus.Connected, 145 | connectionStatusName: getConnectionStatusName(ConnectionStatus.Connected), 146 | code: undefined 147 | } 148 | 149 | if(result.status === BuildStatus.InProgress) { 150 | result.statusName = l10n.t("{0} (in progress)", result.statusName); 151 | } 152 | resolve(result); 153 | break; 154 | } 155 | 156 | case 401: 157 | case 403: 158 | result = { 159 | jobName: "AUTHENTICATION NEEDED", 160 | url, 161 | status: BuildStatus.Disabled, 162 | statusName: l10n.t("Disabled"), 163 | buildNr: undefined, 164 | code: statusCode, 165 | connectionStatus: ConnectionStatus.AuthenticationRequired, 166 | connectionStatusName: getConnectionStatusName(ConnectionStatus.AuthenticationRequired) 167 | } 168 | resolve(result); 169 | break; 170 | 171 | default: 172 | result = { 173 | jobName: "Invalid URL", 174 | url, 175 | status: BuildStatus.Disabled, 176 | statusName: l10n.t("Disabled"), 177 | buildNr: undefined, 178 | code: statusCode, 179 | connectionStatus: ConnectionStatus.InvalidAddress, 180 | connectionStatusName: getConnectionStatusName(ConnectionStatus.InvalidAddress) 181 | } 182 | resolve(result); 183 | break; 184 | } 185 | }) 186 | .on("error", function(err) { 187 | result = { 188 | jobName: err.toString(), 189 | url, 190 | status: BuildStatus.Disabled, 191 | statusName: l10n.t("Disabled"), 192 | buildNr: undefined, 193 | code: err.code, 194 | connectionStatus: ConnectionStatus.Error, 195 | connectionStatusName: getConnectionStatusName(ConnectionStatus.Error) 196 | } 197 | resolve(result); 198 | }) 199 | }); 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [4.5.0] - 2023-07-27 2 | ### Added 3 | - Localization (l10n) support (issue [#90](https://github.com/alefragnani/vscode-jenkins-status/issues/90)) 4 | 5 | ### Changed 6 | - Avoid What's New when using Gitpod (issue [#100](https://github.com/alefragnani/vscode-jenkins-status/issues/100)) 7 | - Avoid What's New when installing lower versions (issue [#100](https://github.com/alefragnani/vscode-jenkins-status/issues/100)) 8 | 9 | ### Fixed 10 | - Typo (PR [#89](https://github.com/alefragnani/vscode-jenkins-status/pull/89) - kudos to @md2perpe) 11 | 12 | ### Internal 13 | - Update badges in README (issue [#102](https://github.com/alefragnani/vscode-jenkins-status/issues/102)) 14 | - Security Alert: word-wrap (dependabot [PR #101](https://github.com/alefragnani/vscode-jenkins-status/pull/101)) 15 | - Security Alert: webpack (dependabot [PR #99](https://github.com/alefragnani/vscode-jenkins-status/pull/99)) 16 | - Cleanup source (PR [#97](https://github.com/alefragnani/vscode-jenkins-status/pull/97) - kudos to @md2perpe) 17 | - Support Implicit Activation Events API (issue [#94](https://github.com/alefragnani/vscode-jenkins-status/issues/94)) 18 | - Security Alert: minimatch (dependabot [PR #92](https://github.com/alefragnani/vscode-jenkins-status/pull/92)) 19 | - Security Alert: qs (dependabot [PR #91](https://github.com/alefragnani/vscode-jenkins-status/pull/91)) 20 | - Security Alert: terser (dependabot [PR #85](https://github.com/alefragnani/vscode-jenkins-status/pull/85)) 21 | - Package Cleanup (issue [#84](https://github.com/alefragnani/vscode-jenkins-status/issues/84)) 22 | 23 | ## [4.4.1] - 2022-07-17 24 | ### Internal 25 | * Add GitHub Sponsors support (PR [#83](https://github.com/alefragnani/vscode-jenkins-status/pull/83)) 26 | 27 | ## [4.4.0] - 2022-03-30 28 | ### Added 29 | * Support **Remote Development** (issue [#41](https://github.com/alefragnani/vscode-jenkins-status/issues/41)) 30 | * Support **Workspace Trust** (issue [#66](https://github.com/alefragnani/vscode-jenkins-status/issues/66)) 31 | * Support **Virtual Workspaces** (issue [#67](https://github.com/alefragnani/vscode-jenkins-status/issues/67)) 32 | * Support new Status Bar API (issue [#68](https://github.com/alefragnani/vscode-jenkins-status/issues/68)) 33 | 34 | ## [4.3.0] - 2021-11-13 35 | ### Internal 36 | - Add CONTRIBUTING documentation (issue [#71](https://github.com/alefragnani/vscode-jenkins-status/issues/71)) 37 | - Update dependencies (issue [#73](https://github.com/alefragnani/vscode-jenkins-status/issues/73)) 38 | - Security Alert: lodash (dependabot [PR #65](https://github.com/alefragnani/vscode-jenkins-status/pull/65)) 39 | - Security Alert: ssri (dependabot [PR #64](https://github.com/alefragnani/vscode-jenkins-status/pull/64)) 40 | - Security Alert: y18n (dependabot [PR #63](https://github.com/alefragnani/vscode-jenkins-status/pull/63)) 41 | - Security Alert: elliptic (dependabot [PR #62](https://github.com/alefragnani/vscode-jenkins-status/pull/62)) 42 | 43 | ## [4.2.1] - 2020-09-05 44 | ### Fixed 45 | - `Open in Jenkins (Console Output)` command with unnamed job (issue [#56](https://github.com/alefragnani/vscode-jenkins-status/issues/56)) 46 | 47 | ## [4.2.0] - 2020-08-08 48 | ### Internal 49 | - Use `vscode-ext-codicons` package (issue [#52](https://github.com/alefragnani/vscode-jenkins-status/issues/52)) 50 | - Shrink installation size/time (issue [#51](https://github.com/alefragnani/vscode-jenkins-status/issues/51)) 51 | 52 | ### Fixed 53 | - Security Alert: elliptic (dependabot [PR #54](https://github.com/alefragnani/vscode-jenkins-status/pull/54)) 54 | - Security Alert: lodash (dependabot [PR #53](https://github.com/alefragnani/vscode-jenkins-status/pull/53)) 55 | 56 | ## [4.1.0] - 2020-06-14 57 | ## Added 58 | - Localization support (issue [#30](https://github.com/alefragnani/vscode-jenkins-status/issues/30)) 59 | 60 | ### Internal 61 | - Support Extension View Context Menu (issue [#50](https://github.com/alefragnani/vscode-jenkins-status/issues/50)) 62 | - Migrate from TSLint to ESLint (issue [#47](https://github.com/alefragnani/vscode-jenkins-status/issues/47)) 63 | 64 | ## [4.0.0] - 2020-02-22 65 | ### Added 66 | - **Multiple Jobs** and **in-progress** status support (Thanks to @eramitmittal [PR #17](https://github.com/alefragnani/vscode-jenkins-status/pull/17)) 67 | - Auto-detect changes in `.jenkins` file 68 | 69 | ### Changed 70 | - **Status Bar** tooltip 71 | 72 | ### Fixed 73 | - Skip authentication when no `username` is provided (Thanks to @leeopop [PR #35](https://github.com/alefragnani/vscode-jenkins-status/pull/35)) 74 | 75 | ## [3.1.2] - 2019-05-28 76 | ### Fixed 77 | - Security Alert: tar 78 | 79 | ## [3.1.1] - 2019-03-14 80 | ### Fixed 81 | - What's New page broken in VS Code 1.32 due to CSS API changes 82 | - Updated `.jenkins` example in README (Thanks to @kimitaka [PR #31](https://github.com/alefragnani/vscode-jenkins-status/pull/31)) 83 | 84 | ## [3.1.0] - 2019-01-18 85 | ### Added 86 | - Status Bar improvements (Thanks to @LinuxSuRen [PR #29](https://github.com/alefragnani/vscode-jenkins-status/pull/29)) 87 | 88 | ## [3.0.0] - 2018-12-02 89 | ### Added 90 | - What's New 91 | 92 | ## [2.2.0] - 2018-04-16 93 | ### Added 94 | - Patreon button 95 | 96 | ## [0.6.1 - 2.1.1] - 2018-04-13 97 | ### Fixed 98 | - Incorrect value in `strictTls` sample 99 | 100 | ## [0.6.0 - 2.1.0] - 2018-04-13 101 | ### Added 102 | - HTTPS Support using `NODE_TLS_REJECT_UNAUTHORIZED` environment variable (PR [#23](https://github.com/alefragnani/vscode-jenkins-status/pull/23) - kudos to @grzegorzjudas) 103 | 104 | ## [0.5.1 - 2.0.1] - 2017-11-13 105 | ### Fixed 106 | - Error while opening single-root workspace with no active file 107 | 108 | ## [0.5.0 - 2.0.0] - 2017-10-28 109 | ### Added 110 | - Multi-root support (issue [#18](https://github.com/alefragnani/vscode-jenkins-status/issues/18)) 111 | 112 | ## [0.4.1 - 1.3.1] - 2017-10-27 113 | ### Fixed 114 | - Fix tooltip for failed builds (PR [#15](https://github.com/alefragnani/vscode-jenkins-status/pull/15) - kudos to @pzelnip) 115 | 116 | ## [0.4.0 - 1.3.0] - 2017-06-10 117 | ### Added 118 | - Support Authentication (PR [#10](https://github.com/alefragnani/vscode-jenkins-status/pull/10) - kudos to @mikepatrick and @umens) 119 | 120 | ## [0.3.0 - 1.2.0] - 2017-01-03 121 | ### Added 122 | - New Command `Open in Jenkins` (Console Output) 123 | 124 | ## [0.2.0 - 1.1.0] - 2016-12-29 125 | ### Added 126 | - Polling for automatic status update (issue [#1](https://github.com/alefragnani/vscode-jenkins-status/issues/1)) 127 | 128 | ## [0.1.2 - 1.0.1] -2016-12-26 129 | ### Fixed 130 | - Support for larger JSON responses (PR [#7](https://github.com/alefragnani/vscode-jenkins-status/pull/7) - kudos to @vojtechhabarta) 131 | 132 | ## [0.1.1 - 1.0.0] - 2016-08-29 133 | 134 | ### Fixed 135 | - No StatusBar added when some connection error occurs (issue [#5](https://github.com/alefragnani/vscode-jenkins-status/issues/5)) 136 | - Error running commands for non Jenkins project (issue [#6](https://github.com/alefragnani/vscode-jenkins-status/issues/6)) 137 | 138 | ## [0.1.0 - 0.9.0] - 2016-08-28 139 | - Initial release 140 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Jenkins Status VS Code Extension 2 | 3 | Jenkins Status is a TypeScript VS Code extension that displays Jenkins build status in the VS Code status bar. It activates when `.jenkins` or `.jenkinsrc.js` files are present in the workspace. 4 | 5 | Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. 6 | 7 | ## Working Effectively 8 | 9 | ### Bootstrap and Dependencies 10 | - Install Node.js version 14.17.27 or higher 11 | - Clone repository: `git clone https://github.com/alefragnani/vscode-jenkins-status.git` 12 | - Initialize git submodules: `git submodule init && git submodule update` -- takes under 1 second. Required for vscode-whats-new dependency. 13 | - Install dependencies: `npm install` -- takes 3-5 seconds. May show deprecation warnings which are expected. 14 | 15 | ### Build Commands 16 | - Development build: `npm run build` -- takes 3-4 seconds. NEVER CANCEL. Set timeout to 30+ seconds. 17 | - Production build: `npm run vscode:prepublish` -- takes 7 seconds. NEVER CANCEL. Set timeout to 30+ seconds. 18 | - TypeScript compilation: `npm run compile` -- takes 3 seconds. NEVER CANCEL. Set timeout to 30+ seconds. 19 | - Watch mode for development: `npm run watch` -- runs continuously, compiles on file changes 20 | 21 | ### Testing and Validation 22 | - Lint code: `npm run lint` -- takes 2 seconds. Always run before committing. 23 | - Run tests: `npm run test` -- WARNING: Tests may fail due to network connectivity to VS Code servers. This is expected in restricted environments. 24 | - Pretest (compile + lint): `npm run pretest` -- takes 5 seconds total 25 | 26 | ## Extension Development Workflow 27 | 28 | ### Testing the Extension in VS Code 29 | 1. Open the extension project in VS Code 30 | 2. Run `npm run build` to compile the extension 31 | 3. Press F5 or use "Launch Extension" debug configuration to open Extension Development Host 32 | 4. In the new VS Code window, open a folder and create a `.jenkins` file to test activation 33 | 5. Verify the Jenkins status appears in the status bar 34 | 35 | ### Debugging 36 | - Use "Launch Extension" configuration in VS Code debugger 37 | - Extension activates when `.jenkins` or `.jenkinsrc.js` files are detected 38 | - Check VS Code Developer Console (Help > Toggle Developer Tools) for error messages 39 | 40 | ## Validation Scenarios 41 | 42 | ### Always Test After Changes 43 | 1. **Extension Activation**: Create test workspace with `.jenkins` file containing `{"url": "http://localhost:8080/job/test/", "name": "Test Build"}` and verify extension activates 44 | 2. **Commands Available**: Check that Jenkins commands appear in Command Palette (Ctrl+Shift+P): 45 | - "Jenkins: Update Status" 46 | - "Jenkins: Open in Jenkins" 47 | - "Jenkins: Open in Jenkins (Console Output)" 48 | 3. **Status Bar Display**: Verify Jenkins status indicator appears in VS Code status bar when extension is active 49 | 4. **Multi-root Support**: Test with multiple workspace folders, each with their own `.jenkins` configuration 50 | 51 | ### Manual Testing Workflow 52 | ```bash 53 | # Create test workspace 54 | mkdir test-workspace && cd test-workspace 55 | echo '{"url": "http://localhost:8080/job/test/", "name": "Test Build"}' > .jenkins 56 | 57 | # Open in VS Code extension development host 58 | # 1. Press F5 from extension project 59 | # 2. In new window, open the test-workspace folder 60 | # 3. Verify Jenkins status appears in status bar 61 | # 4. Test commands from Command Palette 62 | ``` 63 | 64 | ### Configuration File Testing 65 | Test both supported configuration formats: 66 | 67 | **.jenkins file (JSON)**: 68 | ```json 69 | { 70 | "url": "http://127.0.0.1:8080/job/myproject/", 71 | "name": "Jenkins Build", 72 | "username": "jenkins_user", 73 | "password": "jenkins_password_or_token" 74 | } 75 | ``` 76 | 77 | **.jenkinsrc.js file (JavaScript)**: 78 | ```js 79 | module.exports = [{ 80 | "url": "http://127.0.0.1:8080/job/myproject/", 81 | "name": "Jenkins Build", 82 | "username": "jenkins_user", 83 | "password": "jenkins_password_or_token" 84 | }]; 85 | ``` 86 | 87 | ## Build Timing and Expectations 88 | 89 | - **git submodule init && git submodule update**: Under 1 second 90 | - **npm install**: 3-5 seconds on first run 91 | - **npm run build**: 3-4 seconds (development webpack build) 92 | - **npm run vscode:prepublish**: 7 seconds (production webpack build with minification) 93 | - **npm run compile**: 3 seconds (TypeScript compilation) 94 | - **npm run lint**: 1-2 seconds 95 | - **npm run test**: May fail due to network restrictions - this is expected 96 | 97 | **CRITICAL**: NEVER CANCEL any build command. Always set timeouts to at least 30 seconds for any build operation. 98 | 99 | ## Key Repository Structure 100 | 101 | ### Important Files 102 | - `src/extension.ts` - Main extension entry point and activation logic 103 | - `src/JenkinsIndicator.ts` - Status bar indicator implementation 104 | - `src/Jenkins.ts` - Jenkins API communication logic 105 | - `package.json` - Extension manifest and npm scripts 106 | - `.vscode/launch.json` - Debug configurations for testing extension 107 | - `webpack.config.js` - Build configuration for bundling 108 | 109 | ### Output Directories 110 | - `dist/` - Webpack bundled extension output (used in production) 111 | - `out/` - TypeScript compiled output (used in development/testing) 112 | 113 | ## CI/Build Pipeline 114 | The extension builds on Windows, macOS, and Linux using GitHub Actions (`.github/workflows/main.yml`). The CI runs: 115 | 1. `npm install` 116 | 2. `npm test` (includes lint and compile steps) 117 | 118 | ## Known Issues and Limitations 119 | 120 | ### Network Dependencies 121 | - Tests require internet access to download VS Code for testing 122 | - Jenkins status updates require network access to Jenkins servers 123 | - In restricted network environments, focus on build/compile validation rather than runtime testing 124 | 125 | ### Development Notes 126 | - Extension supports VS Code 1.74.0+ 127 | - Uses deprecated `request` library (planned for future update) 128 | - ESLint shows warnings for TypeScript `any` types - these are acceptable for VS Code API compatibility 129 | - Uses webpack for bundling to reduce extension size 130 | 131 | ## Common Tasks Reference 132 | 133 | ### First-time Setup 134 | ```bash 135 | git clone https://github.com/alefragnani/vscode-jenkins-status.git 136 | cd vscode-jenkins-status 137 | git submodule init && git submodule update 138 | npm install 139 | npm run build 140 | ``` 141 | 142 | ### Development Cycle 143 | ```bash 144 | npm run lint # Check code style 145 | npm run build # Build for development 146 | # Test in VS Code Extension Development Host 147 | npm run lint # Final check before commit 148 | ``` 149 | 150 | ### Production Build 151 | ```bash 152 | npm run vscode:prepublish # Creates optimized build for publishing 153 | ``` 154 | 155 | Always run `npm run lint` before committing changes as the CI pipeline will fail on linting errors. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/github/actions/workflow/status/alefragnani/vscode-jenkins-status/main.yml?branch=master)](https://img.shields.io/github/actions/workflow/status/alefragnani/vscode-jenkins-status/main.yml?branch=master) 2 | 3 |

4 |
5 | Read-only Logo 6 |

7 | 8 | # What's new in Jenkins Status 4.5 9 | 10 | * Adds **Localization** support 11 | * Adds **Workspace Trust** support 12 | * Adds **Virtual Workspace** support 13 | * Adds **Remote Development** support 14 | * Adds **Multi-root** support 15 | 16 | ## Support 17 | 18 | **Jenkins Status** is an open source extension created for **Visual Studio Code**. While being free and open source, if you find it useful, please consider supporting it. 19 | 20 | 21 | 22 | 25 | 28 | 31 | 32 |
23 | 24 | 26 | 27 | 29 | 30 |
33 | 34 | # Jenkins Status 35 | 36 | It adds an area in the status bar, indicating the build status for any **Jenkins** job. Specially useful if you want to _connect_ a project with its CI server. 37 | 38 | # Features 39 | 40 | View the Jenkins build status of your project inside Visual Studio Code. 41 | 42 | ![screenshot](images/jenkins-screenshot.png) 43 | 44 | It is automatically enabled if you have a `.jenkins` or `.jenkinsrc.js` file in the root folder of your project. The only required information is the `url` pointing to your Jenkins job. 45 | 46 | If you need _authentication_, just add `username` and `password_or_token` in the `.jenkins` file and you are ready to go. 47 | 48 | **.jenkins** file 49 | 50 | ```json 51 | { 52 | "url": "http://127.0.0.1:8080/job/myproject/", 53 | "username": "jenkins_user", 54 | "password": "jenkins_password_or_token" 55 | } 56 | ``` 57 | or 58 | ```json 59 | [ 60 | { 61 | "url": "http://127.0.0.1:8080/job/myproject/", 62 | "name": "Jenkins Build", 63 | "username": "jenkins_user", 64 | "password": "jenkins_password_or_token" 65 | }, 66 | { 67 | "url": "http://127.0.0.1:8080/job/myprojectTests/", 68 | "name": "Jenkins Acceptance Tests", 69 | "username": "jenkins_user", 70 | "password": "jenkins_password_or_token" 71 | } 72 | ] 73 | ``` 74 | **.jenkinsrc.js** file 75 | ```js 76 | // can also return a promise of required JSON structure 77 | module.exports = [{ 78 | "url": "http://127.0.0.1:8080/job/myproject/", 79 | "name": "Jenkins Build", 80 | "username": "jenkins_user", 81 | "password": "jenkins_password_or_token" 82 | }, 83 | { 84 | "url": "http://127.0.0.1:8080/job/myprojectTests/", 85 | "name": "Jenkins Acceptance Tests", 86 | "username": "jenkins_user", 87 | "password": "jenkins_password_or_token" 88 | }]; 89 | ``` 90 | 91 | If you are having trouble with self-signed certificates and your build status says `SELF_SIGNED_CERT_IN_CHAIN`, you could use a _workaroud_ adding a `strictTls` flag to your `.jenkins` file or `.jenkinsrc.js` export: 92 | 93 | ```json 94 | "strictTls": false 95 | ``` 96 | 97 | ## Available commands 98 | 99 | * `Jenkins: Open in Jenkins:` Open the Jenkins project in you browser 100 | * `Jenkins: Open in Jenkins (Console Output):` Open the Console Output of the Jenkins project in you browser 101 | * `Jenkins: Update Status:` Manually update the status of our Jenkins project 102 | 103 | ## Working with Remotes 104 | 105 | The extension support [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) scenarios, and you may choose how to use it, depending on your needs 106 | 107 | ### I access Remotes, and use `.jenkins` files 108 | 109 | This is the _regular_ scenario, and that's why you don't need to do anything special for the extension to work. It works out of the box. 110 | 111 | When installed locally, the extension will properly recognize the `.jenkins` file on remotes, and display the status bar for each Url. 112 | 113 | _It just works_ 114 | 115 | ### I access Remotes, but I use `.jenkinsrc.js` files 116 | 117 | If you need to use `.jenkinsrc.js` files, the extension must be installed on that remote. This happens because it is not possible to _import_ the `.jenkinsrc.js` file remotely. 118 | 119 | ## Available settings 120 | 121 | * Interval (in minutes) to automatically update the status 122 | ```json 123 | "jenkins.polling": 2 124 | ``` 125 | > Note: 0 (zero) means _no update_ 126 | 127 | ## Contributors 128 | 129 | Special thanks to the people that have contributed to the project: 130 | 131 | * Kalin Krustev (@md2perpe) - Cleanup source ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/97)) 132 | * Kalin Krustev (@md2perpe) - Typo ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/89)) 133 | * Kalin Krustev (@kalinkrustev) - Support Remote Development ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/76)) 134 | * Keunhong Lee (@leeopop) - Skip authentication when no `username` is provided ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/35)) 135 | * Amit Mittal (@eramitmittal) - Multiple Jobs and in-progress status support ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/17)) 136 | * Kimitaka Watanabe (@kimitaka) - Updated `.jenkins` example in README ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/31)) 137 | * Zhao Xiaojie (@LinuxSuRen) - Status Bar improvements ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/29)) 138 | * Grzegorz Judas (@grzegorzjudas) - HTTPS Support using `NODE_TLS_REJECT_UNAUTHORIZED` environment variable ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/23)) 139 | * Adam Parkin (@pzelnip) - Fix tooltip for failed builds ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/15)) 140 | * Mike Patrick (@mikepatrick) and Mensa Ulysse (@umens) - Support Authentication ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/10)) 141 | * Vojtěch Habarta (@vojtechhabarta) - Support for larger JSON responses ([see PR](https://github.com/alefragnani/vscode-jenkins-status/pull/7)) 142 | 143 | Also thanks to everyone who helped opening issues with ideas and bug reports. 144 | 145 | # License 146 | 147 | [MIT](LICENSE.md) © Alessandro Fragnani -------------------------------------------------------------------------------- /src/JenkinsIndicator.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from "vscode"; 7 | import * as Jenkins from "./Jenkins"; 8 | import { Setting } from "./setting"; 9 | import { codicons } from "vscode-ext-codicons"; 10 | import { l10n } from "vscode"; 11 | 12 | export class JenkinsIndicator { 13 | 14 | private statusBarItems: {[settingName: string]: vscode.StatusBarItem} = {}; 15 | private settingNameToUrl: {[settingName: string]: string} = {}; 16 | 17 | public dispose() { 18 | this.hideReadOnly(this.statusBarItems); 19 | } 20 | 21 | public updateJenkinsStatus(settings: Setting[], registerCommand: (cmd: string, callback: () => void ) => void, deRegisterCommand: (cmd: string) => void): Setting[] { 22 | if (!settings) { 23 | return; 24 | } 25 | 26 | let noNameCount = -1; 27 | this.settingNameToUrl = {}; 28 | 29 | for (let index = 0; index < settings.length; index++) { 30 | const setting = settings[index]; 31 | if (!(setting.name)) { 32 | noNameCount++; 33 | setting.name = "Jenkins " + (noNameCount ? noNameCount : ""); 34 | } 35 | 36 | this.settingNameToUrl[setting.name] = setting.url; 37 | 38 | // Create as needed 39 | if (!this.statusBarItems[setting.name]) { 40 | const itemId = settings.length === 1 ? "Jenkins Status" : setting.name; 41 | this.statusBarItems[setting.name] = vscode.window.createStatusBarItem(`alefragnani.jenkins-status.${itemId}`, vscode.StatusBarAlignment.Left); 42 | this.statusBarItems[setting.name].name = itemId; 43 | this.statusBarItems[setting.name].command = "Jenkins." + setting.name + ".openInJenkins"; 44 | 45 | registerCommand("Jenkins." + setting.name + ".openInJenkins", () => { 46 | vscode.env.openExternal(vscode.Uri.parse(this.settingNameToUrl[setting.name])); 47 | }); 48 | registerCommand("Jenkins." + setting.name + ".openInJenkinsConsoleOutput", () => { 49 | jjj.getStatus(url, user, pw) 50 | .then((status) => { 51 | if (status.connectionStatus === Jenkins.ConnectionStatus.Connected) { 52 | vscode.env.openExternal(vscode.Uri.parse(this.settingNameToUrl[setting.name] + status.buildNr.toString() + "/console")); 53 | } else { 54 | vscode.window.showWarningMessage(l10n.t("The Jenkins job has some connection issues. Please check the status bar for more information.")); 55 | } 56 | }); 57 | }); 58 | } 59 | 60 | const jjj: Jenkins.Jenkins = new Jenkins.Jenkins(); 61 | 62 | const url = setting.url; 63 | const user = setting.username ? setting.username : ""; 64 | const pw = setting.password ? setting.password : ""; 65 | 66 | if (setting.strictTls !== undefined) { 67 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = setting.strictTls ? "1" : "0"; 68 | } 69 | 70 | this.statusBarItems[setting.name].text = setting.name; 71 | this.statusBarItems[setting.name].show(); 72 | 73 | // invalid URL 74 | if (!url) { 75 | this.statusBarItems[setting.name].tooltip = l10n.t("No URL Defined"); 76 | this.statusBarItems[setting.name].text = "Jenkins " + codicons.x; 77 | continue; 78 | } 79 | 80 | jjj.getStatus(url, user, pw) 81 | .then((status) => { 82 | 83 | const tooltipJobName = l10n.t("Job Name: {0}", status.jobName); 84 | const tooltipStatus = l10n.t("Status: {0}", status.statusName); 85 | const tooltipUrl = l10n.t("URL: {0}", status.url); 86 | const tooltipConnectionStatus = l10n.t("Connection Status: {0}", status.connectionStatusName); 87 | const tooltipBuild = status.buildNr !== undefined 88 | ? l10n.t("Build #: {0}", status.buildNr) 89 | : undefined; 90 | const tooltipCode = status.code !== undefined 91 | ? l10n.t("Code #: {0}", status.code) 92 | : undefined; 93 | 94 | let tooltip = tooltipJobName + "\n" + 95 | tooltipStatus + "\n" + 96 | tooltipUrl + "\n" + 97 | tooltipConnectionStatus; 98 | if (tooltipBuild !== undefined) 99 | tooltip = tooltip + "\n" + tooltipBuild; 100 | if (tooltipCode !== undefined) 101 | tooltip = tooltip + "\n" + tooltipCode; 102 | 103 | let icon: string; 104 | switch (status.status) { 105 | case Jenkins.BuildStatus.InProgress: 106 | icon = codicons.pulse; 107 | break; 108 | 109 | case Jenkins.BuildStatus.Success: 110 | icon = codicons.check; 111 | break; 112 | 113 | case Jenkins.BuildStatus.Failed: 114 | icon = codicons.alert; 115 | break; 116 | 117 | default: 118 | icon = codicons.stop; 119 | } 120 | 121 | this.statusBarItems[setting.name].text = icon + " " + setting.name; 122 | this.statusBarItems[setting.name].tooltip = tooltip; 123 | this.statusBarItems[setting.name].show(); 124 | }); 125 | } 126 | 127 | const tmpStatusBarItems = this.statusBarItems; 128 | this.statusBarItems = {}; 129 | for (const key in this.settingNameToUrl) { 130 | // eslint-disable-next-line no-prototype-builtins 131 | if (this.settingNameToUrl.hasOwnProperty(key)) { 132 | this.statusBarItems[key] = tmpStatusBarItems[key]; 133 | delete tmpStatusBarItems[key]; 134 | } 135 | } 136 | 137 | this.hideReadOnly(tmpStatusBarItems); 138 | for (const key in tmpStatusBarItems) { 139 | // eslint-disable-next-line no-prototype-builtins 140 | if (tmpStatusBarItems.hasOwnProperty(key)) { 141 | deRegisterCommand("Jenkins." + key + ".openInJenkins"); 142 | deRegisterCommand("Jenkins." + key + ".openInJenkinsConsoleOutput"); 143 | } 144 | } 145 | 146 | return settings; 147 | } 148 | 149 | public hideReadOnly(items) { 150 | for (const key in items) { 151 | // eslint-disable-next-line no-prototype-builtins 152 | if (items.hasOwnProperty(key)) { 153 | const statusBarItem = items[key]; 154 | statusBarItem.dispose(); 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/whats-new/contentProvider.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | // tslint:disable-next-line:max-line-length 7 | import { ChangeLogItem, ChangeLogKind, ContentProvider, Header, Image, IssueKind, SocialMediaProvider, SupportChannel } from "../../vscode-whats-new/src/ContentProvider"; 8 | 9 | export class JenkinsStatusContentProvider implements ContentProvider { 10 | 11 | public provideHeader(logoUrl: string): Header { 12 | return
{logo: {src: logoUrl, height: 50, width: 50}, 13 | message: `Jenkins Status adds an area in the status bar, indicating the 14 | build status for any Jenkins job. Specially useful if you want to 15 | connect a project with its CI server`}; 16 | } 17 | 18 | public provideChangeLog(): ChangeLogItem[] { 19 | const changeLog: ChangeLogItem[] = []; 20 | 21 | changeLog.push({ kind: ChangeLogKind.VERSION, detail: { releaseNumber: "4.5.0", releaseDate: "June 2023" } }); 22 | changeLog.push({ 23 | kind: ChangeLogKind.NEW, 24 | detail: { 25 | message: "Add Localization (l10n) support", 26 | id: 90, 27 | kind: IssueKind.Issue 28 | } 29 | }); 30 | changeLog.push({ 31 | kind: ChangeLogKind.CHANGED, 32 | detail: { 33 | message: "Avoid What's New when using Gitpod", 34 | id: 100, 35 | kind: IssueKind.Issue 36 | } 37 | }); 38 | changeLog.push({ 39 | kind: ChangeLogKind.CHANGED, 40 | detail: { 41 | message: "Avoid What's New when installing lower versions", 42 | id: 100, 43 | kind: IssueKind.Issue 44 | } 45 | }); 46 | changeLog.push({ 47 | kind: ChangeLogKind.FIXED, 48 | detail: { 49 | message: "Typo", 50 | id: 89, 51 | kind: IssueKind.PR, 52 | kudos: "@md2perpe" 53 | } 54 | }); 55 | changeLog.push({ 56 | kind: ChangeLogKind.INTERNAL, 57 | detail: { 58 | message: "README", 59 | id: 102, 60 | kind: IssueKind.Issue 61 | } 62 | }); 63 | changeLog.push({ 64 | kind: ChangeLogKind.INTERNAL, 65 | detail: { 66 | message: "Security Alert: word-wrap", 67 | id: 101, 68 | kind: IssueKind.PR, 69 | kudos: "dependabot" 70 | } 71 | }); 72 | changeLog.push({ 73 | kind: ChangeLogKind.INTERNAL, 74 | detail: { 75 | message: "Security Alert: webpack", 76 | id: 99, 77 | kind: IssueKind.PR, 78 | kudos: "dependabot" 79 | } 80 | }); 81 | changeLog.push({ 82 | kind: ChangeLogKind.INTERNAL, 83 | detail: { 84 | message: "Cleanup source", 85 | id: 97, 86 | kind: IssueKind.PR, 87 | kudos: "@md2perpe" 88 | } 89 | }); 90 | changeLog.push({ 91 | kind: ChangeLogKind.INTERNAL, 92 | detail: { 93 | message: "Support Implicit Activation Event API", 94 | id: 94, 95 | kind: IssueKind.Issue 96 | } 97 | }); 98 | changeLog.push({ 99 | kind: ChangeLogKind.INTERNAL, 100 | detail: { 101 | message: "Security Alert: minimatch", 102 | id: 92, 103 | kind: IssueKind.PR, 104 | kudos: "dependabot" 105 | } 106 | }); 107 | changeLog.push({ 108 | kind: ChangeLogKind.INTERNAL, 109 | detail: { 110 | message: "Security Alert: qs", 111 | id: 91, 112 | kind: IssueKind.PR, 113 | kudos: "dependabot" 114 | } 115 | }); 116 | changeLog.push({ 117 | kind: ChangeLogKind.INTERNAL, 118 | detail: { 119 | message: "Security Alert: terser", 120 | id: 85, 121 | kind: IssueKind.PR, 122 | kudos: "dependabot" 123 | } 124 | }); 125 | changeLog.push({ 126 | kind: ChangeLogKind.INTERNAL, 127 | detail: { 128 | message: "Package cleanup", 129 | id: 84, 130 | kind: IssueKind.Issue 131 | } 132 | }); 133 | 134 | changeLog.push({ kind: ChangeLogKind.VERSION, detail: { releaseNumber: "4.4.1", releaseDate: "June 2022" } }); 135 | changeLog.push({ 136 | kind: ChangeLogKind.INTERNAL, 137 | detail: "Add GitHub Sponsors support" 138 | }); 139 | 140 | changeLog.push({ kind: ChangeLogKind.VERSION, detail: { releaseNumber: "4.4.0", releaseDate: "March 2022" } }); 141 | changeLog.push({ 142 | kind: ChangeLogKind.NEW, 143 | detail: { 144 | message: "Support Remote Development", 145 | id: 41, 146 | kind: IssueKind.Issue 147 | } 148 | }); 149 | changeLog.push({ 150 | kind: ChangeLogKind.NEW, 151 | detail: { 152 | message: "Support Workspace Trust", 153 | id: 66, 154 | kind: IssueKind.Issue 155 | } 156 | }); 157 | changeLog.push({ 158 | kind: ChangeLogKind.NEW, 159 | detail: { 160 | message: "Support Virtual Workspace", 161 | id: 67, 162 | kind: IssueKind.Issue 163 | } 164 | }); 165 | changeLog.push({ 166 | kind: ChangeLogKind.NEW, 167 | detail: { 168 | message: "Support new Status Bar API", 169 | id: 68, 170 | kind: IssueKind.Issue 171 | } 172 | }); 173 | 174 | return changeLog; 175 | } 176 | 177 | public provideSupportChannels(): SupportChannel[] { 178 | const supportChannels: SupportChannel[] = []; 179 | supportChannels.push({ 180 | title: "Become a sponsor on GitHub", 181 | link: "https://www.github.com/sponsors/alefragnani", 182 | message: "Become a Sponsor" 183 | }); 184 | supportChannels.push({ 185 | title: "Donate via PayPal", 186 | link: "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=EP57F3B6FXKTU&lc=US&item_name=Alessandro%20Fragnani&item_number=vscode%20extensions¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted", 187 | message: "Donate via PayPal" 188 | }); 189 | return supportChannels; 190 | } 191 | } 192 | 193 | export class JenkinsStatusSocialMediaProvider implements SocialMediaProvider { 194 | public provideSocialMedias() { 195 | return [{ 196 | title: "Follow me on Twitter", 197 | link: "https://www.twitter.com/alefragnani" 198 | }]; 199 | } 200 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Alessandro Fragnani. All rights reserved. 3 | * Licensed under the MIT License. See License.md in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from "vscode"; 7 | import { JenkinsIndicator } from "./JenkinsIndicator"; 8 | import { Setting } from "./setting"; 9 | import { registerWhatsNew } from "./whats-new/commands"; 10 | import { Container } from "./container"; 11 | import { l10n, Uri } from "vscode"; 12 | import { appendPath, readFileUri, uriExists } from "./fs"; 13 | import { isRemoteUri } from "./remote"; 14 | 15 | declare const __webpack_require__: typeof require; 16 | declare const __non_webpack_require__: typeof require; 17 | 18 | export async function activate(context: vscode.ExtensionContext) { 19 | 20 | Container.context = context; 21 | 22 | let jenkinsIndicator: JenkinsIndicator; 23 | 24 | let currentSettings: Setting[]; 25 | 26 | if (await hasJenkinsInAnyRoot()) { 27 | createJenkinsIndicator(context); 28 | updateStatus(); 29 | } 30 | 31 | await registerWhatsNew(); 32 | 33 | const dispUpdateStatus = vscode.commands.registerCommand("jenkins.updateStatus", () => updateStatus(true)); 34 | context.subscriptions.push(dispUpdateStatus); 35 | 36 | context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async () => { 37 | if (await hasJenkinsInAnyRoot()) { 38 | createJenkinsIndicator(context); 39 | } 40 | updateStatus()} 41 | )); 42 | 43 | context.subscriptions.push(vscode.workspace.onDidGrantWorkspaceTrust(async () => { 44 | updateStatus(false); 45 | })); 46 | 47 | const dispOpenInJenkins = vscode.commands.registerCommand("jenkins.openInJenkins", async () => { 48 | if (!await hasJenkinsInAnyRoot()) { 49 | vscode.window.showWarningMessage(l10n.t("The project is not enabled for Jenkins. Missing .jenkins file.")); 50 | return; 51 | } 52 | 53 | const settings = currentSettings; 54 | if (!settings.length) { 55 | vscode.window.showWarningMessage(l10n.t("The current project is not enabled for Jenkins. Please review .jenkins file.")); 56 | return; 57 | } 58 | 59 | if (settings.length > 1) { 60 | vscode.window.showQuickPick(settings.map(setting => setting.name ? setting.name : setting.url), { 61 | placeHolder : l10n.t("Select the Jenkins job to open in browser") 62 | }).then((settingName: string) => { 63 | vscode.commands.executeCommand("Jenkins." + settingName + ".openInJenkins"); 64 | }); 65 | } else { 66 | vscode.commands.executeCommand("Jenkins." + settings[0].name + ".openInJenkins"); 67 | } 68 | }); 69 | context.subscriptions.push(dispOpenInJenkins); 70 | 71 | const dispOpenInJenkinsConsoleOutput = vscode.commands.registerCommand("jenkins.openInJenkinsConsoleOutput", async () => { 72 | if (!await hasJenkinsInAnyRoot()) { 73 | vscode.window.showWarningMessage(l10n.t("The project is not enabled for Jenkins. Missing .jenkins file.")); 74 | return; 75 | } 76 | 77 | const settings = currentSettings; 78 | if (!settings.length) { 79 | vscode.window.showWarningMessage(l10n.t("The current project is not enabled for Jenkins. Please review .jenkins file.")); 80 | return; 81 | } 82 | 83 | if (settings.length > 1) { 84 | vscode.window.showQuickPick(settings.map(setting => setting.name ? setting.name : setting.url), { 85 | placeHolder : l10n.t("Select the Jenkins job to open in browser") 86 | }).then((settingName: string) => { 87 | vscode.commands.executeCommand("Jenkins." + settingName + ".openInJenkinsConsoleOutput"); 88 | }); 89 | } else { 90 | vscode.commands.executeCommand("Jenkins." + settings[0].name + ".openInJenkinsConsoleOutput"); 91 | } 92 | }); 93 | context.subscriptions.push(dispOpenInJenkinsConsoleOutput); 94 | 95 | function createJenkinsIndicator(aContext: vscode.ExtensionContext) { 96 | if (jenkinsIndicator) { 97 | return; 98 | } 99 | 100 | jenkinsIndicator = new JenkinsIndicator(); 101 | aContext.subscriptions.push(jenkinsIndicator); 102 | } 103 | 104 | async function updateStatus(showMessage?: boolean) { 105 | if (showMessage && !await hasJenkinsInAnyRoot()) { 106 | vscode.window.showWarningMessage(l10n.t("The project is not enabled for Jenkins. Missing .jenkins file.")); 107 | return; 108 | } 109 | 110 | if (jenkinsIndicator) { 111 | currentSettings = jenkinsIndicator.updateJenkinsStatus(await getCurrentSettings(), registerCommand, deRegisterCommand); 112 | } 113 | } 114 | 115 | // let interval; 116 | const polling: number = vscode.workspace.getConfiguration("jenkins").get("polling", 0); 117 | if (polling > 0) { 118 | setInterval(() => updateStatus(), polling * 60000); 119 | } 120 | 121 | async function hasJenkinsInAnyRoot(): Promise { 122 | 123 | if (!vscode.workspace.workspaceFolders) { 124 | return false; 125 | } 126 | 127 | let hasAny = false; 128 | 129 | // for (let index = 0; index < vscode.workspace.workspaceFolders.length; index++) { 130 | for (const element of vscode.workspace.workspaceFolders) { 131 | // const element: vscode.WorkspaceFolder = vscode.workspace.workspaceFolders[index]; 132 | hasAny = !!await getConfigPath(element.uri); 133 | if (hasAny) { 134 | return hasAny; 135 | } 136 | } 137 | 138 | return hasAny; 139 | } 140 | 141 | async function getCurrentSettings(): Promise { 142 | if (!vscode.workspace.workspaceFolders) { 143 | return []; 144 | } 145 | 146 | let settings: Setting[] = []; 147 | try { 148 | for (const element of vscode.workspace.workspaceFolders) { 149 | const jenkinsSettingsPath = await getConfigPath(element.uri); 150 | if (jenkinsSettingsPath.fsPath !== element.uri.fsPath) { 151 | const jenkinsSettings = await readSettings(jenkinsSettingsPath); 152 | if (!jenkinsSettings) { 153 | return undefined; 154 | } 155 | const jenkinsSettings2 = Array.isArray(jenkinsSettings) ? jenkinsSettings : [jenkinsSettings]; 156 | settings = settings.concat(...jenkinsSettings2); 157 | } 158 | } 159 | } catch (error) { 160 | vscode.window.showErrorMessage(l10n.t("Error while retrieving Jenkins settings")); 161 | } 162 | return settings; 163 | } 164 | 165 | async function readSettings(jenkinsSettingsPath: Uri): Promise { 166 | if (jenkinsSettingsPath.fsPath.endsWith(".jenkinsrc.js")) { 167 | if (!vscode.workspace.isTrusted) { 168 | vscode.window.showInformationMessage(l10n.t("The current workspace must be Trusted in order to load settings from .jenkinsrc.js files.")); 169 | return undefined; 170 | } 171 | 172 | if (isRemoteUri(jenkinsSettingsPath)) { 173 | vscode.window.showInformationMessage(l10n.t("This workspace contains a `.jenkinsrc.js` file, which requires the Jenkins Status extension to be installed on the remote.")); 174 | return undefined; 175 | } 176 | 177 | const r = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require; 178 | delete r.cache[r.resolve(jenkinsSettingsPath.fsPath)]; 179 | return await r(jenkinsSettingsPath.fsPath); 180 | } else { 181 | const content = await readFileUri(jenkinsSettingsPath); 182 | return content; 183 | } 184 | } 185 | 186 | function registerCommand(cmd: string, callback: () => void) { 187 | const command = vscode.commands.registerCommand(cmd, callback); 188 | context.subscriptions.push(new Command(cmd, command)); 189 | } 190 | 191 | function deRegisterCommand(cmd: string) { 192 | let foundIndex = -1; 193 | for (let index = 0; index < context.subscriptions.length; index++) { 194 | const subscription = context.subscriptions[index]; 195 | if (subscription instanceof Command) { 196 | if (subscription.cmdId === cmd) { 197 | subscription.dispose(); 198 | foundIndex = index; 199 | break; 200 | } 201 | } 202 | } 203 | 204 | if (foundIndex > -1) { 205 | context.subscriptions.splice(foundIndex, 1); 206 | } 207 | return; 208 | } 209 | 210 | async function getConfigPath(uri: Uri): Promise { 211 | if (await uriExists(appendPath(uri, ".jenkinsrc.js"))) { 212 | return appendPath(uri, ".jenkinsrc.js"); 213 | } else if (uriExists(appendPath(uri, ".jenkins"))) { 214 | return appendPath(uri, ".jenkins"); 215 | } 216 | return uri; 217 | } 218 | 219 | function createWatcher(folder: vscode.WorkspaceFolder) { 220 | const fileSystemWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(folder, "*.{jenkins,jenkins.js}")); 221 | fileSystemWatcher.onDidChange(() => updateStatus(false), context.subscriptions); 222 | fileSystemWatcher.onDidCreate(() => updateStatus(false), context.subscriptions); 223 | fileSystemWatcher.onDidDelete(() => updateStatus(false), context.subscriptions); 224 | context.subscriptions.push(fileSystemWatcher); 225 | } 226 | 227 | if (vscode.workspace.workspaceFolders) { 228 | vscode.workspace.workspaceFolders.forEach(folder => createWatcher(folder)); 229 | } 230 | } 231 | 232 | class Command { 233 | constructor(public cmdId: string, private command: vscode.Disposable) {} 234 | public dispose() { 235 | return this.command.dispose(); 236 | } 237 | } --------------------------------------------------------------------------------