GitHub Actions is very flexible but that comes at the cost of it not always being easy to configure it for tasks which are common on other CI/CD tools.

In this post I will show you how to use a workflow that will:

  • Automatically deploy into an environment named test on every commit
  • Give you a button in the GitHub UI that allows you to manually deploy into a choice of environments (test, staging, production etc) allowing you to promote a release from test to production with a single click.
  • Place the name of the environment into an ENV variable for use in your deployment scripts.
  • Have different variable in each environment, such as different usernames and passwords for deployment.

The workflow

Inside your repository create a new file at .github/workflows/deploy.yml with the following content. You will need to replace the echo command with your actual deployment script. Push the commit to main and the actions script will run automatically.

name: Deploy to server

# A nice name for the workflow that will show up in the Actions tab 
# in the GitHub UI and includes the name of the environment you are deploying to
run-name: Deploy to ${{ inputs.environment || 'test' }} (${{ github.ref_name }})
on:
  # By default automatically deploy all pushes into the test environment
  push:
    paths-ignore:
      - "README**"
    branches:
      - "**"
    tags:
      - "*"
  # Allows you to run this workflow manually with a button on the Actions tab
  workflow_dispatch:
    inputs:
      environment:
        description: "The environment to deploy to"
        required: true
        default: "test"
        type: choice
        options:
          - test
          - production

# only one deployment at a time!
concurrency: one-deploy-at-a-time

# these variableas are available in all your deployment steps
env:
    # the name of the environment you will deploy to, defaulting to test
    ENV_NAME: ${{ inputs.environment || 'test'}}

    # other variables (& defaults) you might want in your deployment scripts
    DEPLOY_USER: ${{ vars.DEPLOY_USER || 'admin-user' }}
    DEPLOY_PASSWORD: ${{ secrets.DEPLOY_PASSWORD || 'default-secret' }}

jobs:
  deploy:

    # This maps to the Environments tab in the GitHub UI 
    # and scopes secrets and variables to that environment
    environment: ${{ inputs.environment || 'test' }}

    steps:
      # fetch the code to deploy
      - uses: actions/checkout@v4

      # Do the deployment! - You can now use the $ENV_NAME variable
      # in your deployment scripts to call whatever scripted deployment 
      # you use, and the scoped variable like $DEPLOY_USER and 
      # $DEPLOY_PASSWORD that are specific to the environment
      - name: Deploy to ${{ env.ENV_NAME }} environment
        run: echo "Deploy to $ENV_NAME using $DEPLOY_USER and $DEPLOY_PASSWORD"
        # prints "Deploy to test using admin-user and default-secret"

One thing to note is that the ${{ inputs.environment || 'test'}} syntax is repeated - this is because of annoying Actions scoping rules that mean you can only the env.ENV_NAME variabled inside steps and not in the jobs.<jobid>.environment field and you cannot read the jobs.<jobid>.environment value from the rest of the workflow.

In the GitHub UI you can configure variables and secrets for each environment in the settings > Environments tab.

When you go to the Actions page for your repository you will see a button to manually run the workflow in the top right, and you can choose the environment to deploy to from the drop down box.

You can see I have just done an echo and not a real deployment - I will leave that to you, but i usually have a script like ./deploy.sh $ENV_NAME which then calls the cloud CLI or ssh script to deploy to the correct environment.