A repo that demostrates GitHub's Custom Deployment Protection Rules
Repository Environment configurations in GitHub allow you to define your own custom deployment protection rules. This gives you the opportunity to have your own custom logic in an app/website/script/etc. to assist you in knowing if you can or cannot move forward with a workflow job targeting that specific environment.
At a high level, the steps to create your own custom deployment protection rule are:
- Create/edit a GitHub App to serve as your protection rule. The app will subscribe to a deployment protection webhook and will be delivered the necessary payload when an approval/rejection is needed from it.
- Determine how you want to implement your rule. Will it be a website that users will interact with? Will it be a third-party product like ServiceNow?
- Provide an endpoint for the deployment protection webhook (configured in your GitHub App) so GitHub knows where to send the data.
- When a webhook is received, parse the data to know how/where to repsond.
- Do whatever custom logic you require in your app
- Respond to the API endpoint you received in the webhook payload with the correct state of "approved" or "rejected"
Some links for more details on this process:
- Protecting Deployments
- Creating a GitHub App
- Authenticating as a GitHub App Installation
- Deployment Protections REST API
If you want an easy way to receive webhooks, you can use a free service like Smee.io to quickly create an endpoint for a webhook. Smee.io allows you to view all webhook data received.
This repository uses GitHub Pages to run a website that is used to control approval and rejection of a workflow run.
In the src/python directory, the file set-deployment-state,py file will allow you to complete the process of approving or rejecting a deployment protection rule. The python code demostrates this in a manual way for demo purposes only. This is to avoid developing a fully automated system for the purpse of simplicity. It shows the main steps of the process that you would eventually fully automate.
Pre-reqs we will assume are completed at this point:
- Your GitHub app is configured and installed in the appropriate Org/Account
- You have your webhook endpoint set up and ready to receive events
- You have added the custom protection to the environment you wish to target
- You have a workflow created that is targeting the protected environment
Python specific pre-reqs:
- Open the terminal of your choice and ensure you can run python code from it (ex: type "python3" and see if it recognizes the command)
- If you need to install Python, visit Python.org
- The required Python dependencies are jwt, requests, and time
- Pip is required to install python dependencies, which may also need to be installed. To install pip to add these dependencies, run the command:
sudo apt install python3-pip
- If you have any problem installing pip (or any of the other dependencies), you may first have to run:
sudo apt-get update
- To add any of the missing dependencies, run the appropriate install commands
pip install jwt
pip install requests
pip install time
- In whatever manner you chose, have the set-deplyment-state,py script accessible from your terminal
To demostrate:
- Grab the App ID for the GitHub App and set the value in the set-deplyment-state,py script
- Grab the pem file you received when creating a Private Key for your GitHub App. This should have be automatically downloaded when you created a Private Key (check your downloads folder)
- Store the pem file in a location that is accessible in your terminal and add the path to the file in the set-deplyment-state,py script
- Trigger the workflow
- Wait for Webhook event to be received at your endpoint
- View the webhook payload data
- From the Payload, you will want the following data:
- installation.id
- deployment_callback_url
- Set these values in the set-deplyment-state,py
- In the set-deplyment-state,py, select which command you want to perform (APPROVE or REJECTED) and comment out the other.
- Run the script when ready:
python3 set-deployment-state.py
NOTE I have had issues with the exp value of the JWT payload (which is the requested expiration of the token). I feel like the server responding to me had a clock that was out-of-sync and I had to artifically inflate the expiration to a time 54k seconds ahead. Look for an error response that is sugessting your exp value "must be a date in the future".
The script will run and do the following:
- Get a JWT token for your GitHub App so that it can access the API
- Get an Installation token for your GitHub App so that it can call specific API endpoint(s) that use installation tokens
- An "Installation" is a specific installation of the github app on an organization or in a users personal account
- Approves or Rejects the deployment based on which method call you make