Skip to content

Instantly share code, notes, and snippets.

@mpvosseller
Last active January 2, 2023 19:34
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpvosseller/a2dc96cf1bb5f9de0db9d6963c3e7a97 to your computer and use it in GitHub Desktop.
Save mpvosseller/a2dc96cf1bb5f9de0db9d6963c3e7a97 to your computer and use it in GitHub Desktop.
Ruby function to create a new Heroku app with the Heroku Platform API
# Function to create a new Heroku app instance using the ruby Heroku Platform API
#
# Documentation:
# https://devcenter.heroku.com/articles/setting-up-apps-using-the-heroku-platform-api
# https://devcenter.heroku.com/articles/platform-api-reference#app-setup
# https://github.com/heroku/platform-api
# https://heroku.github.io/platform-api
#
# Add the gem 'platform-api'
require "platform-api"
def create_heroku_app(app_name:, heroku_org:, branch:, github_account:, github_repo:, config_vars:)
# requires a github token with "repo" access. Create one here: https://github.com/settings/tokens
github_token = ENV["GITHUB_ACCESS_TOKEN"]
raise "GITHUB_ACCESS_TOKEN" unless github_token
heroku_token = ENV["HEROKU_TOKEN"] || `heroku auth:token`.strip.presence
raise "You are not logged into heroku cli. Log in with heroku login" unless heroku_token
client = PlatformAPI.connect_oauth(heroku_token)
app_setup_client = PlatformAPI::AppSetup.new(client)
app_setup_client.create(
{
app: {
name: app_name,
organization: heroku_org
},
source_blob: {
url:
"https://user:#{github_token}@api.github.com/repos/#{github_account}/#{github_repo}/tarball/#{branch}"
},
overrides: {
env: config_vars
}
}
)
end
@mpvosseller
Copy link
Author

mpvosseller commented Apr 19, 2022

Then deploy updates using git and destroy the app when you are done:
You might name the app something like mycompany-pr123 and the remote pr123

# add the git remote
heroku git:remote -a <app-name> -r <git-remote-name>

# deploy via git
git push <git-remote_name> <branch>:master

# destroy the app when done
heroku apps:destroy -a <app-name>

@mpvosseller
Copy link
Author

One improvement you can make is to have the script wait for the app to finish being created. You can do this by polling the app status. A basic function to wait for an app would look like the below. You get setup_id from the response of app_setup_client.create call.

  def wait_for_app_setup(setup_id)
    puts "waiting for app setup to complete..."

    prev_status = {}

    loop do
      status = app_setup_client.info(setup_id)

      if status["status"] != "pending"
        puts # add new line after the progress dots
        return status
      end

      puts status if status.to_json != prev_status.to_json
      print "."
      sleep 5
      prev_status = status
    end
  end

@chaadow
Copy link

chaadow commented Apr 22, 2022

Thank you so much for this script

@chaadow
Copy link

chaadow commented Apr 22, 2022

app_setup_client.info(setup_id) sometimes throws a 500 error ( the app still is created so thankfully not that blocking) i haven't still investigated the issue, but have u encountered the error, and if so do u know what might cause the issue

@mpvosseller
Copy link
Author

@chaadow Sorry, I have not run into that issue. Are you sure you passing in the correct setup_id? Would look something like this:

setup_response = create_heroku_app(...)
setup_id = setup_response["id"]
wait_for_app_setup(setup_id)

@chaadow
Copy link

chaadow commented Apr 26, 2022

It worked, thanks again!

@chaadow
Copy link

chaadow commented Jan 1, 2023

Thanks to you, I've recreated an entire review app generation system that integrates with github through a webhook. and Even though the github integration came back, I just stuck with my system since it's better :D

The platform API is really cool and allows you to customize a LOT. The heroku github integration has its own limitations, ( cannot execute scripts at certain levels/states of the deployment)

I can launch a db restoration process, shutdown/startup dynos on the fly, remap the dyno formation, create addons on the fly by adding enabling/disabling github labels etc..
It also allowed us to optimize drastically our dyno/addon costs!

Thanks again, your gist in addition to the def wait_for_app_setup(setup_id) ( i've opted for a clock process that checks on the status every minute and saves the build state in a db table ) were more than enough to get me started 👌

@mpvosseller
Copy link
Author

@chaadow Awesome! Glad to hear it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment