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:
What is interesting is that you can reference an environment in a job 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 }}
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 theenvironment
from the latest run of the job that references the environment: -
The
url
property ofenvironment
can be static but it can also reference a step output parameter which can even be from a step within the same job. In the latter case it will be evaluated after the step within the job that 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"
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 (assuming we are deploying each PR's source branch to a different URL, as happens when you deploy to Preview environment - this is an environment in a Vercel project as opposed to in GitHub repo for the project - in which case multiple deployments would be listed for that environment and each would be acessible!).
-
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:
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:
When using environments, my CI workflow deploys to Vercel's Preview environment and references GitHub repo environment of the same name, and the CD/release workflow deployment job references the GitHub Production envronment.
Full CI workflow (ci.yml
) as follows:
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-pr-preview-env:
runs-on: ubuntu-24.04
environment:
name: Preview
url: ${{ steps.deploy-artifacts.outputs.previewUrl }}
steps:
- uses: actions/checkout@v2
- name: Update Version Number in package.json
run: npm --no-git-tag-version version 0.0.0-pr${{ github.event.number }}
- 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"
Release workflow that deploys to Production is as follows:
name: Release to Production
concurrency: release-to-prod-pipeline
on:
push:
branches:
- main
jobs:
create-release:
# UNCOMMENT THE FOLLOWING LINE WHEN
# you have defined jobs named
# 'build' and 'test'
#
# SEMANTIC-RELEASE SHOULD ONLY RUN
# AFTER TESTS HAVE RUN SUCCESSFULLY
#
#needs: [build, test]
# This permissions block wasn't needed in
# earlier versions of semantic-release
# (I have a project currently using an
# earlier version and it works fine without
# a permissions block anywhere in the workflow)
# The issue is described here:
# https://github.com/semantic-release/semantic-release/issues/2481
permissions:
contents: write
runs-on: ubuntu-24.04
name: Create Release
outputs:
released: ${{ env.RELEASED }}
newVersion: ${{ env.NEW_VERSION }}
steps:
- uses: actions/checkout@v3
name: Checkout code
id: checkout
with:
submodules: true
- name: Create GitHub release
id: semanticrelease
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
npm ci
echo "RELEASED=0" >> $GITHUB_ENV
npm run release
deploy:
name: Deploy to Vercel
runs-on: ubuntu-24.04
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
needs: create-release
if: ${{ needs.create-release.outputs.released == 1}}
steps:
- name: Show version
run: |
echo "New Version Number is: ${{ needs.create-release.outputs.newVersion }}"
- uses: actions/checkout@v2
- name: Update Version Number in package.json
run: npm --no-git-tag-version version ${{ needs.create-release.outputs.newVersion }}
- 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
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
Top comments (0)