From 1f88cd5a696b247d034f1b796f0dbab691cab6e8 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Thu, 17 Nov 2022 12:48:03 +0000 Subject: [PATCH 1/3] feat: allow pipeline name to be passed in as a param --- bin/out.js | 4 +-- bin/out.test.js | 69 +++++++++++++++++++++++++++++++------------- bin/validate.js | 6 +++- bin/validate.test.js | 9 ++++-- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/bin/out.js b/bin/out.js index 90dc965..6836f9a 100755 --- a/bin/out.js +++ b/bin/out.js @@ -47,7 +47,7 @@ stdin.on('end', function () { function buildUrl(source, params) { const instanceVars = buildInstanceVariables(); - return encodeURI(`${env.ATC_EXTERNAL_URL}/api/v1/teams/${env.BUILD_TEAM_NAME}/pipelines/${env.BUILD_PIPELINE_NAME}/resources/${params.resource_name}/check/webhook?webhook_token=${params.webhook_token}${instanceVars}`); + return encodeURI(`${env.ATC_EXTERNAL_URL}/api/v1/teams/${env.BUILD_TEAM_NAME}/pipelines/${params.pipeline ? params.pipeline : env.BUILD_PIPELINE_NAME}/resources/${params.resource_name}/check/webhook?webhook_token=${params.webhook_token}${instanceVars}`); } function buildInstanceVariables() { @@ -200,4 +200,4 @@ function log(message) { console.error(message); } -module.exports = { buildInstanceVariables }; +module.exports = { buildInstanceVariables, buildUrl }; diff --git a/bin/out.test.js b/bin/out.test.js index 2c3acde..f0d2f38 100644 --- a/bin/out.test.js +++ b/bin/out.test.js @@ -1,6 +1,6 @@ const out = require('./out'); -describe('buildInstanceVaribles', () => { +describe('out', () => { const OLD_ENV = process.env; @@ -13,26 +13,55 @@ describe('buildInstanceVaribles', () => { process.env = OLD_ENV; }); - it('with unset variables, returns empty string', () => { - delete process.env.BUILD_PIPELINE_INSTANCE_VARS; - const instanceVar = out.buildInstanceVariables(); - expect(instanceVar).toEqual('') - }); - - it('with empty variables, returns empty string', () => { - process.env.BUILD_PIPELINE_INSTANCE_VARS = "{}"; - const instanceVar = out.buildInstanceVariables(); - expect(instanceVar).toEqual('') - }); - - it('with bad json, it throws an exception', () => { - process.env.BUILD_PIPELINE_INSTANCE_VARS = "{695988"; - expect(() => out.buildInstanceVariables()).toThrow(); + describe('buildInstanceVaribles', () => { + it('with unset variables, returns empty string', () => { + delete process.env.BUILD_PIPELINE_INSTANCE_VARS; + const instanceVar = out.buildInstanceVariables(); + expect(instanceVar).toEqual('') + }); + + it('with empty variables, returns empty string', () => { + process.env.BUILD_PIPELINE_INSTANCE_VARS = "{}"; + const instanceVar = out.buildInstanceVariables(); + expect(instanceVar).toEqual('') + }); + + it('with bad json, it throws an exception', () => { + process.env.BUILD_PIPELINE_INSTANCE_VARS = "{695988"; + expect(() => out.buildInstanceVariables()).toThrow(); + }); + + it('with valid instance variables, it builds variable string to append to url', () => { + process.env.BUILD_PIPELINE_INSTANCE_VARS = '{"name":"John", "age":30, "car":"Toyota"}'; + const instanceVar = out.buildInstanceVariables(); + expect(instanceVar).toEqual(`&vars.name="John"&vars.age="30"&vars.car="Toyota"`) + }); }); - it('with valid instance variables, it builds variable string to append to url', () => { - process.env.BUILD_PIPELINE_INSTANCE_VARS = '{"name":"John", "age":30, "car":"Toyota"}'; - const instanceVar = out.buildInstanceVariables(); - expect(instanceVar).toEqual(`&vars.name="John"&vars.age="30"&vars.car="Toyota"`) + describe('buildUrl', () => { + it('defaults to using the current pipeline name', () => { + process.env.ATC_EXTERNAL_URL = 'https://example.com'; + process.env.BUILD_PIPELINE_NAME = 'pipeline'; + process.env.BUILD_TEAM_NAME = 'team'; + const params = { + resource_name: 'resource', + webhook_token: 'token' + } + const instanceVar = out.buildUrl(null, params); + expect(instanceVar).toEqual("https://example.com/api/v1/teams/team/pipelines/pipeline/resources/resource/check/webhook?webhook_token=token") + }); + + it('prefers the pipeline name from params', () => { + process.env.ATC_EXTERNAL_URL = 'https://example.com'; + process.env.BUILD_PIPELINE_NAME = 'pipeline'; + process.env.BUILD_TEAM_NAME = 'team'; + const params = { + pipeline: 'another-pipeline', + resource_name: 'resource', + webhook_token: 'token' + } + const instanceVar = out.buildUrl(null, params); + expect(instanceVar).toEqual("https://example.com/api/v1/teams/team/pipelines/another-pipeline/resources/resource/check/webhook?webhook_token=token") + }); }); }); diff --git a/bin/validate.js b/bin/validate.js index e2dfef8..de4d97c 100644 --- a/bin/validate.js +++ b/bin/validate.js @@ -46,7 +46,11 @@ const configSchema = { transform: ['trim', 'toEnumCase'], enum: validOperations, errorMessage: { enum: 'must be either create or delete' } - } + }, + pipeline: { + type: 'string', + transform: ['trim', 'toLowerCase'] + }, }, required: ['org', 'repo', 'resource_name', 'webhook_token', 'operation'] }, diff --git a/bin/validate.test.js b/bin/validate.test.js index 08d2183..8d62111 100644 --- a/bin/validate.test.js +++ b/bin/validate.test.js @@ -117,6 +117,7 @@ describe('validate.input', () => { 'params.resource_name', 'params.webhook_token', 'params.operation', + 'params.pipeline', ]; constrainedFields.forEach(field => { @@ -155,13 +156,15 @@ describe('validate.input', () => { resource_name: '', webhook_token: '', operation: 'CrEaTe', - events: ['pUsH'] + events: ['pUsH'], + pipeline: 'mYPipeline' } }; expect(() => validate.config(config)).not.toThrow(); expect(config.params.operation).toBe('create'); expect(config.params.events).toEqual(['push']); + expect(config.params.pipeline).toBe('mypipeline'); }); it('trims whitespace', () => { @@ -176,13 +179,15 @@ describe('validate.input', () => { resource_name: '', webhook_token: '', operation: ' create ', - events: [' push '] + events: [' push '], + pipeline: ' mypipeline ' } }; expect(() => validate.config(config)).not.toThrow(); expect(config.params.operation).toBe('create'); expect(config.params.events).toEqual(['push']); + expect(config.params.pipeline).toBe('mypipeline'); }); it('checks fields with array constraint', () => { From 8358ff33dc46094049ae231b4cf2d7c21a67888c Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Thu, 17 Nov 2022 14:46:27 +0000 Subject: [PATCH 2/3] feat: allow pipeline_instance_vars to be passed in as a param --- bin/out.js | 13 +++++++++++-- bin/out.test.js | 14 +++++++++----- bin/validate.js | 3 +++ bin/validate.test.js | 44 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/bin/out.js b/bin/out.js index 6836f9a..8ea7752 100755 --- a/bin/out.js +++ b/bin/out.js @@ -46,11 +46,11 @@ stdin.on('end', function () { }); function buildUrl(source, params) { - const instanceVars = buildInstanceVariables(); + const instanceVars = buildInstanceVariables(params); return encodeURI(`${env.ATC_EXTERNAL_URL}/api/v1/teams/${env.BUILD_TEAM_NAME}/pipelines/${params.pipeline ? params.pipeline : env.BUILD_PIPELINE_NAME}/resources/${params.resource_name}/check/webhook?webhook_token=${params.webhook_token}${instanceVars}`); } -function buildInstanceVariables() { +function buildInstanceVariables(params) { let vars = ""; if (env.BUILD_PIPELINE_INSTANCE_VARS) { try { @@ -62,6 +62,15 @@ function buildInstanceVariables() { throw new Error(exception); } } + if ("pipeline_instance_vars" in params && params.pipeline_instance_vars) { + try { + for (const [key, value] of Object.entries(params.pipeline_instance_vars)) { + vars += `&vars.${key}="${value}"`; + } + } catch(exception) { + throw new Error(exception); + } + } return vars; } diff --git a/bin/out.test.js b/bin/out.test.js index f0d2f38..6d886b7 100644 --- a/bin/out.test.js +++ b/bin/out.test.js @@ -16,25 +16,29 @@ describe('out', () => { describe('buildInstanceVaribles', () => { it('with unset variables, returns empty string', () => { delete process.env.BUILD_PIPELINE_INSTANCE_VARS; - const instanceVar = out.buildInstanceVariables(); + const params = {}; + const instanceVar = out.buildInstanceVariables(params); expect(instanceVar).toEqual('') }); it('with empty variables, returns empty string', () => { process.env.BUILD_PIPELINE_INSTANCE_VARS = "{}"; - const instanceVar = out.buildInstanceVariables(); + const params = {pipeline_instance_vars: {}}; + const instanceVar = out.buildInstanceVariables(params); expect(instanceVar).toEqual('') }); it('with bad json, it throws an exception', () => { process.env.BUILD_PIPELINE_INSTANCE_VARS = "{695988"; - expect(() => out.buildInstanceVariables()).toThrow(); + const params = {}; + expect(() => out.buildInstanceVariables(params)).toThrow(); }); it('with valid instance variables, it builds variable string to append to url', () => { process.env.BUILD_PIPELINE_INSTANCE_VARS = '{"name":"John", "age":30, "car":"Toyota"}'; - const instanceVar = out.buildInstanceVariables(); - expect(instanceVar).toEqual(`&vars.name="John"&vars.age="30"&vars.car="Toyota"`) + const params = {pipeline_instance_vars: {"license": "full"}}; + const instanceVar = out.buildInstanceVariables(params); + expect(instanceVar).toEqual(`&vars.name="John"&vars.age="30"&vars.car="Toyota"&vars.license="full"`) }); }); diff --git a/bin/validate.js b/bin/validate.js index de4d97c..4320a17 100644 --- a/bin/validate.js +++ b/bin/validate.js @@ -51,6 +51,9 @@ const configSchema = { type: 'string', transform: ['trim', 'toLowerCase'] }, + pipeline_instance_vars: { + type: 'object', + }, }, required: ['org', 'repo', 'resource_name', 'webhook_token', 'operation'] }, diff --git a/bin/validate.test.js b/bin/validate.test.js index 8d62111..50e07f0 100644 --- a/bin/validate.test.js +++ b/bin/validate.test.js @@ -157,7 +157,8 @@ describe('validate.input', () => { webhook_token: '', operation: 'CrEaTe', events: ['pUsH'], - pipeline: 'mYPipeline' + pipeline: 'mYPipeline', + pipeline_instance_vars: {} } }; @@ -180,7 +181,8 @@ describe('validate.input', () => { webhook_token: '', operation: ' create ', events: [' push '], - pipeline: ' mypipeline ' + pipeline: ' mypipeline ', + pipeline_instance_vars: {} } }; @@ -192,7 +194,7 @@ describe('validate.input', () => { it('checks fields with array constraint', () => { const constrainedFields = [ - 'params.events' + 'params.events', ]; constrainedFields.forEach(field => { @@ -207,7 +209,41 @@ describe('validate.input', () => { resource_name: '', webhook_token: '', operation: 'create', - events: [] + events: [], + pipeline: '', + pipeline_instance_vars: {} + } + }; + + expect(() => validate.config(config)).not.toThrow(); + + const fieldTree = field.split('.'); + config[fieldTree[0]][fieldTree[1]] = null; + + expect(() => validate.config(config)).toThrow(); + }); + }); + + it('checks fields with object constraint', () => { + const constrainedFields = [ + 'params.pipeline_instance_vars' + ]; + + constrainedFields.forEach(field => { + const config = { + source: { + github_api: '', + github_token: '' + }, + params: { + org: '', + repo: '', + resource_name: '', + webhook_token: '', + operation: 'create', + events: [], + pipeline: '', + pipeline_instance_vars: {} } }; From 2435bdf258b728346a5b4e4575d3899e8ac90bc8 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Thu, 17 Nov 2022 16:12:32 +0000 Subject: [PATCH 3/3] chore: update docs --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 549dcdb..27e6292 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ Create or delete a webhook using the configured parameters. webhook_token: your-token operation: create events: [push, pull_request] + pipeline: pipeline-name + pipeline_instance_vars: { + your_instance_var_name: value + } ``` - `org`: *Required.* Your github organization. @@ -60,6 +64,8 @@ Create or delete a webhook using the configured parameters. - `create` to create a new webhook. Updates existing webhook if your configuration differs from remote. - `delete` to delete an existing webhook. Outputs current timestamp on non-existing webhooks. - `events`: *Optional*. An array of [events](https://developer.github.com/webhooks/#events) which will trigger your webhook. Default: `push` +- `pipeline`: *Optional.* Defaults to the name of the pipeline executing the task +- `pipeline_instance_vars`: *Optional.* Instance vars to append to the webhook url. These help Concourse identify which [instance pipeline](https://concourse-ci.org/resources.html#schema.resource.webhook_token) it should invoke ## Example Include the github-webhook-resource in your pipeline.yml file