CoursesArchitecture & DevOpsAutomating Development Workflow
Architecture & DevOps
Markdown Version

Automating Development Workflow

Automate Sanity Studio deployments and CI checks that validate schemas and content, ensuring every code change is rigorously reviewed and production-ready.
Log in to mark your progress for each Lesson and Task

Now that your environments and Studios are fully configured, it’s time to automate the workflow.

In this lesson, we will explore how automating the deployment of your Sanity Studio streamlines your development process and helps you achieve faster, more reliable releases. By transitioning from manual deployments to an automated workflow, you not only ensure that your production code is built and deployed consistently, but you also gain immediate feedback on changes with minimal human intervention.

When developing new features, for example adding a new schema definition or creating a custom input component, should follow a consistent process:

  1. Start by checking out a new feature branch
  2. Making code changes while running the Studio locally
  3. Once they're ready to deploy and looking for a code review, the developer will push their branch to the remote and open a pull request
  4. Once their code has been reviewed and validated, they'll merge their pull request to the main branch.

Imagine you have just committed changes to a feature branch and opened a pull request. Instead of manually building and deploying your Sanity Studio, like we did in the previous lesson, an automated process springs into action. The workflow is triggered by push or pull request events. First, it checks out the latest code from your branch, sets up the Node.js environment, and installs the dependencies. It'll then build your Studio and deploy it to a PR-numbered hostname. As an added benefit, the workflow also automatically posts a comment with a link to the preview environment where reviewers can see your changes. When your code is merged into the main branch, the workflow builds and deploys the Studio to the production environment. Once a pull request is closed, a separate job is triggered to clean up the associated preview deployment.

Here is a sample GitHub workflow that demonstrates this automated deployment process for a Sanity Studio.

Though written here for GitHub, these steps can be ported to any CI/CD provider and can be adapted to your preferred solution.
name: Deploy Sanity Studio
on:
push:
branches:
- main
- development
pull_request:
types: [opened, synchronize, reopened, closed]
permissions:
contents: read
pull-requests: write
env:
SANITY_AUTH_TOKEN: ${{ secrets.SANITY_AUTH_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
environment:
name: ${{ github.ref == 'refs/heads/main' && 'Production' || github.ref == 'refs/heads/development' && 'Development' || 'Preview' }}
url: ${{ steps.deploy.outputs.STUDIO_URL }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci
- name: Set Studio hostname
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "SANITY_STUDIO_HOSTNAME=${HOSTNAME}-pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV
else
echo "SANITY_STUDIO_HOSTNAME=${HOSTNAME}" >> $GITHUB_ENV
fi
- name: Build and deploy Sanity Studio
id: deploy
run: |
if [ -z "${SANITY_STUDIO_HOSTNAME}" ]; then
echo "Error: SANITY_STUDIO_HOSTNAME is not set" >&2
exit 1
fi
if [[ "$SANITY_ACTIVE_ENV" == "development" ]]; then
npm run deploy -- --yes --source-maps
else
npm run deploy -- --yes
fi
echo "STUDIO_URL=https://${SANITY_STUDIO_HOSTNAME}.sanity.studio" >> $GITHUB_OUTPUT
- name: Post preview link
if: github.event_name == 'pull_request' && github.event.action == 'opened'
uses: actions/github-script@v7
with:
script: |
const body = [
'**🚀 Preview environment has been deployed!**',
`Visit [${process.env.STUDIO_URL}](${process.env.STUDIO_URL}) to see your changes.`,
"*This is a temporary environment that will be undeployed when this PR is merged or closed.*"
].join('\n\n')
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
})
env:
STUDIO_URL: ${{ steps.deploy.outputs.STUDIO_URL }}
teardown:
name: Teardown
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'closed'
environment:
name: Preview
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install dependencies
run: npm ci
- name: Cleanup PR preview
run: npx sanity undeploy -- --yes
env:
SANITY_STUDIO_HOSTNAME: ${HOSTNAME}-pr-${{ github.event.pull_request.number }}

Now that your Sanity Studio is deployed automatically, it’s crucial that every change merged into the main branch has been thoroughly reviewed and validated. When a pull request is opened or updated, your CI pipeline not only runs the typical linting and type-checking jobs but also includes Sanity-specific checks to catch errors early. If any of these jobs fail, detailed reports are automatically posted to the pull request, providing instant feedback for your team. In this way, before any merge occurs, your code is guaranteed to have passed all the necessary automated checks.

Within your CI pipeline, the commands sanity schema validate and sanity documents validate play critical roles in ensuring that your code does not introduce breaking changes. These validation steps create a robust safety net that goes beyond simply automating deployments.

The command sanity schema validate is used to verify that your schema definitions are error-free. When you run this command, it checks your schema files for syntax errors, misconfigurations, or other issues that might cause runtime errors.

In contrast, the command sanity documents validate verifies that the content stored in your Sanity dataset conform to the constraints defined in your schema. This command inspects each document to ensure that required fields are present, data types match the expected formats, and any additional validation rules you have implemented are adhered to. This step is essential for maintaining data integrity, and any discrepancies—such as missing values or incorrect data formats—are flagged to prevent problematic changes from being merged into production.

Changes to your content model often require migration scripts to ensure data integrity. You can learn more about migrating data in Handling schema changes confidently.
name: CI
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
SCHEMA_VALIDATION_REPORT: schema-report.txt
DATASET_VALIDATION_REPORT: dataset-report.txt
jobs:
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: lts/*
- run: npm ci
- name: Typecheck
run: npm run typecheck
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: lts/*
- run: npm ci
- name: Lint
run: npm run lint -- --max-warnings 0
validate-schema:
name: Validate Studio schema
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: lts/*
- run: npm ci
- name: Validate Studio schema
id: validate
run: |
npx sanity schema validate >> ${{ env.SCHEMA_VALIDATION_REPORT }}
exit_code=$?
{
echo "## Schema Validation Results"
echo "\`\`\`"
cat ${{ env.SCHEMA_VALIDATION_REPORT }}
echo "\`\`\`"
} >> $GITHUB_STEP_SUMMARY
exit $exit_code
- name: Post schema validation report
uses: actions/github-script@v6
if: failure() && steps.validate.outcome == 'failure'
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('${{ env.SCHEMA_VALIDATION_REPORT }}', 'utf8');
const body = [
'### ❌ Schema validation failed',
'',
`\`\`\`${report}\`\`\``,
].join('\n');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
validate-dataset:
name: Validate dataset
runs-on: ubuntu-latest
if: (github.event_name == 'pull_request' && github.base_ref == 'main') || (github.ref == 'refs/heads/main')
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: lts/*
- run: npm ci
- name: Validate dataset
id: validate
run: |
npx sanity documents validate --yes --level info >> ${{ env.DATASET_VALIDATION_REPORT }}
exit_code=$?
{
echo "## Dataset Validation Results"
echo "\`\`\`"
cat ${{ env.DATASET_VALIDATION_REPORT }}
echo "\`\`\`"
} >> $GITHUB_STEP_SUMMARY
exit $exit_code
env:
SANITY_ACTIVE_ENV: production
SANITY_AUTH_TOKEN: ${{ secrets.SANITY_AUTH_TOKEN }}
# TODO: delete
SANITY_STUDIO_PROJECT_ID: ${{ vars.SANITY_PROJECT_ID }}
- name: Post dataset validation report
if: failure() && steps.validate.outcome == 'failure'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('${{ env.DATASET_VALIDATION_REPORT }}', 'utf8');
const body = [
'### ❌ Dataset validation failed',
'',
`\`\`\`${report}\`\`\``,
].join('\n');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body
});

By incorporating these validation steps into your GitHub workflow, you ensure that changes undergo rigorous review before they trigger the automated deployment process. This CI process not only enhances the quality and reliability of your Sanity Studio but also builds confidence that both its structure and underlying data are sound when updates are pushed to production.

Changes to your content model often require migration scripts to ensure data integrity. You can learn more about migrating data in Handling schema changes confidently.

You'll now have a robust DevOps process that enables continuous development while maintaining a stable production environment for your content team. This approach balances the needs of both developers and content editors, ensuring smooth operations and reliable deployments.

Mark lesson as complete
You have 1 uncompleted task in this lesson
0 of 1