{"meta":{"title":"Delivering deployments","intro":"Using the Deployments REST API, you can build custom tooling that interacts with your server and a third-party app.","product":"REST API","breadcrumbs":[{"href":"/en/rest","title":"REST API"},{"href":"/en/rest/guides","title":"Guides"},{"href":"/en/rest/guides/delivering-deployments","title":"Delivering deployments"}],"documentType":"article"},"body":"# Delivering deployments\n\nUsing the Deployments REST API, you can build custom tooling that interacts with your server and a third-party app.\n\nYou can use the REST API to deploy your projects hosted on GitHub on a server that you own. For more information about the endpoints to manage deployments and statuses, see [REST API endpoints for deployments](/en/rest/deployments). You can also use the REST API to coordinate your deployments the moment your code lands on the default branch. For more information, see [Building a CI server](/en/rest/guides/building-a-ci-server).\n\nThis guide will use the REST API to demonstrate a setup that you can use.\nIn our scenario, we will:\n\n* Merge a pull request.\n* When the CI is finished, we'll set the pull request's status accordingly.\n* When the pull request is merged, we'll run our deployment to our server.\n\nOur CI system and host server will be figments of our imagination. They could be\nHeroku, Amazon, or something else entirely. The crux of this guide will be setting up\nand configuring the server managing the communication.\n\nIf you haven't already, be sure to [download `ngrok`](https://ngrok.com/), and learn how\nto [use it](https://ngrok.com/docs/getting-started/). We find it to be a very useful tool for exposing local\napplications to the internet.\n\n> \\[!NOTE]\n> Alternatively, you can use webhook forwarding to set up your local environment to receive webhooks. For more information, see [Using the GitHub CLI to forward webhooks for testing](/en/webhooks/testing-and-troubleshooting-webhooks/using-the-github-cli-to-forward-webhooks-for-testing).\n\nNote: you can download the complete source code for this project\n[from the platform-samples repo](https://github.com/github/platform-samples/tree/master/api/ruby/delivering-deployments).\n\n## Writing your server\n\nWe'll write a quick Sinatra app to prove that our local connections are working.\nLet's start with this:\n\n```ruby\nrequire 'sinatra'\nrequire 'json'\n\npost '/event_handler' do\n  payload = JSON.parse(params[:payload])\n  \"Well, it worked!\"\nend\n```\n\n(If you're unfamiliar with how Sinatra works, we recommend [reading the Sinatra guide](http://www.sinatrarb.com/).)\n\nStart this server up. By default, Sinatra starts on port `4567`, so you'll want\nto configure `ngrok` to start listening for that, too.\n\nIn order for this server to work, we'll need to set a repository up with a webhook. The webhook should be configured to fire whenever a pull request is created, or merged.\n\nGo ahead and create a repository you're comfortable playing around in. Might we\nsuggest [@octocat's Spoon/Knife repository](https://github.com/octocat/Spoon-Knife)?\n\nAfter that, you'll create a new webhook in your repository, feeding it the URL that `ngrok` gave you, and choosing `application/x-www-form-urlencoded` as the content type.\n\nClick **Update webhook**. You should see a body response of `Well, it worked!`.\nGreat! Click on **Let me select individual events.**, and select the following:\n\n* Deployment\n* Deployment status\n* Pull Request\n\nThese are the events GitHub will send to our server whenever the relevant action\noccurs. We'll configure our server to *just* handle when pull requests are merged\nright now:\n\n```ruby\npost '/event_handler' do\n  @payload = JSON.parse(params[:payload])\n\n  case request.env['HTTP_X_GITHUB_EVENT']\n  when \"pull_request\"\n    if @payload[\"action\"] == \"closed\" && @payload[\"pull_request\"][\"merged\"]\n      puts \"A pull request was merged! A deployment should start now...\"\n    end\n  end\nend\n```\n\nWhat's going on? Every event that GitHub sends out attached a `X-GitHub-Event`\nHTTP header. We'll only care about the PR events for now. When a pull request is\nmerged (its state is `closed`, and `merged` is `true`), we'll kick off a deployment.\n\nTo test out this proof-of-concept, make some changes in a branch in your test\nrepository, open a pull request, and merge it. Your server should respond accordingly!\n\n## Working with deployments\n\nWith our server in place, the code being reviewed, and our pull request\nmerged, we want our project to be deployed.\n\nWe'll start by modifying our event listener to process pull requests when they're\nmerged, and start paying attention to deployments:\n\n```ruby\nwhen \"pull_request\"\n  if @payload[\"action\"] == \"closed\" && @payload[\"pull_request\"][\"merged\"]\n    start_deployment(@payload[\"pull_request\"])\n  end\nwhen \"deployment\"\n  process_deployment(@payload)\nwhen \"deployment_status\"\n  update_deployment_status\nend\n```\n\nBased on the information from the pull request, we'll start by filling out the\n`start_deployment` method:\n\n```ruby\ndef start_deployment(pull_request)\n  user = pull_request['user']['login']\n  payload = JSON.generate(:environment => 'production', :deploy_user => user)\n  @client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => \"Deploying my sweet branch\"})\nend\n```\n\nDeployments can have some metadata attached to them, in the form of a `payload`\nand a `description`. Although these values are optional, it's helpful to use\nfor logging and representing information.\n\nWhen a new deployment is created, a completely separate event is triggered. That's\nwhy we have a new `switch` case in the event handler for `deployment`. You can\nuse this information to be notified when a deployment has been triggered.\n\nDeployments can take a rather long time, so we'll want to listen for various events,\nsuch as when the deployment was created, and what state it's in.\n\nLet's simulate a deployment that does some work, and notice the effect it has on\nthe output. First, let's complete our `process_deployment` method:\n\n```ruby\ndef process_deployment\n  payload = JSON.parse(@payload['payload'])\n  # you can send this information to your chat room, monitor, pager, etc.\n  puts \"Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}\"\n  sleep 2 # simulate work\n  @client.create_deployment_status(\"repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}\", 'pending')\n  sleep 2 # simulate work\n  @client.create_deployment_status(\"repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}\", 'success')\nend\n```\n\nFinally, we'll simulate storing the status information as console output:\n\n```ruby\ndef update_deployment_status\n  puts \"Deployment status for #{@payload['id']} is #{@payload['state']}\"\nend\n```\n\nLet's break down what's going on. A new deployment is created by `start_deployment`,\nwhich triggers the `deployment` event. From there, we call `process_deployment`\nto simulate work that's going on. During that processing, we also make a call to\n`create_deployment_status`, which lets a receiver know what's going on, as we\nswitch the status to `pending`.\n\nAfter the deployment is finished, we set the status to `success`.\n\n## Conclusion\n\nAt GitHub, we've used a version of `Heaven` to manage\nour deployments for years. A common flow is essentially the same as the\nserver we've built above:\n\n* Wait for a response on the state of the CI checks (success or failure)\n* If the required checks succeed, merge the pull request\n* `Heaven` takes the merged code, and deploys it to staging and production servers\n* In the meantime, `Heaven` also notifies everyone about the build, via [Hubot](https://github.com/github/hubot) sitting in our chat rooms\n\nThat's it! You don't need to build your own deployment setup to use this example.\nYou can always rely on [GitHub integrations](https://github.com/integrations)."}