GitHub Action- Rover Lint Command

Aim
To set up a GitHub Action that automatically runs the
rover lint
command whenever any.graphql
file is added or modified in the repository. This ensures that all GraphQL schema and operation files are validated for correctness and style compliance as part of the development workflow.Command:
rover subgraph lint --name "$SUBGRAPH_NAME" --schema "$file" $GRAPH_REF
Steps
We need to add the YAML file in below path-
.repo-root/
└──
.github/
└──
workflows/
└──
graphql-lint.yml
✅ (This is where your workflow file goes)Please find below the YAML file, which I’ve added-
name: Lint GraphQL Subgraphs
on:
push:
paths:
- '**/*.graphql'
pull_request:
paths:
- '**/*.graphql'
jobs:
lint-subgraphs:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout the repo so we can access the files
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# Step 2: Install Rover CLI (official tool from Apollo)
- name: Install Rover CLI
run: |
curl -sSL https://rover.apollo.dev/nix/latest | sh
echo "$HOME/.rover/bin" >> $GITHUB_PATH
# Step 3: Find all changed .graphql files in this push/PR
- name: Get list of changed .graphql files
id: changed-files
run: |
echo "Finding changed .graphql files..."
# List changed files from the last commit or PR
CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.graphql$' || true)
echo "Changed files: $CHANGED_FILES"
# Save the list as an output (newline separated)
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Step 4: Lint each changed .graphql file using rover subgraph lint
- name: Lint changed subgraph schemas with Rover
if: steps.changed-files.outputs.files != ''
env:
APOLLO_KEY: ${{ secrets.APOLLO_KEY }}
GRAPH_REF: "sfcc-pro-7uddgfq@current" # Change this to your actual graph ref
run: |
set -e
echo "Linting subgraphs..."
while IFS='' read -r file; do
echo "Linting file: $file"
# Extract subgraph name from filename, example: products-schema.graphql -> products
# Adjust this if your naming differs
BASENAME=$(basename "$file")
SUBGRAPH_NAME=${BASENAME%.graphql}
echo "Running rover subgraph lint for subgraph: $SUBGRAPH_NAME"
rover subgraph lint --name "$SUBGRAPH_NAME" --schema "$file" $GRAPH_REF
done <<< "${{ steps.changed-files.outputs.files }}"
Detailed Explanation of YAML File
name: Lint GraphQL Subgraphs
name:
The name of the workflow. This shows up in GitHub under the "Actions" tab.
➤ Here, it's labeled as "Lint GraphQL Subgraphs".
on:
push:
paths:
- '**/*.graphql'
pull_request:
paths:
- '**/*.graphql'
on:
Defines the trigger — when the workflow should run.
push:
Runs the workflow when someone pushes code to the repository.pull_request:
Runs the workflow when someone opens or updates a pull request.paths:
Specifies the files to watch. The workflow will only trigger if these files are affected.'**/*.graphql'
This is a glob pattern.**/
means any folder, any level deep*.graphql
means any file ending in.graphql
➤ So this matches all GraphQL files anywhere in the repo.
jobs:
lint-subgraphs:
runs-on: ubuntu-latest
jobs:
Defines a list of tasks (jobs) that will run.
lint-subgraphs:
A custom name for this specific job. You can name it anything.runs-on:
This tells GitHub what operating system to run the job on.
➤ Here, we use a fresh Ubuntu Linux virtual machine.
steps:
- This begins the list of steps inside the job. Steps are executed one by one.
# Step 1: Checkout the repo so we can access the files
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name:
Describes what this step does. This label appears in the GitHub Actions UI.uses:
This uses an existing GitHub Action (actions/checkout
) from the GitHub Marketplace.@v4
Version number of the action. It's best practice to use pinned versions.with:
Provides additional inputs/arguments for the action.fetch-depth: 0
Tells Git to clone the full commit history (needed for comparing commits to find changed files).
# Step 2: Install Rover CLI (official tool from Apollo)
- name: Install Rover CLI
run: |
curl -sSL https://rover.apollo.dev/nix/latest | sh
echo "$HOME/.rover/bin" >> $GITHUB_PATH
run:
Executes shell commands.
The|
means you're writing a multi-line shell script.curl -sSL URL | sh
Downloads and installs the Rover CLI.echo "$HOME/.rover/bin" >> $GITHUB_PATH
Adds the Rover CLI path to the systemPATH
, so it can be used in later steps.It adds the
"$HOME/.rover/bin"
directory to thePATH
environment variable in GitHub Actions, making executables in that directory accessible in subsequent steps of the workflow.
# Step 3: Find all changed .graphql files in this push/PR
- name: Get list of changed .graphql files
id: changed-files
run: |
echo "Finding changed .graphql files..."
# List changed files from the last commit or PR
CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.graphql$' || true)
echo "Changed files: $CHANGED_FILES"
# Save the list as an output (newline separated)
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
1. - name: Get list of changed .graphql files
This defines a step in your GitHub Actions workflow, and it gives the step a name: "Get list of changed .graphql files". The name is just for reference in your workflow logs so you know what the step is doing.
2. id: changed-files
- This gives the step an ID (
changed-files
). The ID allows you to refer to the outputs of this step later in the workflow. For example, you can use${{ steps.changed-files.outputs.files }}
to access the output from this step in later steps.
3. run: |
run
is used to specify the shell script (or commands) that GitHub Actions will execute during this step.The
|
symbol indicates that the commands below will be executed in a multi-line format (i.e., the script continues on the following lines).
4. echo "Finding changed .graphql files..."
- This simply prints the message "Finding changed .graphql files..." to the log, so you can see in the workflow logs that the process has started.
5. CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep '\.graphql$' || true)
git diff --name-only ${{ github.event.before }} ${{ github.sha }}
:This command gets the list of files that were changed between two commits or git references:
${{ github.event.before }}
(the commit hash before the event, like the last commit or PR) and${{ github.sha }}
(the commit hash of the current commit).git diff --name-only
lists only the names of the files that were changed, not their content.
| grep '\.graphql$'
:The pipe (
|
) sends the list of changed files to thegrep
command.grep '\.graphql$'
filters the list to only include files that end with.graphql
, essentially looking for GraphQL schema/query files.
|| true
:- This ensures that even if there are no
.graphql
files (orgrep
doesn’t find any matches), the command will not fail. It forces the command to succeed by runningtrue
ifgrep
finds nothing.
- This ensures that even if there are no
CHANGED_FILES=...
:- The list of changed
.graphql
files (if any) is saved in the variableCHANGED_FILES
.
- The list of changed
6. echo "Changed files: $CHANGED_FILES"
- This command prints out the list of changed
.graphql
files to the log so you can see which files were detected as changed.
7. Saving the list as an output:
The next few lines are about saving the list of changed .graphql
files as an output for use in later steps of the workflow.
echo "files<<EOF" >> $GITHUB_OUTPUT
:This syntax is used to create a named output. In this case, it’s setting the output key as
files
.<<EOF
is a special syntax in shell that creates a "here document," which lets you append multiline content to a file.
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
:- This appends the list of changed
.graphql
files (stored in theCHANGED_FILES
variable) to the output file, which is$GITHUB_OUTPUT
.$GITHUB_OUTPUT
is a special file used by GitHub Actions to store output values from a step.
- This appends the list of changed
echo "EOF" >> $GITHUB_OUTPUT
:- This marks the end of the multiline output, completing the "here document" that was started with
<<EOF
.
- This marks the end of the multiline output, completing the "here document" that was started with
# Step 4: Lint each changed .graphql file using rover subgraph lint
- name: Lint changed subgraph schemas with Rover
if: steps.changed-files.outputs.files != ''
env:
APOLLO_KEY: ${{ secrets.APOLLO_KEY }}
GRAPH_REF: "sfcc-pro-7uddgfq@current" # Change this to your actual graph ref
run: |
set -e
echo "Linting subgraphs..."
while IFS='' read -r file; do
echo "Linting file: $file"
# Extract subgraph name from filename, example: products-schema.graphql -> products
# Adjust this if your naming differs
BASENAME=$(basename "$file")
SUBGRAPH_NAME=${BASENAME%.graphql}
echo "Running rover subgraph lint for subgraph: $SUBGRAPH_NAME"
rover subgraph lint --name "$SUBGRAPH_NAME" --schema "$file" $GRAPH_REF
done <<< "${{ steps.changed-files.outputs.files }}"
1. run: |
:
- This defines a multi-line shell script in GitHub Actions, specifying the commands that will be executed in this step. The
|
symbol allows the script to span multiple lines.
2. set -e
:
set -e
is a shell option that tells the shell to exit immediately if any command fails (i.e., if any command returns a non-zero exit code). This is useful in CI/CD pipelines to stop further execution when a failure occurs. It ensures that if there's an error in the linting process, the entire job stops.
3. echo "Linting subgraphs..."
:
- This prints the message
"Linting subgraphs..."
to the console. It gives feedback in the workflow logs, indicating that the linting process has started.
4. while IFS='' read -r file; do
:
while
: This starts a loop that reads each line of input one at a time.IFS=''
: This sets the Internal Field Separator (IFS) to an empty string, which prevents any word splitting. By default,IFS
is set to space, tab, and newline, so setting it to''
makes sure that the lines are read exactly as they are without any unwanted splitting.read -r file
: Theread
command reads input line by line and stores each line in the variablefile
. The-r
flag prevents backslashes from being treated as escape characters, ensuring the input is read literally.done
: This marks the end of thewhile
loop.
The loop will continue until all lines of input are read, which, in this case, are the files listed in the ${{ steps.changed-files.outputs.files }}
output (explained later).
5. echo "Linting file: $file"
:
- This prints the message
"Linting file: $file"
, where$file
is the current line being processed. This gives feedback on the file that is currently being linted.
6. BASENAME=$(basename "$file")
:
basename "$file"
: Thebasename
command strips the directory path from a filename, leaving just the filename itself.- For example, if
$file
is/path/to/products-schema.graphql
, thebasename
command will returnproducts-schema.graphql
.
- For example, if
The result is stored in the variable
BASENAME
.
7. SUBGRAPH_NAME=${BASENAME%.graphql}
:
This line extracts the subgraph name from the
BASENAME
variable, assuming the file follows a certain naming convention.${BASENAME%.graphql}
: This syntax removes the.graphql
suffix from theBASENAME
variable.- For example, if
BASENAME
isproducts-schema.graphql
, this will result inproducts-schema
.
- For example, if
The subgraph name is then saved into the variable
SUBGRAPH_NAME
.
8. echo "Running rover subgraph lint for subgraph: $SUBGRAPH_NAME"
:
This prints a message indicating that the linting process is being run for the specific subgraph.
- For example, if
$SUBGRAPH_NAME
isproducts
, the message would be:"Running rover subgraph lint for subgraph: products"
.
- For example, if
9. rover subgraph lint --name "$SUBGRAPH_NAME" --schema "$file" $GRAPH_REF
:
- This command runs Rover, which is a command-line tool for interacting with Apollo Studio’s subgraphs. The
subgraph lint
command checks the given schema file for any errors or issues.
Let's break down the arguments:
--name "$SUBGRAPH_NAME"
: This specifies the name of the subgraph being linted. It's set to the value stored in theSUBGRAPH_NAME
variable (e.g.,products
).--schema "$file"
: This specifies the schema file to lint. The$file
variable contains the path to the schema file being processed.$GRAPH_REF
: This variable contains a reference to the graph, which is typically something like the version or ID of the schema in Apollo Studio. It's passed as an argument to specify which graph to lint against. This could be set earlier in the workflow or passed as an environment variable.
10. done <<< "${{ steps.changed-files.outputs.files }}"
:
<<<
: This is a here-string in bash, which allows the output of a variable or command to be passed directly as input to a command. In this case, the output of${{ steps.changed-files.outputs.files }}
is passed to thewhile
loop.${{ steps.changed-files.outputs.files }}
: This refers to the output of a previous step in the GitHub Actions workflow. In this case, it’s the list of changed files from the stepchanged-files
. The files are expected to be newline-separated.- This is the list of
.graphql
files that were changed in the last commit or pull request, and the loop processes each of them one by one.
- This is the list of
Special Symbols and Syntax
- | Starts a list item (step, path, etc.) |
: | Separates keys and values |
${{ }} | GitHub Actions expression syntax (used to inject dynamic values) |
>> | Appends to a file or output |
EOF | Marker for multi-line variable content |
!= | “Not equal” in GitHub expressions |
<< | Start of a multi-line value block (used in output or HEREDOC) |
Visual Representation of the flow
Important Points Related to YAML File
fetch-depth
:
In Git, the fetch-depth
parameter is used to limit the history when you clone or fetch a repository. This is especially common in CI/CD pipelines (like GitHub Actions, GitLab CI, etc.) to speed up build times by avoiding a full repository history download.
Value | Meaning |
1 | Shallow clone. Only fetch the latest commit (no history). |
>1 | Fetch the latest N commits. Example: fetch-depth: 10 gets last 10. |
0 or omitted | Full clone. All history and commits are fetched. |
NOTE: When using the actions/checkout action
without explicitly setting fetch-depth
, the default value is 1
.
curl -sSL
:
In the curl
command, the -sSL
flag is a combination of several options:
-s
stands for silent. It makescurl
run without showing progress or error messages, which can be useful when you want a clean output (like when scripting).-S
stands for show error. It tellscurl
to show an error message if something goes wrong, even when the-s
(silent) option is used. Without this,curl
would fail silently.-L
stands for location. It tellscurl
to follow any redirects. If the URL responds with a redirect (like a 301 or 302),curl
will automatically follow the new URL.
So, curl -sSL
means "run silently, but show errors if they occur, and follow redirects."
| sh
:
The | sh
part of a command means that the output of the command before the pipe (|
) is being passed as input to the sh
command, which is a shell interpreter.
In simpler terms:
The
|
is a pipe, which takes the output of the command on the left side and sends it as input to the command on the right side.sh
is a shell command (usually the Bourne shell) that executes shell commands.
| tr -d '\n'
:
The tr -d '\n'
command is used to delete newline characters (\n
) from the input text.
ls -ltrah
:
ls
This is the basic command to list files and directories in the current directory (or any specified directory).
-l
(long listing format)
-t
(sort by modification time)
-r
(reverse order)
-a
(show hidden files)
-h
(human-readable file sizes)
tree .
:
The command tree .
is used to display the directory structure in a tree-like format, starting from the current directory (.
).
Subscribe to my newsletter
Read articles from Aditya Dev Shrivastava directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
