commit e479f46d568ba5b8b5c022ba3837a45865af2e74 Author: Zev Averbach Date: Fri Mar 31 15:59:26 2023 +0200 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80a862d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +env/ +__pycache__/ diff --git a/netlify.py b/netlify.py new file mode 100644 index 0000000..9aba456 --- /dev/null +++ b/netlify.py @@ -0,0 +1,296 @@ +import os +import pathlib as pl +import shutil +import sys +from time import sleep +import uuid + +import requests +from rich.pretty import pprint + +from dotenv import load_dotenv + +load_dotenv() +NETLIFY_TOKEN = os.environ["NETLIFY_TOKEN"] +AUTH_HEADER = {"Authorization": f"Bearer {NETLIFY_TOKEN}"} +DOMAINS = os.environ["DOMAINS"].split(",") + + +class NoNestedFolder(Exception): + pass + + +class NoIndexHtml(Exception): + pass + + +class NoResult(Exception): + pass + + +def make_a_zip_file(dirpath: pl.Path) -> pl.Path: + + zipfile_filename = str(uuid.uuid4()) + shutil.make_archive(zipfile_filename, "zip", dirpath) + return pl.Path(zipfile_filename + ".zip") + + +def make_sure_theres_a_nested_folder_and_index_html(dirpath: pl.Path) -> None: + if "folder" not in [d.name for d in list(dirpath.iterdir())]: + raise NoNestedFolder + + if "index.html" not in [i.name for i in list((dirpath / "folder").iterdir())]: + raise NoIndexHtml + + +def deploy_page_to_netlify(dirpath: pl.Path, custom_domain: str | None = None) -> None: + make_sure_theres_a_nested_folder_and_index_html(dirpath) + + URL = "https://api.netlify.com/api/v1/sites" + headers = { + "Content-Type": "application/zip", + } | AUTH_HEADER + zip_file = make_a_zip_file(dirpath) + response = requests.post( + URL, + data=zip_file.read_bytes(), + headers=headers, + ) + zip_file.unlink() + if not response.ok: + raise Exception("something went wrong") + rj = response.json() + print("the url is " + rj["url"]) + if custom_domain is not None: + sleep(2) + set_to_custom_domain(rj["id"], custom_domain) + print(f"custom domain was set to {custom_domain}") + + +def remove_custom_domain(site_id: str) -> None: + URL = f"https://app.netlify.com/access-control/bb-api/api/v1/sites/{site_id}" + print(f"{URL=}") + response = requests.put( + URL, + json={"custom_domain": None}, + headers=AUTH_HEADER, + ) + if not response.ok: + print(response.reason) + raise Exception("something went wrong with removing the custom domain") + + +def get_site_id_from_netlify_domain(domain: str) -> str: + URL = "https://api.netlify.com/api/v1/sites" + response = requests.get(URL, headers=AUTH_HEADER) + if not response.ok: + raise Exception("something went wrong") + rj = response.json() + if not domain.startswith("http://"): + domain = f"http://{domain}" + print(f"trying to find {domain}") + for site in rj: + if site["url"] == domain: + return site["id"] + raise NoResult + + +def get_site_id_from_custom_domain(custom_domain: str) -> str: + URL = "https://api.netlify.com/api/v1/sites" + response = requests.get(URL, headers=AUTH_HEADER) + if not response.ok: + raise Exception("something went wrong") + rj = response.json() + for site in rj: + if site["custom_domain"] == custom_domain: + return site["id"] + raise NoResult + + +def set_to_custom_domain(site_id: str, custom_domain: str) -> None: + URL = f"https://app.netlify.com/access-control/bb-api/api/v1/sites/{site_id}" + print(f"{URL=}") + response = requests.put( + URL, + json={"custom_domain": custom_domain}, + headers=AUTH_HEADER, + ) + if not response.ok: + print(response.reason) + raise Exception("something went wrong with setting the custom domain") + + +def cli_remove_custom_domain() -> None: + """ + Remove a custom domain from a Netlify site + """ + try: + custom_domain = sys.argv[sys.argv.index("--remove-custom-domain") + 1] + except IndexError: + print("Please provide a domain") + return + if len(DOMAINS) == 1 and custom_domain.count(".") == 0: + custom_domain = f"{custom_domain}.{DOMAINS[0]}" + site_id = get_site_id_from_custom_domain(custom_domain) + remove_custom_domain(site_id) + print(f"{custom_domain} was removed") + + +def cli_set_custom_domain() -> None: + """ + Assign a custom domain to an already deployed site on Netlify + """ + try: + custom_domain = sys.argv[sys.argv.index("--custom-domain") + 1] + except IndexError: + print("Please provide a domain") + return + if "--domain" not in sys.argv: + print("Please provide a --domain") + return + try: + domain = sys.argv[sys.argv.index("--domain") + 1] + except IndexError: + print("Please provide a domain") + return + if domain.count(".") == 0: + domain = f"{domain}.netlify.app" + try: + site_id = get_site_id_from_netlify_domain(domain) + except NoResult: + print(f"No site found with domain '{domain}'") + return + if len(DOMAINS) == 1 and custom_domain.count(".") == 0: + custom_domain = f"{custom_domain}.{DOMAINS[0]}" + set_to_custom_domain(site_id, custom_domain) + print(f"custom domain was set to {custom_domain}") + + +def cli_delete_site() -> None: + """ + Delete a site from Netlify + """ + try: + domain = sys.argv[sys.argv.index("--delete-site") + 1] + except (ValueError, IndexError): + try: + domain = sys.argv[sys.argv.index("--remove-site") + 1] + except IndexError: + print("Please provide a domain") + return + netlify_domain = domain + if netlify_domain.count(".") == 0: + netlify_domain = f"{domain}.netlify.app" + try: + site_id = get_site_id_from_netlify_domain(netlify_domain) + except NoResult: + print(f"No site found with domain '{domain}', trying custom domains") + if domain.count(".") == 0 and len(DOMAINS) == 1: + domain = f"{domain}.{DOMAINS[0]}" + try: + site_id = get_site_id_from_custom_domain(domain) + except NoResult: + print(f"No site found with custom domain '{domain}'") + return + delete_site(site_id) + print(f"site {domain} was deleted") + + +def delete_site(site_id: str) -> None: + URL = f"https://api.netlify.com/api/v1/sites/{site_id}" + response = requests.delete(URL, headers=AUTH_HEADER) + if not response.ok: + raise Exception("something went wrong") + + +def display_help() -> None: + print( + """ + netlify.py --help + netlify.py --root-dir --custom-domain + netlify.py --list-sites + netlify.py --custom-domain --domain + netlify.py --remove-custom-domain + netlify.py --delete-site + """ + ) + + +def deploy_site() -> None: + """ + Deploy a folder of web pages to Netlify + """ + custom_domain = None + if "--custom-domain" in sys.argv: + try: + custom_domain = sys.argv[sys.argv.index("--custom-domain") + 1] + except IndexError: + print("Please provide a domain") + return + if "--root-dir" not in sys.argv: + print("Please provide a root directory") + return + try: + root_dir = pl.Path(sys.argv[sys.argv.index("--root-dir") + 1]) + except IndexError: + print("Please provide a root directory") + return + + if ( + custom_domain is not None + and len(DOMAINS) == 1 + and custom_domain.count(".") == 0 + ): + custom_domain = f"{custom_domain}.{DOMAINS[0]}" + deploy_page_to_netlify(root_dir, custom_domain) + print("okay, should be all set!") + + +def cli_list_sites() -> None: + """ + List all sites on Netlify + """ + URL = "https://api.netlify.com/api/v1/sites" + response = requests.get(URL, headers=AUTH_HEADER) + if not response.ok: + raise Exception("something went wrong") + rj = response.json() + + print("sites without custom domains:") + for site in rj: + if site["custom_domain"] is None: + print(f"{site['name']}: {site['url']}") + + print() + + print("sites with custom domains:") + for site in rj: + if site["custom_domain"] is not None: + print(f"{site['name']}: {site['url']}") + + +def main() -> None: + if "--help" in sys.argv: + display_help() + sys.exit() + elif "--list-sites" in sys.argv: + cli_list_sites() + sys.exit() + elif "--remove-custom-domain" in sys.argv: + try: + cli_remove_custom_domain() + except NoResult: + print("No site found with that custom domain") + sys.exit() + elif "--delete-site" in sys.argv or "--remove-site" in sys.argv: + cli_delete_site() + sys.exit() + elif "--custom-domain" in sys.argv: + cli_set_custom_domain() + sys.exit() + else: + deploy_site() + + +if __name__ == "__main__": + main()