Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool

GitHub Actions for Automated JSON Formatting and Validation

In modern software development, configuration files, data payloads, and API responses often rely heavily on JSON. Maintaining consistency in JSON formatting and ensuring its validity across a project is crucial for readability, preventing errors, and smooth collaboration, especially in teams. Manually checking and formatting JSON before committing can be tedious and error-prone. This is where automation comes in, and GitHub Actions provide a powerful way to enforce these standards directly within your version control workflow.

Why Automate with GitHub Actions?

GitHub Actions allow you to automate tasks in response to GitHub events like pushes, pull requests, and merges. By integrating JSON formatting and validation into your CI/CD pipeline, you gain several key benefits:

  • Consistency: Ensure all JSON files adhere to a predefined style guide (e.g., indentation, spacing, key ordering if using a powerful formatter).
  • Early Error Detection: Catch syntax errors, invalid structures, or incorrect formatting before they are merged into the main branch, saving debugging time later.
  • Improved Code Review: Code reviews can focus on logic and content rather than formatting issues.
  • Reduced Cognitive Load: Developers don't have to remember to manually run formatting/validation tools.

Setting Up Your Workflow

GitHub Actions workflows are defined in YAML files located in the `.github/workflows` directory at the root of your repository. A workflow consists of one or more jobs, and each job has a series of steps that are executed on a runner (a virtual machine hosted by GitHub or self-hosted).

For our purpose, we'll create a simple workflow that runs whenever changes are pushed or a pull request is opened targeting your main branch (e.g., `main` or `master`). The workflow will check out the code, set up the necessary environment, and then run formatting and validation tools.

Workflow Trigger

You'll typically want to run JSON checks on `push` and `pull_request` events.

on:
  push:
    branches: [ main, master ] # Adjust branches as needed
  pull_request:
    branches: [ main, master ] # Adjust branches as needed

Jobs and Steps

A single job is usually sufficient for this task. Inside the job, you'll define steps using standard GitHub Actions actions or running command-line scripts.

Example 1: Using jsonlint

jsonlint is a simple command-line tool for validating and formatting JSON. It's a good choice for basic syntax checking.

First, ensure you have Node.js set up in your workflow, as jsonlint is an npm package.

.github/workflows/json-check.yml

name: JSON Check

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

jobs:
  validate-and-format-check:
    runs-on: ubuntu-latest # Use a standard runner

    steps:
    - name: Checkout code
      uses: actions/checkout@v4 # Action to get your code

    - name: Setup Node.js
      uses: actions/setup-node@v4 # Action to set up Node.js
      with:
        node-version: '20' # Specify Node.js version

    - name: Install jsonlint
      run: npm install -g jsonlint # Install jsonlint globally

    - name: Validate JSON files
      run: |
        find . -name '*.json' -print0 | xargs -0 jsonlint -q # Find all JSON and validate quietly
      # The -q flag makes jsonlint output nothing on success, and error messages on failure.
      # xargs -0 handles filenames with spaces or special characters.

    - name: Check JSON Formatting
      run: |
        find . -name '*.json' -print0 | xargs -0 -I {} sh -c 'jsonlint -f "{}" | diff - "{}"' # Format and diff
      # This formats each file and compares it to the original.
      # If diff finds differences (exit code 1), the command fails.
      # -I {} tells xargs to replace {} with the filename in the command.
      # sh -c is used to execute multiple commands (jsonlint and diff) for each file found by find.
      # Note: This check might be tricky with empty files or specific jsonlint output nuances.
      # A common alternative is using a dedicated tool like Prettier (see next example).

In this example, the workflow first checks out the code and sets up Node.js. It then installs jsonlint. The "Validate JSON files" step uses find and xargs to run jsonlint -q on every .json file found. If any file is invalid, jsonlint -q exits with a non-zero status, causing the step and thus the job to fail . The "Check JSON Formatting" step attempts to format each file and then uses diff to compare the formatted output to the original file. If there's a difference, it means the file wasn't correctly formatted, and the step fails . If all checks pass, the job completes successfully .

Example 2: Using Prettier

Prettier is a widely used opinionated code formatter that supports JSON among many other languages. It's generally more robust and configurable for formatting than simple tools like jsonlint -f.

If your project already uses Prettier, you likely have it as a dev dependency. If not, you'd install it.

.github/workflows/prettier-json-check.yml

name: Prettier JSON Check

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

jobs:
  prettier-check:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '20'

    # If Prettier is a project dependency (recommended)
    - name: Install dependencies
      run: npm ci # Use npm ci for CI/CD environments

    # If Prettier is NOT a project dependency, install it globally just for the action
    # - name: Install Prettier (Global)
    #   run: npm install -g prettier

    - name: Run Prettier Check for JSON files
      # Use npx if Prettier is a project dependency
      run: npx prettier --check "**/*.json" --ignore-path .gitignore
      # Use global prettier if installed globally
      # run: prettier --check "**/*.json" --ignore-path .gitignore
      # The --check flag makes Prettier exit with a non-zero status if files need formatting.
      # "**/*.json" targets all .json files recursively.
      # --ignore-path .gitignore respects your .gitignore file for files to skip.

This workflow is similar, but instead of jsonlint, it uses npx prettier --check. The --check flag is designed specifically for CI environments; it compares the files to how Prettier would format them and exits with an error code if any files differ, or if there are syntax errors that prevent formatting. This single command handles both validation (indirectly, as unparseable JSON can cause Prettier to fail) and formatting checks . Using npx is recommended as it runs the version of Prettier installed in your project's node_modules, ensuring consistency with local development.

Integrating with Pull Requests

Running these checks on pull requests is highly effective. If the GitHub Action job fails, the pull request will show a failed status check. You can configure branch protection rules in your GitHub repository settings to require this check to pass before a pull request can be merged. This ensures that no ill-formatted or invalid JSON makes it into your main branch.

Checking vs. Auto-Fixing and Committing

The examples above use the "check" approach: the workflow fails if JSON is not formatted/valid, and the developer must fix it locally and push a new commit. This is generally the preferred method as it keeps the responsibility of correct code on the developer and avoids the complexity of giving the GitHub Actions runner commit permissions.

An alternative is to have the workflow automatically format/validate the JSON and commit the changes back to the branch. While possible, this adds complexity:

  • You need to configure Git in the action runner.
  • You need to grant the action permissions to write to the repository (e.g., using a Personal Access Token or GITHUB_TOKEN with appropriate scopes).
  • It creates automatic commits, which some teams might find noisy.

For most use cases, simply failing the check and notifying the developer is sufficient and easier to maintain.

Tips and Best Practices

  • Specify Files: Instead of checking *all* .json files, you might want to target specific directories or files if your repo contains external JSON or build artifacts you don't want to check. Adjust the find command or Prettier patterns ("**/*.json") accordingly.
  • Use .gitignore / Ignore Files: Both find (with exclusions) and Prettier's --ignore-path flag allow you to skip files or directories listed in your .gitignore, which is essential for avoiding checks on generated files.
  • Monorepos: In a monorepo, you might only want to run the check if JSON files within specific projects have changed. You can use GitHub Actions features like paths filtering in the on trigger or check for changed files within the job.
  • Combine with Linting: If you have other linting rules (like ESLint for JS/TS), consider combining the JSON check into a single "Lint" job in your workflow file.

Conclusion

Automating JSON formatting and validation with GitHub Actions is a straightforward process that significantly improves code quality and developer workflow. By adding a simple YAML file to your repository, you can enforce consistent standards and catch potential errors early in the development cycle. Whether you choose a lightweight tool like jsonlint or a more comprehensive formatter like Prettier, the principle is the same: let automation handle the mundane checks so your team can focus on building features. Implement these checks today and streamline your JSON handling!

Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool