Your browser was unable to load all of the resources. They may have been blocked by your firewall, proxy or browser configuration.
Press Ctrl+F5 or Ctrl+Shift+R to have your browser try again.

Help with QuickBuild webhook #4528

zoywiki ·

Hello,

I have an application that I want to send custom webhook events from quickbuild to the application.

In Settings > QuickBuild > Webhooks, I have defined:

Payload URL: http://localhost:3000
Secret: MySecret
Payload:
{
"event_type": ??????, 
"data": "some data",
"var": ${vars.getValue("MyVar")}
}
Media type: application/json
Active: yes

How do I specify the event type attribute for the data in quickbuild? All events for build started, build finished, build recommended, build un-recommended all appear to be identical to the receiving application.

  • replies 8
  • views 674
  • stars 0
steveluo ADMIN ·

Hi@zoywiki

Event type in payload is whatever you want as it is input by you. If you mean event type for this webhook from QuickBuild, it is not in payload data, instead, QuickBuild uses a header: X-QB-EventType which you are not able to customize and includes 4 event types:

BUILD_STARTED,
BUILD_FINISHED,
BUILD_RECOMMENDED,
BUILD_UNRECOMMENDED

But for now, you are not able to select which event you want to send the payload. I have filed a ticket to improve this so user can choose which events to trigger the build:

steveluo ADMIN ·
zoywiki ·

Is this exposed now through selector or through groovy script (or both)?

steveluo ADMIN ·

Hi@zoywiki

Yes, this has been fixed in QB 13.0.36. It's able to use script now. For example, if you only want to trigger webhook when build is finished, you can use below script in Active field:

${eventType == "BUILD_FINISHED"}

If you want to send build id and some variables in payload data, you can use below script in Payload field:

{
  "buildId": "${build.id}",
  "vars": "${vars.get("my_variable")}"
}
zoywiki ·

can I also make it part of the payload as well?

Or is this only defined in the active field?

Edit: found out it does work. I am doing something like this to call a script that generates a JSON object for return:

${groovy:  
  
import com.pmease.quickbuild.model.*; 

// setup a groovy shell and data bindings
def sharedData = new Binding();  
def shell = new GroovyShell(sharedData);  
  
// add classes needed for the groovy script
sharedData.setProperty('current', current);  
sharedData.setProperty('system', system);  
sharedData.setProperty('util', util);  
sharedData.setProperty('user', user);  
sharedData.setProperty('configuration', configuration);  
sharedData.setProperty('build', build);  
sharedData.setProperty('request', request);  
sharedData.setProperty('vars', vars);  
sharedData.setProperty('repositories', repositories);  
sharedData.setProperty('logger', logger);

// web-hook event type
sharedData.setProperty('eventType', eventType );

// get script text
def groovy_root = vars.get("groovyRoot").getValue();  
def script = new File( groovy_root + "/my_script.groovy").text;  
  
//execute script
return shell.evaluate( script );
}

Then I have a script like this:

import groovy.json.JsonBuilder;
import com.pmease.quickbuild.plugin.scm.perforce.*;

def map = [:];

// event type setup
map["event_type"] = eventType;

// build variables
map["build_id"] = build.getId();
map["build_status"] = build.getStatus().name();
map["build_version"] = build.getVersion();
map["build_url"] = build.getUrl();
map["build_log"] = system.getUrl() + "/download/" + build.getId() + "/log";

// get perforce changes
def start_cl = new PerforceRevision("111"); // temporarily hardcoded, make sure its a string
def end_cl = new PerforceRevision("222"); // temporarily hardcoded, make sure its a string
def src_repo = repositories.get( "My Repo" ); // temporarily hardcoded
def changes = src_repo.getChangesBetween( start_cl, end_cl );

map["changes"] = changes;

return new JsonBuilder( map );

Added this code just so folks that stumble upon this can see why you might want to do this.

zoywiki ·

@steveluo

I found a couple wrinkles or something rather interesting behavior to determine when the code for this might be executed.

So if I have a web hook for BUILD_STARTED and BUILD_FINISHED, and in my payload i do something weird like logger.debug( "hello world" );

The BUILD_STARTED runs right away as expected and you see a debug message for "hello world" at the top of you logfile.

However if you then expect to get a payload for BUILD_FINISHED, then you will not see at the end of the log "hello world". I would expect it to be at the end of the logfile in this case or at the very least in the system log.

This leads me to believe the payload section may not be evaluated separately for each condition. So I setup a second disabled web hook which was set to false. To my surprise the output was seen twice at the beginning of the logfile. Just to confirm i did logger.debug( "Hello build started" ); and logger.debug( "Hello build disabled" ); and saw both messages in the log.

I think it would be more useful if the payload were evaluated when it is needed instead of ahead of at build request time. I realize this may not be a trivial thing to modify, but the behavior seems a bit awkward and may lead to some unintended results. If I need to put a formal ticket in let me know.

This feature has been particularly fun to work with!

steveluo ADMIN ·

@zoywiki

Please let me know how you log the message in your payload script.

zoywiki ·

payload:
${groovy:

logger.info( "test" );
}