Creating and Executing Batch Changes via the GraphQL API

Overview

This document outlines how to create a Batch Change and execute it via the API.

 

Steps

Step 1: Get the Namespace ID

Every batch change is associated with a single "namespace".  The namespace can be either a user, an organization, or the global namespace.  Each of the APIs require you to provide the namespace ID, rather than the name.  So, we need to first convert the name to this ID.

G

R

A
P
H
Q
L
query NamespaceByName($name: String!) {
  namespaceByName(name: $name) {
    id
  }
}
V
A
R
I
A
B
L
E
S
{
  "name": "username"
}
E R
X E
A S
M P
P O
L N
E S
  E
  
{
  "data": {
    "namespaceByName": {
      "id": "VXNlcjoyNQ=="
    }
  }
}
 

 

Save the id returned ("VXNlcjoyNQ==" in the above example response) when running this query.  We'll refer to it below as ID-Namespace.

 

Step 2: Create an Empty Batch Spec

Next, create an empty batch change in the namespace.  We'll add the spec file in the next step.

 

G
R
A
P
H
Q
L
mutation CreateEmptyBatchChange($namespace: ID!, $name: String!) {
  createEmptyBatchChange(namespace: $namespace, name: $name) {
    id
    url
  }
}
V
A
R
I
A
B
L
E
S
{
  "namespace": "ID-Namespace",
  "name": "name-of-your-batch-change"
}
E R
X E
A S
M P
P O
L N
E S
  E
{
  "data": {
    "createEmptyBatchChange": {
      "id": "QmF0Y2hDaGFuZ2U6OTQ3",
      "url": "/users/$user/batch-changes/name-of-your-batch-change"
    }
  }
}

 

Save the ID returned ("QmF0Y2hDaGFuZ2U6OTQ3" in the above example response) when running this mutation.  We'll refer to it below as ID-BatchChange.

Once created, you can view it in the Drafts (https://your-sourcegraph-server/batch-changes?states=draft).

 

Step 3: Create Batch Change Specification

Create your Batch Change specification file in an editor of your choice. 

 

Step 4: Flatten the Batch Change Specification

Once you have the Batch Change specification file created you need to "flatten" it so it's a single line string with:

  1. Escape other special characters (i.e., \n => \\n)
  2. New lines converted to "\n"
  3. Quotes escaped with a backslash (i.e., " => \")

Example

Batch Change Specification File "Flattened"

name: test-from-api

description: Add Hello World to READMEs


# Find the first 100 search results for README files.

# This could yield less than 100 repos/workspaces if some repos contain multiple READMEs.

on:

  - repositoriesMatchingQuery: file:README.md count:1


# In each repository, run this command. Each repository's resulting diff is captured.

steps:

  - run: IFS=$'\n'; echo Hello World | tee -a $(find -name README.md)

    container: ubuntu:18.04


# Describe the changeset (e.g., GitHub pull request) you want for each repository.

changesetTemplate:

  title: Hello World

  body: My first batch change!

  commit:

    message: Append Hello World to all README.md files

  # Optional: Push the commit to a branch named after this batch change by default.

  branch: ${{ batch_change.name }}

"name: test-from-api\ndescription: Add Hello World to READMEs\n\n# Find the first 100 search results for README files.\n# This could yield less than 100 repos/workspaces if some repos contain multiple READMEs.\non:\n  - repositoriesMatchingQuery: file:README.md count:1\n\n# In each repository, run this command. Each repository's resulting diff is captured.\nsteps:\n  - run: IFS=$'\n'; echo Hello World | tee -a $(find -name README.md)\n    container: ubuntu:18.04\n\n# Describe the changeset (e.g., GitHub pull request) you want for each repository.\nchangesetTemplate:\n  title: Hello World\n  body: My first batch change!\n  commit:\n    message: Append Hello World to all README.md files\n  # Optional: Push the commit to a branch named after this batch change by default.\n  branch: ${{ batch_change.name }}"

 

Step 5: Upload the Spec File

Next, we'll upload the Batch Change specification file.  

 

G
R
A
P
H
Q
L
mutation CreateBatchSpecFromRaw($batchSpec: String!, $namespace: ID!, $batchChange: ID!) {
  createBatchSpecFromRaw(batchSpec: $batchSpec, namespace: $namespace, batchChange: $batchChange) {
    id
    state
  }
}
V
A
R
I
A
B
L
E
S
{
  "namespace": "ID-Namespace",
  "batchChange": "ID-BatchChange",
  "batchSpec": "SPEC_FLATTENED_FROM_STEP_4"
}
E R
X E
A S
M P
P O
L N
E S
  E
{

  "data": {

    "createBatchSpecFromRaw": {

      "id": "QmF0Y2hTcGVjOiJiYzIxNjRlOS02M2M0LTQyNjQtOWMwYS0yOTJmNDViOWFmNjYi",
      "state": "PENDING"
    }
  }
}

 

Save the ID returned from this mutation ("QmF0Y2hTcGVjOiJiYzIxNjRlOS02M2M0LTQyNjQtOWMwYS0yOTJmNDViOWFmNjYi" in the above example response).  We'll refer to it later as ID-BatchSpec.

 

NOTE: if you need to make a change after performing this step, use the replaceBatchChangeInput mutation.

 

Step 6: Execute the Batch Change

Finally, we'll execute the Batch Change.  

 

G
R
A
P
H
Q
L
mutation ExecuteBatchSpec($batchSpec: ID!) {
  executeBatchSpec(batchSpec: $batchSpec) {
    state
    id
    applyURL
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchSpec": "ID-BatchSpec"
}
E R
X E
A S
M P
P O
L N
E S
  E
{

  "data": {

    "executeBatchSpec": {
      "state": "QUEUED",
      "id": "QmF0Y2hTcGVjOiJiYzIxNjRlOS02M2M0LTQyNjQtOWMwYS0yOTJmNDViOWFmNjYi",
      "applyURL": null
    }
  }
}

 

Confirm that the returned state is QUEUED OR COMPLETED.  If you are interested in previewing the results, go to to the URL https://your-sourcegraph-server/<applyURL>

 

Step 7: Wait for Batch Spec Execution to Complete

If the state in Step 6 is QUEUED, run the following until it is COMPLETED

G
R
A
P
H
Q
L
query WorkspaceResolutionStatus($batchSpec: ID!) {
  node(id: $batchSpec) {
    ... on BatchSpec {
          workspaceResolution {
            startedAt
            state
            failureMessage          }
     }
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchSpec": "ID-BatchSpec"
}
E R
X E
A S
M P
P O
L N
E S
  E
{

  "data": {

    "node": {

      "workspaceResolution": {

        "startedAt": "2024-04-15T16:20:34Z",

        "state": "COMPLETED",

        "failureMessage": null

      },

    }

  }

}

 

Next, wait for the Batch Spec status to move to COMPLETED also

G
R
A
P
H
Q
L
query BatchChangeExecutionStatus($batchSpec: ID!) {
  node(id: $batchSpec) {
    ... on BatchSpec {
       id
       state
     }
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchSpec": "ID-BatchSpec"
}
E R
X E
A S
M P
P O
L N
E S
  E
{

  "data": {

    "node": {

        "id": "",

        "state": "COMPLETED",

      },

    }

  }
}

 

Step 8: Apply the Batch Change

Once you are ready to apply this Batch Change and create the PRs, run the following:

 

G
R
A
P
H
Q
L
mutation ApplyBatchChange($batchSpec: ID!) {
  applyBatchChange(batchSpec: $batchSpec) {
    id,
    name,
    state,
    url
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchSpec": "ID-BatchSpec"
}
E R
X E
A S
M P
P O
L N
E S
  E
{

  "data": {

    "applyBatchChange": {

      "id": "QmF0Y2hDaGFuZ2U6OTQ0",

     "name": "$user-test-from-api",

      "state": "OPEN",

     "url": "/users/$user/batch-changes/$user-test-from-api"

    }

  }
}

 

Step 9: (Optional) View Changeset Count and State

G
R
A
P
H
Q
L
query BatchChangeChangesets($batchChange: ID!) {

  node(id: $batchChange) {

    ... on BatchChange {

      changesets {

        totalCount

      }

      url

      state

    }

    }

  }
}
V
A
R
I
A
B
L
E
S
{
  "batchChange": "ID-BatchChange"
}
E R
X E
A S
M P
P O
L N
E S
  E
{
  "data": {
    "applyBatchChange": {
      "id": "QmF0Y2hDaGFuZ2U6OTQ0",
      "name": "$user-test-from-api",
      "state": "OPEN",
      "url": "/users/$user/batch-changes/$user-test-from-api"
    }
  }
}

 

Step 10: Publish

In order to publish changesets, you need to query the list of changeset IDs and publish by ID:

 

G
R
A
P
H
Q
L
query BatchChangeChangesets($batchChange: ID!) {

  node(id: $batchChange) {

    ... on BatchChange {

      changesets {

        totalCount

        nodes {
          id
          state
        }
        pageInfo {
          endCursor
          hasNextPage
        }
      }
      url
      state
    }
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchChange": "ID-BatchChange",
  "first": x,
  "after": null-or-value-from-previous-pageInfo-endCursor
}
E R
X E
A S
M P
P O
L N
E S
  E
{
  "data": {
    "node": {
      "changesets": {
        "totalCount": 1,
        "nodes": [
          {
            "id": "Q2hhbmdlc2V0OjEzODA4",
            "state": "FAILED"
          }
        ],
        "pageInfo": {
          "endCursor": null,
          "hasNextPage": false
        }
      },
      "url": "/users/$user/batch-changes/$user-test-from-api",
      "state": "OPEN"
    }
  }
}

 

You will need to iterate over the list of IDs in "nodes".  NOTE: if there is another page of results, be sure to call this query again with the "after" parameter set to the "endCursor" value.

 

Once you have the IDs to publish, call the publish API:

 

G
R
A
P
H
Q
L
mutation PublishChangesets($batchChange: ID!, $changesets: [ID!]!, $draft: Boolean) {

  publishChangesets(batchChange: $batchChange, changesets: $changesets, draft: $draft) {

    errors {

      changeset {
        id
        state
      }
      error
    }
    changesetCount
    progress
    state
    finishedAt
  }
}
V
A
R
I
A
B
L
E
S
{
  "batchChange": "ID-BatchChange",
  "changesets": list-of-change-set-IDs,
  "draft": true-or-false
}
E R
X E
A S
M P
P O
L N
E S
  E
 

 

Appendix

Retrieving PR links

G
R
A
P
H
Q
L
query BatchChangeChangesets($batchChange: ID!) {

    node(id: $batchChange) {

        ... on BatchChange {

            changesets {

                totalCount
                nodes {
                    id
                    state
                    ... on ExternalChangeset {
                        externalURL {
                            url
                        }
                    }
                }
                pageInfo {
                    endCursor
                    hasNextPage
                }
            }
            url
            state            
        }
    }
}
V
A
R
I
A
B
L
E
S
{
  "batchChange": "ID-BatchChange"
}
E R
X E
A S
M P
P O
L N
E S
  E
{
    "data": {
        "node": {
            "changesets": {
                "totalCount": 1,
                "nodes": [
                    {
                        "id": "Q2hhbmdlc2V0OjYzMDY=",
                        "state": "OPEN",
                        "externalURL": {
                            "url": https://bbucket/projects/PROJ1/repos/repo1/pull-requests/25
                        }
                    }
                ],
                "pageInfo": {
                    "endCursor": null,
                    "hasNextPage": false
                }
            },
            "url": "/users/abc/batch-changes/test-api",
            "state": "OPEN"
        }
    }
}

 

Check on Status

G
R
A
P
H
Q
L
query GetBatchChange($namespace: ID!, $name: String!) {
  batchChange(namespace: $namespace, name: $name) {
    url
    state
    name
    description
  }
}
V
A
R
I
A
B
L
E
S
{
  "namespace": "ID-Namespace",
  "name": "NAME"
}
Was this article helpful?
0 out of 0 found this helpful

Articles in this section