Template Content
Scripts
About the template
About ScriptRunner Connect
What is ScriptRunner Connect?
Can I try it out for free?
Yes. ScriptRunner Connect comes with a forever free tier.
Can I customize the integration logic?
Absolutely. The main value proposition of ScriptRunner Connect is that you'll get full access to the code that is powering the integration, which means you can make any changes to the the integration logic yourself.
Can I change the integration to communicate with additional apps?
Yes. Since ScriptRunner Connect specializes in enabling complex integrations, you can easily change the integration logic to connect to as many additional apps as you need, no limitations.
What if I don't feel comfortable making changes to the code?
First you can try out our AI assistant which can help you understand what the code does, and also help you make changes to the code. Alternatively you can hire our professionals to make the changes you need or build new integrations from scratch.
Do I have to host it myself?
No. ScriptRunner Connect is a fully managed SaaS (Software-as-a-Service) product.
What about security?
ScriptRunner Connect is ISO 27001 and SOC 2 certified. Learn more about our security.
Template Content
Scripts
This integration between GitLab and Jira Cloud ensures merge requests are aligned with project management protocols. It listens to merge request events from GitLab and checks the status of the associated Jira ticket. If the ticket is not approved, the merge request is blocked. Additionally, the script listens to push events from GitLab and updates the corresponding Jira ticket with the branch name, ensuring seamless synchronization between code changes and project tracking.
Setup the API connection with the Gitlab and Jira Cloud connectors.
Create a custom field in Jira Cloud in order to store the Gitlab branch name.
In ScriptRunner Connect, navigate to Parameters and ensure the custom field name matches what was setup in the previous step.
Following the event listener setup instructions to create the webhooks in Gitlab.
Push a change into a Gitlab repository or create a merge request to trigger the scripts.
Please note that the branch name is expected to be in the format: JIRA-TICKET/BRANCH-NAME
. If you wish to use a different format, update the getJiraTicketKey
function in the Utils
script.
import { GetIssueResponseOK } from '@managed-api/jira-cloud-v3-core/types/issue';
import GitLab from './api/gitlab';
import JiraCloud from './api/jira/cloud';
import { MergeRequestEvent } from '@sr-connect/gitlab/events';
import { createParagraphInADF, getJiraTicketKey } from './Utils';
/**
* Entry point to Merge request events event
*
* @param event Object that holds Merge request events event data
* @param context Object that holds function invocation context data
*/
export default async function(event: MergeRequestEvent, context: Context<EV>): Promise<void> {
if (context.triggerType === 'MANUAL') {
console.error('This script is designed to be triggered externally. If you need to trigger the script manually, consider emulating the event with a test payload instead.');
return;
}
//Add a Jira comment with the merge request description on a successful merge
if (event.object_attributes.action === 'merge' && event.object_attributes.state === 'merged') {
const { description, source_branch } = event.object_attributes;
const jiraTicket = getJiraTicketKey(source_branch);
console.log(`Going to add a comment on the Jira ticket ${jiraTicket} with the merge description: ${description}`);
const issue = await JiraCloud.Issue.getIssue({
issueIdOrKey: jiraTicket,
});
await JiraCloud.Issue.Comment.addComment({
issueIdOrKey: issue.key,
body: {
body: createParagraphInADF(description)
}
});
return;
}
//When a merge request is opened or updated, block the merge if certain conditions are not met
if (event.object_attributes.state === 'opened') {
const { source_branch } = event.object_attributes;
const jiraTicket = getJiraTicketKey(source_branch);
let issue: GetIssueResponseOK;
try {
issue = await JiraCloud.Issue.getIssue({
issueIdOrKey: jiraTicket,
});
} catch(error) {
console.error('Error whilst fetching ticket', error);
throw error;
}
const status = issue ? issue.fields.status.name : undefined;
//Block the merge request if the Jira ticket is not approved or not found
if (!status || status !== 'Approved') {
console.warn('Ticket is not approved so will create a thread: ', { ticket: jiraTicket, merge_request_id: event.object_attributes.iid })
await GitLab.fetch(`/api/v4/projects/${event.project.id}/merge_requests/${event.object_attributes.iid}/discussions`, {
method: 'POST',
body: JSON.stringify({
body: issue ? `The Jira ticket ${jiraTicket} is not approved.` : `${jiraTicket} not found in Jira.`
})
})
return;
}
} else {
console.warn('Ignoring the merge request event because status is not opened');
}
}
import { getBranchCustomField, getJiraTicketKey } from './Utils';
import JiraCloud from './api/jira/cloud';
import { PushEvent } from '@sr-connect/gitlab/events';
/**
* Entry point to Push event
*
* @param event Object that holds Push event data
* @param context Object that holds function invocation context data
*/
export default async function(event: PushEvent, context: Context<EV>): Promise<void> {
if (context.triggerType === 'MANUAL') {
console.error('This script is designed to be triggered externally. If you need to trigger the script manually, consider emulating the event with a test payload instead.');
return;
}
const branchCustomField = await getBranchCustomField(context.environment.vars.BRANCH_NAME_CUSTOM_FIELD_NAME);
const { ref } = event;
if (ref === 'refs/heads/main') {
console.warn('Ignoring push events to the main branch');
return;
}
//Assuming the branch is in the format: JIRA-TICKET/BRANCH-NAME
const branchName = ref.split('refs/heads/')[1];
const jiraTicket = getJiraTicketKey(branchName);
//Get the branch name of the Jira ticket
const issue = await JiraCloud.Issue.getIssue({
issueIdOrKey: jiraTicket,
fields: [branchCustomField]
});
if (issue.fields[branchCustomField] !== branchName) {
await JiraCloud.Issue.editIssue({
issueIdOrKey: issue.key,
body: {
update: {
[branchCustomField]: [{
set: branchName
}]
}
}
})
}
}
import JiraCloud from './api/jira/cloud';
export function getJiraTicketKey(branchName: string): string {
//Assuming the branch is in the format: JIRA-TICKET/BRANCH-NAME
return branchName.split('/')[0];
}
export async function getBranchCustomField(fieldName: string): Promise<string> {
// Get all custom issue fields in Jira Cloud
const customFields = await JiraCloud.Issue.Field.getFields();
console.log(customFields)
// Find the custom field where the Gitlab branch name is stored
const customField = customFields.find(f => f.name === fieldName);
// If no such field is found, throw an error
if (!customField?.key) {
throw Error(`Custom field ${fieldName} not found in Jira Cloud`);
}
return customField.key;
}
export function createParagraphInADF(text: string) {
return {
type: 'doc' as const,
version: 1 as const,
content: [
{
type: 'paragraph' as const,
content: [
{
type: 'text' as const,
text
}
]
}
]
};
};
© 2025 ScriptRunner · Terms and Conditions · Privacy Policy · Legal Notice