Last active
November 8, 2023 16:40
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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