Source code for historia._create_project
import os
import warnings
import beartype
import requests
[docs]
@beartype.beartype
def create_project_page(*, owner: str, title: str) -> dict[str, str]:
"""
Create a GitHub Project (v2) page for the specified owner.
Parameters
----------
owner : str
The GitHub user or organization login under which to create the project.
title : str
The title of the new GitHub Project.
Returns
-------
dict[str, str]
A dictionary containing the ``"id"`` and ``"url"`` of the created project.
Returns an empty dictionary if the GitHub API rate limit was hit (HTTP 403);
a warning is also issued in that case.
"""
github_token = os.getenv("GITHUB_TOKEN")
if github_token is None:
message = "\nPlease set the `GITHUB_TOKEN` environment variable with a valid GitHub Personal Access Token!\n\n"
raise ValueError(message)
headers = {"Authorization": f"token {github_token}"}
# Resolve the owner's global node ID (works for both users and organizations)
owner_id = _get_owner_node_id(owner=owner, headers=headers)
# Create the GitHub Project v2 via the GraphQL API
mutation = """
mutation CreateProject($ownerId: ID!, $title: String!) {
createProjectV2(input: {ownerId: $ownerId, title: $title}) {
projectV2 {
id
url
}
}
}
"""
variables = {"ownerId": owner_id, "title": title}
response = requests.post(
url="https://api.github.com/graphql",
json={"query": mutation, "variables": variables},
headers=headers,
timeout=30,
)
status = response.status_code
result = response.json()
message = f"GitHub GraphQL API mutation to create project `{title}` failed!\nStatus code {status}: {result}"
if status == 403:
warnings.warn(message=message, stacklevel=2)
return {}
if "errors" in result or status != 200:
raise RuntimeError(message)
project = result["data"]["createProjectV2"]["projectV2"]
project_id = project["id"]
# Create the canonical date fields expected by the populate / update-dates commands
_create_date_field(project_id=project_id, field_name="Start date", headers=headers)
_create_date_field(project_id=project_id, field_name="End date", headers=headers)
return {"id": project_id, "url": project["url"]}
def _create_date_field(*, project_id: str, field_name: str, headers: dict[str, str]) -> None:
"""
Create a DATE field with the given name on a GitHub Project (v2).
Parameters
----------
project_id : str
The global node ID of the project.
field_name : str
The name to assign to the new DATE field (e.g. ``"Start date"``).
headers : dict[str, str]
HTTP headers including the Authorization token.
"""
mutation = """
mutation CreateField($projectId: ID!, $name: String!) {
createProjectV2Field(input: {projectId: $projectId, dataType: DATE, name: $name}) {
projectV2Field {
... on ProjectV2Field {
id
name
}
}
}
}
"""
variables = {"projectId": project_id, "name": field_name}
response = requests.post(
url="https://api.github.com/graphql",
json={"query": mutation, "variables": variables},
headers=headers,
timeout=30,
)
status = response.status_code
result = response.json()
if "errors" in result or status != 200:
message = f"GitHub GraphQL API mutation to create field `{field_name}` failed!\nStatus code {status}: {result}"
raise RuntimeError(message)
def _get_owner_node_id(*, owner: str, headers: dict[str, str]) -> str:
"""
Resolve the GitHub node ID for a user or organization login.
Parameters
----------
owner : str
The GitHub user or organization login.
headers : dict[str, str]
HTTP headers including the Authorization token.
Returns
-------
str
The global node ID of the owner.
"""
# Try user endpoint first, then organization endpoint
for endpoint in (f"https://api.github.com/users/{owner}", f"https://api.github.com/orgs/{owner}"):
response = requests.get(url=endpoint, headers=headers, timeout=30)
if response.status_code == 200:
return response.json()["node_id"]
message = f"Could not resolve GitHub node ID for owner `{owner}`. Please verify the login is correct."
raise ValueError(message)