Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save DavidMStraub/9ea640f84c84e27546020147edd08ba5 to your computer and use it in GitHub Desktop.

Select an option

Save DavidMStraub/9ea640f84c84e27546020147edd08ba5 to your computer and use it in GitHub Desktop.
Upload a file to a Github repository using the Github API
#!/usr/bin/env python3
"""Upload a file to a Github repository using the Github API.
Requirements:
- Install the `httpx` and `click` libraries: `pip install click httpx`
- Set your GitHub token, repository owner, and repository name in the script.
- Get a GitHub personal access token with permissions to write repo contents
(https://github.com/settings/personal-access-tokens)
Usage:
```
python md2gh.py file-to-upload --token github_pat_XXXX \
--owner YourGithubUser --repo your-repo-name
```
"""
import base64
import hashlib
import logging
import pathlib
import click
import httpx
def hash_blob_sha1(file_content: bytes) -> str:
"""Compute the SHA1 of file contents like git would."""
size = len(file_content)
header = f"blob {size}\0".encode("utf-8")
store = header + file_content
return hashlib.sha1(store).hexdigest()
@click.command()
@click.argument(
"filepath", type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path)
)
@click.option("--token", required=True, help="Github Personal Access Token")
@click.option("--owner", required=True, help="Github Repository Owner")
@click.option("--repo", required=True, help="Github Repository Name")
@click.option("--folder", required=False, help="Github Repository Subfolder")
def main(filepath: pathlib.Path, token: str, owner: str, repo: str, folder: str | None) -> None:
"""Upload the file `filepath` a Guthub repo."""
file_content = filepath.read_bytes()
encoded_content = base64.b64encode(file_content).decode("utf-8")
if folder:
path = f"{folder}/{filepath.name}"
else:
path = filepath.name
api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
payload = {
"message": f"Add new blog post: {filepath.name}",
"content": encoded_content,
}
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
}
logging.debug("Checking if remote file exists")
try:
response = httpx.get(api_url, headers=headers)
except Exception as exc:
print(f"Error connecting to Github: {exc}")
return
if response.status_code == 200:
remote_sha = response.json()["sha"]
local_sha = hash_blob_sha1(file_content)
if remote_sha == local_sha:
print(f"File '{filepath.name}' already exists and content is identical.")
return
logging.debug("Updating remote file")
payload["sha"] = remote_sha
logging.debug("Uploading file")
response = httpx.put(api_url, headers=headers, json=payload)
data = response.json()
if response.status_code in {200, 201}:
print(f"Successfully uploaded file. View at {data['content']['html_url']}")
else:
print(f"Error uploading file: {data}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment