jira
This promotion step is only available in Kargo on the Akuity Platform, versions v1.6 and above.
The jira promotion step provides comprehensive integration with Jira, allowing you
to create, update, delete, and search for issues, manage comments, and track
promotion workflows. This is particularly useful for maintaining traceability between
your promotion processes and project management activities.
This promotion step supports various operations including issue management, comment handling, and status tracking, making it a powerful tool for promotion workflows that require coordination with project management systems.
Credentials Configuration
All Jira operations require proper authentication credentials stored in a Kubernetes
Secret.
| Name | Type | Required | Description | 
|---|---|---|---|
| credentials.secretName | string | Y | Name of the Secretcontaining the Jira credentials in the project namespace. | 
The referenced Secret should contain the following keys:
- domain: The domain of your Jira instance or Jira api (e.g.,- https://yourcompany.atlassian.net)
- username: Your Jira username or email
- password: Your Jira API token or password
Issue Management
Create Issue
Creates a new Jira issue with specified details.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| createIssue.projectKey | string | Y | The key of the Jira project where the issue will be created. | 
| createIssue.summary | string | Y | The summary or title of the issue. | 
| createIssue.description | string | N | The description of the issue. Supports markdown formatting. | 
| createIssue.adfDescription | object | N | ADF (Atlassian Document Format) content for complex formatting. Alternative to description. | 
| createIssue.issueType | string | N | The type of issue to create (e.g., 'Bug', 'Task', 'Story'). | 
| createIssue.assigneeEmail | string | N | Email of the user to assign the issue to. | 
| createIssue.labels | array | N | Labels to add to the issue for categorization. | 
| createIssue.customFields | object | N | Custom fields to set. Keys should match Jira custom field IDs. | 
| createIssue.issueAlias | string | N | Override for the freight metadata key used to reference the created issue id. Defaults to jira-issue-key. | 
Output
| Name | Type | Description | 
|---|---|---|
| key | string | The key/id of the created Jira issue (e.g., EXT-123). | 
Example
This example creates a new Jira issue to track a promotion, assigns it to a team member, and adds relevant labels.
steps:
- uses: jira
  as: create-promotion-issue
  config:
    credentials:
      secretName: jira-credentials
    createIssue:
      projectKey: PROMOTE
      summary: "Promote ${{ imageFrom(vars.imageRepo).Tag }} to ${{ ctx.stage }}"
      description: "Promoteing ${{ imageFrom(vars.imageRepo).RepoURL }}:${{ imageFrom(vars.imageRepo).Tag }} to ${{ ctx.stage }} environment. Promotion ID: ${{ ctx.promotion }}. Freight: ${{ ctx.targetFreight.name }}."
      issueType: Task
      assigneeEmail: devops@company.com
      labels:
      - promotion
      - "${{ ctx.stage }}"
      - "release-${{ imageFrom(vars.imageRepo).Tag }}"
# Use the created issue key in subsequent steps
- uses: jira
  config:
    credentials:
      secretName: jira-credentials
    updateIssue:
      issueKey: "${{ outputs['create-promotion-issue'].key }}"
      status: "IN PROGRESS"
Update Issue
Updates an existing Jira issue with new information.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| updateIssue.issueKey | string | Y | The Jira Issue Key (e.g., EXT-123). | 
| updateIssue.summary | string | N | Updated summary or title of the issue. | 
| updateIssue.description | string | N | Updated description. Supports markdown formatting. | 
| updateIssue.adfDescription | object | N | ADF content for complex formatting. Alternative to description. | 
| updateIssue.issueType | string | N | Updated issue type. | 
| updateIssue.assigneeEmail | string | N | Email of the user to assign the issue to. | 
| updateIssue.status | string | N | Status to set for the issue (e.g., 'IN PROGRESS', 'DONE'). | 
| updateIssue.addLabels | array | N | Labels to add to the issue. | 
| updateIssue.removeLabels | array | N | Labels to remove from the issue. | 
| updateIssue.customFields | object | N | Custom fields to update. | 
Output
This step does not produce any output.
Example
This example updates an existing issue's status and adds a comment with promotion details.
steps:
- uses: jira
  config:
    credentials:
      secretName: jira-credentials
    updateIssue:
      issueKey: PROMOTE-123
      status: "IN PROGRESS"
      summary: "Promote ${{ imageFrom(vars.imageRepo).Tag }} to ${{ ctx.stage }} - IN PROGRESS"
      addLabels:
      - promoting
      - "${{ ctx.stage }}-promotion"
      customFields:
        customfield_10000: "${{ ctx.stage }} Environment"
        customfield_10001: "${{ ctx.promotion }}"
Delete Issue
Deletes a Jira issue and, optionally, its subtasks.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| deleteIssue.issueKey | string | Y | The Jira Issue Key (e.g., EXT-123). | 
| deleteIssue.deleteSubtasks | boolean | N | If true, all subtasks will be deleted as well. | 
Output
This step does not produce any output.
Example
This example deletes a Jira issue and all its subtasks when a promotion fails.
steps:
# existing steps create issue and other promotion steps
# ....
# ....
# on failure cleanup logic
- as: on-failure-cleanup-issue
  uses: jira
  if: ${{ failure() }}
  config:
    credentials:
      secretName: jira-credentials
    deleteIssue:
      issueKey: "${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}"
      deleteSubtasks: true
Search Issues
Searches for Jira issues using JQL (Jira Query Language).
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| searchIssue.jql | string | Y | The JQL query to search for issues. | 
| searchIssue.expectMultiple | boolean | N | If true, expects multiple results and returns the first matching result. If false, expects single result and fails with >1 results. | 
| searchIssue.fields | array | N | List of fields to include in search results. | 
| searchIssue.expands | array | N | List of fields to expand in search results. | 
Output
| Name | Type | Description | 
|---|---|---|
| issue | object | The found Jira issue object containing all requested fields and expansions. | 
Example
This example searches for open promotion issues in a specific project and expects multiple results.
steps:
- uses: jira
  as: find-open-promotions
  config:
    credentials:
      secretName: jira-credentials
    searchIssue:
      jql: 'project = PROMOTE AND status != "Done" AND labels = "${{ ctx.stage }}-promotion" AND created >= -7d'
      expectMultiple: true
      fields:
      - summary
      - status
      - assignee
      - created
      expands:
      - changelog
# Use search results in subsequent steps to notify team
# Note: This is just an example of using search outputs and may not be syntactically valid
- uses: http
  config:
    method: POST
    url: https://slack.com/api/chat.postMessage
    headers:
    - name: Authorization
      value: "Bearer ${{ secret('slack-credentials').token }}"
    - name: Content-Type
      value: application/json
    body: |
      ${{ quote({
        "channel": "#promotions",
        "text": "Found issue" + outputs['find-open-promotions'].issue.key + " for " + ctx.stage + " environment"
      }) }}
Comment Management
Add Comment
Adds a comment to an existing Jira issue.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| commentOnIssue.issueKey | string | Y | The Jira Issue Key (e.g., EXT-123). | 
| commentOnIssue.body | string | N | Text content of the comment. | 
| commentOnIssue.adfBody | object | N | ADF content for complex formatting. Alternative to body. | 
Output
| Name | Type | Description | 
|---|---|---|
| commentID | string | The ID of the created comment that can be used for later operations. | 
Example
This example adds a comment to a Jira issue with promotion progress information.
steps:
- uses: jira
  as: add-progress-comment
  config:
    credentials:
      secretName: jira-credentials
    commentOnIssue:
      issueKey: "${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}"
      body: "Promotion started. Environment: ${{ ctx.stage }}. Image: ${{ imageFrom(vars.imageRepo).RepoURL }}:${{ imageFrom(vars.imageRepo).Tag }}. Promotion: ${{ ctx.promotion }}. Status: Promoting to ${{ ctx.stage }} environment..."
# Later use the comment ID if needed
- uses: jira
  config:
    credentials:
      secretName: jira-credentials
    deleteComment:
      issueKey: "${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}"
      commentID: "${{ quote(outputs['add-progress-comment'].commentID) }}"
Delete Comment
Removes a specific comment from a Jira issue.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| deleteComment.issueKey | string | Y | The Jira Issue Key (e.g., EXT-123). | 
| deleteComment.commentID | string | Y | The ID of the comment to delete. | 
Output
This step does not produce any output.
Example
This example deletes a specific comment from a Jira issue.
steps:
- uses: jira
  config:
    credentials:
      secretName: jira-credentials
    deleteComment:
      issueKey: "${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}"
      commentID: "${{ outputs['previous-comment-step'].commentID }}"
Status Tracking
Wait for Status
Waits for a Jira issue to reach a specific status before proceeding.
Configuration
| Name | Type | Required | Description | 
|---|---|---|---|
| waitForStatus.issueKey | string | Y | The Jira Issue Key (e.g., EXT-123). | 
| waitForStatus.expectedStatus | string | Y | The expected status to wait for (e.g., 'IN PROGRESS', 'DONE'). | 
Output
This step does not produce any output.
Example
This example waits for a change request issue to be approved before proceeding with promotion.
steps:
- uses: jira
  config:
    credentials:
      secretName: jira-credentials
    waitForStatus:
      issueKey: "${{ freightMetadata(ctx.targetFreight.name, 'change-request-key') }}"
      expectedStatus: "Approved"
# promotion steps continue after approval...
- uses: helm-template
  config:
    path: ./charts
    vars:
      imageTag: "${{ imageFrom(vars.imageRepo).Tag }}"
      environment: "${{ ctx.stage }}"
The Jira configuration supports setting issue and comment content using description
or body fields. These are plain text fields that do not support special formatting
such as Markdown. For rich formatting capabilities, use the ADF (Atlassian Document
Format) alternatives adfDescription or adfBody. For more information about ADF
structure and formatting, see the
Atlassian Document Format documentation.
Multi-Stage Workflow
The Jira promotion step automatically stores created issue keys in the Freight metadata, allowing subsequent stages to reference the same issue. This enables tracking a single Jira issue across multiple promotion stages.
Accessing Issue Keys from Freight Metadata
Use the freightMetadata()
template function to retrieve issue keys stored by previous stages:
# Access the default issue key
issueKey: ${{ freightMetadata(ctx.targetFreight.name)['jira-issue-key'] }}
# Access a custom issue key (when issueAlias was used during creation)
issueKey: ${{ freightMetadata(ctx.targetFreight.name)['my-custom-alias'] }}
Example
This comprehensive example demonstrates using the Jira promotion step across multiple stages in a promotion pipeline, tracking a single issue from its creation during testing through its final promotion to production:
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
  name: test
  namespace: kargo-proj
spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: nginx
    sources:
      direct: true
  promotionTemplate:
    spec:
      vars:
      - name: imageRepo
        value: public.ecr.aws/nginx/nginx
      steps:
      # Create initial promotion issue
      - as: create-promotion-issue
        uses: jira
        config:
          credentials:
            secretName: jira
          createIssue:
            projectKey: PROMOTE
            issueType: Task
            summary: "Promote Release ${{ imageFrom(vars.imageRepo).Tag }}"
            assigneeEmail: "devops@company.com"
            adfDescription:
              type: doc
              version: 1
              content:
              - type: paragraph
                content:
                - type: text
                  text: " "
              - type: heading
                attrs:
                  level: 3
                content:
                - type: text
                  text: "Automated promotion issue for release "
                - type: text
                  text: "${{ imageFrom(vars.imageRepo).Tag }}"
                  marks:
                  - type: code
              - type: paragraph
                content:
                - type: text
                  text: "Image:"
                  marks:
                  - type: strong
                - type: text
                  text: " "
                - type: text
                  text: "${{ imageFrom(vars.imageRepo).RepoURL }}:${{ imageFrom(vars.imageRepo).Tag }}"
                  marks:
                  - type: code
              - type: paragraph
                content:
                - type: text
                  text: "Project:"
                  marks:
                  - type: strong
                - type: text
                  text: " "
                - type: text
                  text: "${{ ctx.project }}"
                  marks:
                  - type: code
            labels:
            - "automated-promotion"
            - "env-${{ ctx.stage }}"
            - "release-${{ imageFrom(vars.imageRepo).Tag }}"
            - "project-${{ ctx.project }}"
      # Update the Argo CD Application directly. Not ideal for practical purposes.
      - as: update-app
        uses: argocd-update
        config:
          apps:
          - name: test-app
            namespace: argocd
            sources:
            - repoURL: https://github.com/company/app-config.git
              kustomize:
                images:
                - repoURL: public.ecr.aws/nginx/nginx
                  tag: ${{ imageFrom("public.ecr.aws/nginx/nginx").Tag }}
      # Add progress comment
      - as: comment-on-issue
        uses: jira
        config:
          credentials:
            secretName: jira
          commentOnIssue:
            issueKey: ${{ outputs['create-promotion-issue'].key }}
            body: "Release ${{ imageFrom(vars.imageRepo).Tag }} has been promoted to ${{ ctx.stage }} environment. Freight: ${{ ctx.targetFreight.name }}. Ready for testing."
      # Cleanup on failure
      - as: on-failure-cleanup-issue
        uses: jira
        if: ${{ failure() }}
        config:
          credentials:
            secretName: jira
          deleteIssue:
            issueKey: ${{ outputs['create-promotion-issue'].key }}
            deleteSubtasks: true
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
  name: uat
  namespace: kargo-proj
spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: nginx
    sources:
      stages:
        - test
  promotionTemplate:
    spec:
      vars:
      - name: imageRepo
        value: public.ecr.aws/nginx/nginx
      steps:
        # Wait for manual approval to proceed to UAT
      - as: wait-approval
        uses: jira
        config:
          credentials:
            secretName: jira
          waitForStatus:
            issueKey: ${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}
            expectedStatus: UAT
      # Update the Argo CD Application directly. Not ideal for practical purposes.
      - as: update-app
        uses: argocd-update
        config:
          apps:
            - name: uat-app
              namespace: argocd
              sources:
              - repoURL: https://github.com/company/app-config.git
                kustomize:
                  images:
                  - repoURL: ${{ vars.imageRepo }}
                    tag: ${{ imageFrom(vars.imageRepo).Tag }}
      # Update issue with UAT progress
      - as: comment-on-issue
        uses: jira
        config:
          credentials:
            secretName: jira
          commentOnIssue:
            issueKey: ${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}
            body: "Release ${{ imageFrom(vars.imageRepo).Tag }} has been promoted to ${{ ctx.stage }} environment. Promotion: ${{ ctx.promotion }}. Status: Promoteed and ready for uat validation."
      # Update environment labels
      - as: update-issue-labels
        uses: jira
        config:
          credentials:
            secretName: jira
          updateIssue:
            issueKey: ${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}
            removeLabels:
            - "env-test"
            addLabels:
            - "env-${{ ctx.stage }}"
            - "promotion-${{ ctx.promotion }}"
      # Cleanup comments on failure
      - as: on-failure-cleanup-comment
        uses: jira
        if: ${{ failure() && status('comment-on-issue') == 'Succeeded' }}
        config:
          credentials:
            secretName: jira
          deleteComment:
            issueKey: ${{ freightMetadata(ctx.targetFreight.name, 'jira-issue-key') }}
            commentID: ${{ quote(outputs['comment-on-issue'].commentID) }}
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
  name: prod
  namespace: kargo-proj
spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: nginx
    sources:
      stages:
        - uat
  promotionTemplate:
    spec:
      vars:
      - name: imageRepo
        value: public.ecr.aws/nginx/nginx
      steps:
      # Find the issue by searching for release label
      - as: search-issue
        uses: jira
        config:
          credentials:
            secretName: jira
          searchIssue:
            jql: "created <= 1d and labels IN (release-${{ imageFrom(vars.imageRepo).Tag }}) ORDER BY created DESC"
            expectMultiple: true
            fields:
              - key
      # Wait for final production approval
      - as: wait-approval
        uses: jira
        config:
          credentials:
            secretName: jira
          waitForStatus:
            issueKey: ${{ outputs['search-issue'].key }}
            expectedStatus: RELEASED
      # Update the Argo CD Application directly. Not ideal for practical purposes.
      - as: update-app
        uses: argocd-update
        config:
          apps:
          - name: prod-app
            namespace: argocd
            sources:
            - repoURL: https://github.com/company/app-config.git
              kustomize:
                images:
                - repoURL: public.ecr.aws/nginx/nginx
                  tag: ${{ imageFrom("public.ecr.aws/nginx/nginx").Tag }}
      # Add final completion comment
      - as: comment-on-issue
        uses: jira
        config:
          credentials:
            secretName: jira
          commentOnIssue:
            issueKey: ${{ outputs['search-issue'].key }}
            body: "Release ${{ imageFrom(vars.imageRepo).Tag }} has been successfully promoted to ${{ ctx.stage }} environment. promotion completed for promotion ${{ ctx.promotion }}. All systems operational and release is live!"
      # Update to production labels
      - as: update-issue-labels
        uses: jira
        config:
          credentials:
            secretName: jira
          updateIssue:
            issueKey: ${{ outputs['search-issue'].key }}
            removeLabels:
            - "env-stage"
            addLabels:
            - "env-${{ ctx.stage }}"
            - "released-${{ imageFrom(vars.imageRepo).Tag }}"
            - "promotion-${{ ctx.promotion }}"
      # Cleanup on failure
      - as: on-failure-cleanup-comment
        uses: jira
        if: ${{ failure() && status('comment-on-issue') == 'Succeeded' }}
        config:
          credentials:
            secretName: jira
          deleteComment:
            issueKey: ${{ outputs['search-issue'].key }}
            commentID: ${{ quote(outputs['comment-on-issue'].commentID) }}
This multi-stage workflow demonstrates:
- Issue Creation: The teststage creates a comprehensive Jira issue with ADF formatting
- Freight Metadata: The issue key is automatically stored in freight metadata for later stages
- Status Tracking: Each stage waits for specific approval statuses before proceeding
- Progressive Updates: Labels and comments are updated as the release moves through environments
- Error Handling: Cleanup steps run on failures to maintain clean state
- Search Functionality: The prodstage demonstrates finding issues by label when freight metadata isn't available