mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-06 20:48:40 +00:00
Fix flake update workflow (#184)
This commit is contained in:
166
.github/workflows/commit.py
vendored
Executable file
166
.github/workflows/commit.py
vendored
Executable file
@@ -0,0 +1,166 @@
|
||||
import datetime
|
||||
import time
|
||||
import subprocess
|
||||
import requests
|
||||
import os
|
||||
import base64
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
class TreeEntry(TypedDict):
|
||||
path: str
|
||||
mode: Literal["100644", "100755", "120000"]
|
||||
type: Literal["blob", "tree"]
|
||||
sha: str
|
||||
|
||||
|
||||
# GitHub API configuration
|
||||
GITHUB_API_URL = "https://api.github.com"
|
||||
GITHUB_TOKEN = os.environ.get("BEARER_TOKEN")
|
||||
if not GITHUB_TOKEN:
|
||||
raise Exception("Supply BEARER_TOKEN env var")
|
||||
REPO = os.environ.get("GITHUB_REPOSITORY")
|
||||
if not REPO:
|
||||
raise Exception("Supply GITHUB_REPOSITORY env var")
|
||||
GITHUB_BASE_REF = os.environ.get("GITHUB_BASE_REF") or ""
|
||||
if not GITHUB_BASE_REF:
|
||||
raise Exception("Supply GITHUB_BASE_REF env var")
|
||||
GITHUB_OUTPUT = os.environ.get("GITHUB_OUTPUT") or ""
|
||||
if not GITHUB_OUTPUT:
|
||||
raise Exception("Supply GITHUB_OUTPUT env var")
|
||||
|
||||
headers = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"Authorization": f"Bearer {GITHUB_TOKEN}",
|
||||
"X-GitHub-Api-Version": "2022-11-28"
|
||||
}
|
||||
|
||||
def get_git_diff():
|
||||
"""Get the files which have changed in the current repository."""
|
||||
return subprocess.check_output(["git", "diff", "--name-only"]).decode("utf-8").splitlines()
|
||||
|
||||
def create_blob(content, encoding="utf-8"):
|
||||
"""Create a blob in the GitHub repository."""
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO}/git/blobs"
|
||||
data = {
|
||||
"content": content,
|
||||
"encoding": encoding
|
||||
}
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if not response.ok:
|
||||
raise Exception(f"bad response: {response}")
|
||||
print(f"Blob response: {response.text}")
|
||||
return response.json()["sha"]
|
||||
|
||||
def create_tree(base_tree: str, changes: list[TreeEntry]) -> str:
|
||||
"""Create a new tree with the given changes."""
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO}/git/trees"
|
||||
data = {
|
||||
"base_tree": base_tree,
|
||||
"tree": changes
|
||||
}
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if not response.ok:
|
||||
raise Exception(f"bad response: {response}")
|
||||
print(f"Tree response: {response.text}")
|
||||
return response.json()["sha"]
|
||||
|
||||
def create_commit(tree_sha: str, parent_sha: str, message: str):
|
||||
"""Create a new commit."""
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO}/git/commits"
|
||||
data = {
|
||||
"message": message,
|
||||
"tree": tree_sha,
|
||||
"parents": [parent_sha]
|
||||
}
|
||||
print(f"Commit request body: {data}")
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if not response.ok:
|
||||
raise Exception(f"bad response: {response}")
|
||||
print(f"Commit response: {response.text}")
|
||||
json = response.json()
|
||||
print(f"Commit: {json}")
|
||||
return json["sha"]
|
||||
|
||||
def is_executable(filepath: str):
|
||||
return os.path.isfile(filepath) and os.access(filepath, os.X_OK)
|
||||
|
||||
def get_current_commit() -> str:
|
||||
return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("utf-8").strip()
|
||||
|
||||
def get_current_tree() -> str:
|
||||
return [line for line in subprocess.check_output(["git", "cat-file", "-p", "HEAD"]).decode("utf-8").splitlines() if line.startswith('tree ')][0][5:]
|
||||
|
||||
def create_branch(branch_name: str, commit_sha: str) -> None:
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO}/git/refs"
|
||||
data = {
|
||||
"ref": f"refs/heads/{branch_name}",
|
||||
"sha": commit_sha
|
||||
}
|
||||
print(f"Branch creation request body: {data}")
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if not response.ok:
|
||||
raise Exception(f"bad response: {response}")
|
||||
print(f"Branch creation response: {response.text}")
|
||||
|
||||
def create_pull_request(title: str, branch_name: str, base_branch: str) -> tuple[str, int]:
|
||||
"""Returns the URL of the new PR."""
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO}/pulls"
|
||||
data = {
|
||||
"title": title,
|
||||
"head": branch_name,
|
||||
"base": base_branch,
|
||||
"body": "Automated pull request.",
|
||||
"maintainer_can_modify": True
|
||||
}
|
||||
print(f"PR creation request body: {data}")
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if not response.ok:
|
||||
raise Exception(f"bad response: {response}")
|
||||
print(f"PR creation response: {response.text}")
|
||||
json = response.json()
|
||||
return json["url"], json["number"]
|
||||
|
||||
def main():
|
||||
changed_files = get_git_diff()
|
||||
|
||||
# Create blobs and prepare tree changes
|
||||
tree_changes = []
|
||||
for file_path in changed_files:
|
||||
with open(file_path, "rb") as file:
|
||||
contents = base64.b64encode(file.read()).decode('ascii')
|
||||
blob_sha = create_blob(contents, encoding="base64")
|
||||
if is_executable(file_path):
|
||||
mode = "100755"
|
||||
else:
|
||||
mode = "100644"
|
||||
tree_changes.append(TreeEntry({
|
||||
"path": file_path,
|
||||
"mode": mode,
|
||||
"type": "blob",
|
||||
"sha": blob_sha
|
||||
}))
|
||||
|
||||
base_tree = get_current_tree()
|
||||
|
||||
# Create a new tree
|
||||
new_tree_sha = create_tree(base_tree, tree_changes)
|
||||
print(f"Tree: {new_tree_sha}")
|
||||
|
||||
# Create a new commit
|
||||
commit_message = "Automated commit"
|
||||
new_commit_sha = create_commit(new_tree_sha, get_current_commit(), commit_message)
|
||||
print(f"New commit created: {new_commit_sha}")
|
||||
|
||||
branch_name = f"flake_update" + datetime.datetime.fromtimestamp(time.time()).strftime('%Y_%m_%d-%H_%M_%S_%f')
|
||||
create_branch(branch_name, new_commit_sha)
|
||||
print(f"Branch created: {branch_name}")
|
||||
|
||||
url, pr_num = create_pull_request(title="Upgrade Nix flake and deps", branch_name=branch_name, base_branch=GITHUB_BASE_REF)
|
||||
print(f"See PR at: {url}")
|
||||
|
||||
with open(GITHUB_OUTPUT, "a") as output_file:
|
||||
output_file.write(f"pull-request-number={pr_num}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
42
.github/workflows/flake_update.yaml
vendored
42
.github/workflows/flake_update.yaml
vendored
@@ -14,43 +14,45 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@V27
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update Nix flake
|
||||
run: 'nix flake update'
|
||||
|
||||
- name: Build passthru
|
||||
run: 'nix build .#default.passthru.fetch-deps'
|
||||
run: 'nix build ".#default.passthru.fetch-deps"'
|
||||
|
||||
- name: Run passthru
|
||||
run: 'cp "$(./result | grep "Successfully wrote " | cut -d / -f 2-)" nix/deps.nix'
|
||||
run: |
|
||||
set -o pipefail
|
||||
./result | tee /tmp/passthru.txt
|
||||
cp /"$(cat /tmp/passthru.txt | grep " wrote lockfile to " | cut -d / -f 2-)" nix/deps.nix
|
||||
|
||||
- name: Format
|
||||
run: 'nix develop .#ci --command alejandra .'
|
||||
run: 'nix develop --command alejandra .'
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v6.1.0
|
||||
- name: Create token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
commit-message: Update Nix flake
|
||||
title: Weekly Nix flake update
|
||||
body: |
|
||||
This is an automated pull request to update the Nix flake.
|
||||
# https://github.com/actions/create-github-app-token/issues/136
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
|
||||
Changes:
|
||||
```
|
||||
${{ steps.create-diff.outputs.diff }}
|
||||
```
|
||||
branch: nix-flake-update
|
||||
delete-branch: true
|
||||
- name: Prepare to create commit
|
||||
run: python -m venv /tmp/venv && /tmp/venv/bin/python -m pip install -r .github/workflows/requirements.txt
|
||||
|
||||
- name: Create pull request
|
||||
env:
|
||||
BEARER_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
run: /tmp/venv/bin/python .github/workflows/commit.py
|
||||
|
||||
- name: Enable Pull Request Automerge
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
|
1
.github/workflows/requirements.txt
vendored
Normal file
1
.github/workflows/requirements.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
requests
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ result
|
||||
.analyzerpackages/
|
||||
analysis.sarif
|
||||
.direnv/
|
||||
.venv/
|
||||
|
Reference in New Issue
Block a user