Last active
January 29, 2026 15:45
-
-
Save phaer/70ad6674157589cfc54be6d86c9c30cc to your computer and use it in GitHub Desktop.
Periodic flake-updates for buildbot_effects
This file contains hidden or 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
| # Flake Update Effect for Forgejo | |
| # | |
| # Creates a hercules-ci-effects effect that: | |
| # 1. Runs `nix flake update` | |
| # 2. Commits changes to a branch | |
| # 3. Opens a Pull Request on Forgejo | |
| # | |
| # # Example | |
| # ```nix | |
| # herculesCI = args: { | |
| # onSchedule.flake-update = { | |
| # when = { hour = 4; minute = 0; dayOfWeek = [ "Mon" ]; }; | |
| # outputs.effects.update = withSystem "x86_64-linux" ({ pkgs, hci-effects, ... }: | |
| # import ./flake-update.nix { inherit hci-effects pkgs args; } {} | |
| # ); | |
| # }; | |
| # }; | |
| # ``` | |
| { | |
| hci-effects, | |
| pkgs, | |
| args, | |
| }: | |
| { | |
| baseBranch ? "main", | |
| updateBranch ? "flake-update", | |
| prTitle ? "flake.lock: Update", | |
| prBody ? "Automated flake.lock update", | |
| secretName ? "forgejo", | |
| gitUser ? "Flake Update Bot", | |
| gitEmail ? "[email protected]", | |
| inputs ? null, # null = update all inputs, or list of input names | |
| }: | |
| let | |
| # Parse remoteHttpUrl: "https://forgejo.example.com/owner/repo.git" | |
| # Also handles URLs with username: "https://[email protected]/owner/repo.git" | |
| url = args.primaryRepo.remoteHttpUrl; | |
| cleanUrl = builtins.replaceStrings [ ".git" ] [ "" ] url; | |
| # Split by "/" - builtins.split returns a list with matches and separators | |
| # For "https://forgejo.example.com/owner/repo" this gives: | |
| # ["https:" "" "forgejo.example.com" "owner" "repo"] | |
| parts = builtins.filter (x: x != "" && builtins.isString x) (builtins.split "/" cleanUrl); | |
| hostPart = builtins.elemAt parts 1; | |
| # Strip username@ prefix if present (e.g., "[email protected]" -> "git.example.com") | |
| hostSplit = builtins.filter builtins.isString (builtins.split "@" hostPart); | |
| host = builtins.elemAt hostSplit (builtins.length hostSplit - 1); | |
| forgejoUrl = "https://${host}"; | |
| owner = builtins.elemAt parts 2; | |
| repo = builtins.elemAt parts 3; | |
| inputArgs = | |
| if inputs == null then | |
| "" | |
| else | |
| builtins.concatStringsSep " " (map (i: "--update-input ${i}") inputs); | |
| effectScript = '' | |
| set -euo pipefail | |
| # Read Forgejo token from secrets | |
| if [[ ! -f /run/secrets.json ]]; then | |
| echo "Error: /run/secrets.json not found" | |
| exit 1 | |
| fi | |
| FORGEJO_TOKEN=$(jq -r '.${secretName}.data.token // empty' /run/secrets.json) | |
| if [[ -z "$FORGEJO_TOKEN" ]]; then | |
| echo "Error: Forgejo token not found in secrets.json under '${secretName}.data.token'" | |
| exit 1 | |
| fi | |
| WORK_DIR=$(mktemp -d) | |
| cd "$WORK_DIR" | |
| trap 'rm -rf "$WORK_DIR"' EXIT | |
| echo "Cloning repository..." | |
| AUTH_URL="https://oauth2:$FORGEJO_TOKEN@${host}/${owner}/${repo}.git" | |
| git clone --depth 1 --branch "${baseBranch}" "$AUTH_URL" repo | |
| cd repo | |
| git config user.name "${gitUser}" | |
| git config user.email "${gitEmail}" | |
| ORIGINAL_HASH=$(sha256sum flake.lock 2>/dev/null | cut -d' ' -f1 || echo "") | |
| echo "Running nix flake update..." | |
| nix --extra-experimental-features "nix-command flakes" flake update ${inputArgs} | |
| NEW_HASH=$(sha256sum flake.lock | cut -d' ' -f1) | |
| if [[ "$ORIGINAL_HASH" == "$NEW_HASH" ]]; then | |
| echo "No changes to flake.lock, nothing to do" | |
| exit 0 | |
| fi | |
| echo "flake.lock has changed, creating PR..." | |
| git switch -C "${updateBranch}" | |
| git add flake.lock | |
| git commit -m "${prTitle}" | |
| if git fetch origin "${updateBranch}" 2>/dev/null; then | |
| REMOTE_REF=$(git rev-parse FETCH_HEAD) | |
| git push --force-with-lease="${updateBranch}:$REMOTE_REF" origin "${updateBranch}" | |
| else | |
| git push origin "${updateBranch}" | |
| fi | |
| API_URL="${forgejoUrl}/api/v1" | |
| EXISTING_PR=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \ | |
| "$API_URL/repos/${owner}/${repo}/pulls?state=open&head=${owner}:${updateBranch}" \ | |
| | jq 'length') | |
| if [[ "$EXISTING_PR" -gt 0 ]]; then | |
| echo "PR already exists for branch '${updateBranch}', updated with new commits" | |
| exit 0 | |
| fi | |
| echo "Creating Pull Request..." | |
| PR_RESPONSE=$(curl -s -X POST \ | |
| -H "Authorization: token $FORGEJO_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$(jq -n \ | |
| --arg title "${prTitle}" \ | |
| --arg body "${prBody}" \ | |
| --arg head "${updateBranch}" \ | |
| --arg base "${baseBranch}" \ | |
| '{title: $title, body: $body, head: $head, base: $base}')" \ | |
| "$API_URL/repos/${owner}/${repo}/pulls") | |
| PR_NUMBER=$(echo "$PR_RESPONSE" | jq -r '.number // empty') | |
| if [[ -n "$PR_NUMBER" ]]; then | |
| echo "Created PR #$PR_NUMBER: ${forgejoUrl}/${owner}/${repo}/pulls/$PR_NUMBER" | |
| else | |
| echo "Failed to create PR. Response:" | |
| echo "$PR_RESPONSE" | jq . | |
| exit 1 | |
| fi | |
| ''; | |
| in | |
| hci-effects.mkEffect { | |
| inherit effectScript; | |
| inputs = [ | |
| pkgs.git | |
| pkgs.jq | |
| pkgs.curl | |
| pkgs.nix | |
| pkgs.coreutils | |
| ]; | |
| secretsMap.${secretName} = secretName; | |
| } |
This file contains hidden or 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
| { | |
| herculesCI = | |
| args: | |
| let | |
| pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; | |
| hci-effects = inputs.hercules-ci-effects.lib.withPkgs pkgs; | |
| in | |
| { | |
| onPush.default.outputs.effects = { }; | |
| onSchedule = { | |
| flake-update = { | |
| when = { | |
| hour = [ 11 12 13 14 15 16 17 ]; | |
| minute = 20; | |
| dayOfWeek = [ | |
| "Mon" | |
| "Tue" | |
| "Wed" | |
| "Thu" | |
| ]; | |
| }; | |
| outputs.effects.update = import ./effects/flake-update.nix { inherit hci-effects pkgs args; } { | |
| gitEmail = "[email protected]"; | |
| }; | |
| }; | |
| }; | |
| }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment