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 P H Q L |
query NamespaceByName($name: String!) { namespaceByName(name: $name) { id } } |
V A R I A 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 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:
- Escape other special characters (i.e., \n => \\n)
- New lines converted to "\n"
- 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 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 |
{ "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 L E S |
{ "batchSpec": "ID-BatchSpec" } |
E R X E A S M P P O L N E S E |
{ "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 L E S |
{ "batchSpec": "ID-BatchSpec" } |
E R X E A S M P P O L N E S E |
{ |
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 L E S |
{ "batchSpec": "ID-BatchSpec" } |
E R X E A S M P P O L N E S E |
{ } |
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 L E S |
{ "batchSpec": "ID-BatchSpec" } |
E R X E A S M P P O L N E S E |
{ } |
Step 9: (Optional) View Changeset Count and State
G R A P H Q L |
query BatchChangeChangesets($batchChange: ID!) { } |
V A R I A 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!) { id state } pageInfo { endCursor hasNextPage } } url state } } } |
V A R I A 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) { id state } error } changesetCount progress state finishedAt } } |
V A R I A 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!) { nodes { id state ... on ExternalChangeset { externalURL { url } } } pageInfo { endCursor hasNextPage } } url state } } } |
V A R I A 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 L E S |
{ "namespace": "ID-Namespace", "name": "NAME" } |