DEV Community

nausaf
nausaf

Posted on • Updated on

Environments in GitHub (with example of deploying a Next.js app to Vercel)

An environment is basically a set of three things:

  • Secrets
  • Variables
  • Protection rules

When you define an environment in GitHub, you provide a name and can configure any of the above.

For example, when defining an environment named UAT (in Environments tab in repo Settings), the environment definition page would look like this:

Image description

What is interesting is that you can reference an environment in a job in a GitHub Actions workflow using an environment block like this:

jobs:
  deploy-to-vercel-pr-preview-env:
    runs-on: ubuntu-24.04
    environment:
      name: Preview
      url: ${{ steps.deploy-artifacts.outputs.previewUrl }}
Enter fullscreen mode Exit fullscreen mode

In addition to the fact that such a job is allowed to access Variables and Secrets defined within the environment (as opposed to at the repo level) and Protection Rules such as delayed execution, manual approval, and deployment only from branches meeting specified criteria (e.g. with matching names and/or with branch protection rules) apply, when a job references an environment, this enables a number of other interesting behaviours:

  • There is a really nice sticky comment on the PR (so it updates with every push to the source branch if that triggers the CI workflow) which shows the value of the url property for the environment once the job has completed:

    Image description

  • The url property of environment can be static but it can also reference a step output parameter of the same job. In the latter case, the url property of the environment block in the job will be evaluated after the step which outputs that parameter value has executed.

    In the job snippet shown above, the step that produces the referenced step output parameter is:

    id: deploy-artifacts
        run: |
          previewUrl=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
          echo "previewUrl=$previewUrl" >> "$GITHUB_OUTPUT"
    

    Once the job has completed (or perhaps when the step above has completed), value of step output parameter previewUrl would be available. This would be assigned to url proeprty of the environment. This is what gets displayed on the sticky comment for the environment as the deployment URL

  • If a job referencing an environment runs on multiple source branches (in multiple PRs), they don't seem to wait for each other i.e. they seem to run in parallel (I might be wrong on this).

    In this case each would post a different URL in its sticky comment as long as the underlying deployment logic generated a different URL on every deployment. For example, every deployment of Next.js project to Preview environment in your Vercel project would generate a new URL.

    In fact, since Vercel has such a generous allowance of deployment to an environment that would keep active, all deployments of the same branch, within the same pull request are active and accessible. If your deployment job references a GitHub environment (e.g. "Preview" in above example) then all of these deployment URLs are accessible on the pull request, not just the latest one:

Image description

  • All deployments to an environment, with their respective URLs if provided, can also be seen on repo main page in a section on right hand side named Deployments:

    Image description

    You can click an environment name and see a list of all deployments to it, including link to each. The link to the most recent deployment is shown right at the top:

    Image description


To use GitHub Environments in my Next.js project which I deploy to Vercel, I do the following:

  1. Create two GitHub Environments, named Production and Preview.

    A Vercel project to which the repo is deployed also has evironments with the same names (these are Vercel environments though, not GitHub environments). So its good to have matching names in both GitHub repo and Vercel project.

  2. My CI workflow deploys a pull request's source branch to Vercel's Preview environment and references GitHub Environment named "Preview":

    name: CI
    concurrency:
      group: ci-${{ github.ref }}
      cancel-in-progress: true
    permissions:
      checks: write
      pull-requests: write
    on:
      pull_request:
        branches:
          - main
    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
    
    jobs:
      deploy-to-vercel-preview-env:
        runs-on: ubuntu-24.04
        environment:
          name: Preview
          url: ${{ steps.deploy-artifacts.outputs.previewUrl }}
        steps:
          - uses: actions/checkout@v2
          - name: Install Vercel CLI
            run: npm install --global vercel@latest
          - name: Pull Vercel Environment Information
            run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
          - name: Build Project Artifacts
            run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
          - name: Deploy Project Artifacts to Vercel
            id: deploy-artifacts
            run: |
              previewUrl=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
              echo "previewUrl=$previewUrl" >> "$GITHUB_OUTPUT"
    
  3. Release/CD workflow that deploys main to Vercel Production environment references the GitHub Environment named "Production":

    name: Release to Production
    concurrency: release-to-prod-pipeline
    on:
      push:
        branches:
          - main
    
    jobs:
      deploy:
        name: Deploy to Vercel
        runs-on: ubuntu-24.04
        environment:
          name: Production
          url: ${{ steps.deploy-artifacts.outputs.previewUrl }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        steps:
          - uses: actions/checkout@v2
          - name: Install Vercel CLI
            run: npm install --global vercel@latest
          - name: Pull Vercel Environment Information
            run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
          - name: Build Project Artifacts
            run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
          - name: Deploy Project Artifacts to Vercel
            id: deploy-artifacts
            run: |
              previewUrl=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }})
              echo "previewUrl=$previewUrl" >> "$GITHUB_OUTPUT"
    
    

Top comments (0)