Skip to content

Instantly share code, notes, and snippets.

@gregsadetsky
Last active November 8, 2023 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregsadetsky/7e4f040989d7792c3191316174409670 to your computer and use it in GitHub Desktop.
Save gregsadetsky/7e4f040989d7792c3191316174409670 to your computer and use it in GitHub Desktop.
plugin for xbar which shows the latest deployment status for your services hosted on render.com in your menubar
#!/opt/homebrew/bin/python3
# canonical source for this script:
# https://gist.github.com/gregsadetsky/7e4f040989d7792c3191316174409670
"""
this is a script that is meant to be run by xbar i.e.
https://github.com/matryer/xbar
a utility that places any command line output into a macOS menu bar
I use this script to keep an eye on my deployments to render.com so that
I don't have to have https://dashboard.render.com/ open all the time :-)
to use:
- install xbar
- open xbar (from the menu bar), select "Open plugin folder…"
- this is the directory where you will be placing this gist/python file
- download this gist and name it "render.1m.py" -– xbar will run it once a minute
(you can change the name to render.10m.py to run it once per 10m, etc. -- more frequent than 1/m is probably not a great idea)
- make sure that your python3 install comes from homebrew i.e. that /opt/homebrew/bin/python3 exists/works.
if your path to python is different, change it at the very top of this file in the shebang
- run `pip3 install requests python-dotenv` in your terminal to bring in the required dependencies
- in the terminal, run `chmod a+x <path to the python file>` so that xbar can call/execute it
- almost there! in the same plugins folder, create a .env file and in it, write the following:
RENDER_API_TOKEN="rnd_....."
replacing rnd_..... with your Render API token. you can make a new token for yourself on the following page:
https://dashboard.render.com/u/settings#api-keys
- test the script manually by running it in the terminal i.e. `python3 <path to render.1m.py>` - make sure that it returns "R:" with some emoji.
if the emoji is a question mark, an error happened. leave a comment here and we'll try to debug it.
- you should be able to select "Refresh all" in xbar and see the correct output in your menubar!
- let me know if something doesn't work by leaving a comment below
"""
import os
from datetime import datetime, timedelta, timezone
import requests
from dotenv import load_dotenv
load_dotenv()
RENDER_API_TOKEN = os.getenv("RENDER_API_TOKEN")
assert len(RENDER_API_TOKEN) > 0 # sanity check
def _make_api_request(path, params=None):
url = f"https://api.render.com/v1/{path}"
headers = {
"accept": "application/json",
"authorization": f"Bearer {RENDER_API_TOKEN}",
}
r = requests.get(url, headers=headers, params=params)
assert r.ok
return r.json()
all_services = _make_api_request("services")
all_services_by_id = {
service["service"]["id"]: service["service"] for service in all_services
}
service_status_by_id = {}
for service_id, service in all_services_by_id.items():
# only look at deployments in the last 24 hours -- could be last hour only maybe?
one_day_ago_isoformat = (
(datetime.now() - timedelta(days=1))
# why is this so convoluted
.astimezone(timezone.utc)
.replace(tzinfo=None)
.isoformat(timespec="milliseconds")
+ "Z"
)
service_deploys = _make_api_request(
f"services/{service['id']}/deploys",
params={"updatedAfter": one_day_ago_isoformat},
)
if len(service_deploys):
latest_deploy_status = service_deploys[0]["deploy"]["status"]
service_status_by_id[service_id] = latest_deploy_status
# https://community.render.com/t/deployment-status-api/15431/3
FAIL_CODES = "deactivated|build_failed|update_failed|canceled".split("|")
IN_PROGRESS_CODES = "created|build_in_progress|update_in_progress".split("|")
all_found_statuses = list(service_status_by_id.values())
if len(all_found_statuses) == 0:
print("R:∅")
elif any([status in FAIL_CODES for status in all_found_statuses]):
print("R:🚨")
elif any([status in IN_PROGRESS_CODES for status in all_found_statuses]):
print("R:🏗")
elif all([status == "live" for status in all_found_statuses]):
print("R:✅")
else:
print("R:❓")
print("---")
for service_id, status in sorted(service_status_by_id.items()):
service_name = all_services_by_id[service_id]["name"]
print(f"{service_name}: {status}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment