name: Daily Flake Update for Earth System on: schedule: # Run daily at midnight UTC - cron: "0 0 * * *" workflow_dispatch: # Allow manual triggering env: BRANCH_NAME: automated/flake-update PR_TITLE: "Automated: Update flake inputs" HYDRA_URL: "https://hydra.zoeys.computer" SYSTEM_NAME: "earth" HYDRA_PROJECT: "config" HYDRA_JOBSET: "config-build" HYDRA_JOB: "x86_64-linux.earth" FORGEJO_API_URL: "${{ github.server_url }}/api/v1" jobs: update-flake: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Nix uses: DeterminateSystems/nix-installer-action@main with: github-token: ${{ secrets.GITHUB_TOKEN }} extra-conf: | experimental-features = nix-command flakes access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - name: Check for existing PR id: check-pr run: | # Get repository information REPO_OWNER=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 1) REPO_NAME=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 2) # Query Forgejo API for existing PRs PR_DATA=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/pulls?state=open&head=${REPO_OWNER}:${{ env.BRANCH_NAME }}") # Check if PR exists and extract PR number PR_COUNT=$(echo "$PR_DATA" | jq '. | length') if [[ "$PR_COUNT" -gt 0 ]]; then PR_NUMBER=$(echo "$PR_DATA" | jq -r '.[0].number') echo "Found existing PR #$PR_NUMBER" echo "pr_exists=true" >> $GITHUB_OUTPUT echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT else echo "No existing PR found" echo "pr_exists=false" >> $GITHUB_OUTPUT echo "pr_number=" >> $GITHUB_OUTPUT fi - name: Set up branch run: | git config --global user.name "Flake Update Bot" git config --global user.email "bot@example.com" if [[ "${{ steps.check-pr.outputs.pr_exists }}" == "true" ]]; then # Update existing branch git fetch origin ${{ env.BRANCH_NAME }} git checkout ${{ env.BRANCH_NAME }} else # Create new branch git checkout -b ${{ env.BRANCH_NAME }} fi - name: Update flake inputs id: flake-update run: | cd nixos nix flake update if [[ -z "$(git status --porcelain)" ]]; then echo "No changes to commit" echo "changes_made=false" >> $GITHUB_OUTPUT exit 0 else git add flake.lock git commit -m "chore: update flake inputs" git push -f origin ${{ env.BRANCH_NAME }} echo "changes_made=true" >> $GITHUB_OUTPUT fi - name: Create or update PR id: create-pr if: steps.flake-update.outputs.changes_made == 'true' run: | # Get repository information REPO_OWNER=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 1) REPO_NAME=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 2) if [[ "${{ steps.check-pr.outputs.pr_exists }}" == "true" ]]; then echo "Updating existing PR #${{ steps.check-pr.outputs.pr_number }}" PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" # Update PR with comment about new update curl -s -X POST \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"body": "Updated flake inputs with latest versions"}' \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT else echo "Creating new PR" # Create new PR using API PR_DATA=$(curl -s -X POST \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{ "title": "'"${{ env.PR_TITLE }}"'", "body": "This is an automated PR to update flake inputs. It will be automatically merged if the Hydra build for the Earth system passes.", "head": "'"${{ env.BRANCH_NAME }}"'", "base": "main", "draft": true }' \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/pulls") PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number') echo "Created PR #$PR_NUMBER" echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT fi - name: Wait for Hydra build to start run: | PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" if [[ -z "$PR_NUMBER" ]]; then PR_NUMBER="${{ steps.create-pr.outputs.pr_number }}" fi echo "Waiting for Hydra build to start (checking every 60 seconds, timeout after 30 minutes)..." MAX_ATTEMPTS=30 ATTEMPT=0 while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do ATTEMPT=$((ATTEMPT+1)) # Get the latest commit SHA from the branch COMMIT_SHA=$(git rev-parse HEAD) # Check if Hydra build exists for this commit using API BUILD_STATUS=$(curl -s "${{ env.HYDRA_URL }}/api/jobsets?project=${{ env.HYDRA_PROJECT }}" || echo "") if [[ "$BUILD_STATUS" == *"${{ env.HYDRA_JOBSET }}"* ]]; then echo "Hydra jobset found! Monitoring build status..." break fi echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: No Hydra build found yet, waiting 60 seconds..." sleep 60 done if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then echo "Timeout waiting for Hydra build to start. PR will remain in draft state." exit 0 fi - name: Monitor Hydra build id: hydra-build run: | PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" if [[ -z "$PR_NUMBER" ]]; then PR_NUMBER="${{ steps.create-pr.outputs.pr_number }}" fi echo "Monitoring Hydra build status (checking every 5 minutes, timeout after 6 hours)..." MAX_ATTEMPTS=72 ATTEMPT=0 while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do ATTEMPT=$((ATTEMPT+1)) # Get latest evaluation ID EVAL_INFO=$(curl -s "${{ env.HYDRA_URL }}/jobset/${{ env.HYDRA_PROJECT }}/${{ env.HYDRA_JOBSET }}/evals" || echo "") LATEST_EVAL_ID=$(echo "$EVAL_INFO" | grep -o -E '"id":[0-9]+' | head -n 1 | cut -d ':' -f 2) if [[ -z "$LATEST_EVAL_ID" ]]; then echo "No evaluation found yet, continuing to wait..." sleep 300 continue fi echo "Found latest evaluation ID: $LATEST_EVAL_ID" # Get build status for the specific job from evaluation EVAL_BUILDS=$(curl -s "${{ env.HYDRA_URL }}/eval/$LATEST_EVAL_ID/builds" || echo "") # Look for our specific job in the evaluation if [[ "$EVAL_BUILDS" == *"${{ env.HYDRA_JOB }}"* ]]; then # Extract buildstatus for our job JOB_STATUS=$(echo "$EVAL_BUILDS" | grep -o -E '"buildstatus":[0-9]+' | head -n 1 | cut -d ':' -f 2) if [[ "$JOB_STATUS" == "0" ]]; then echo "Build succeeded (status: $JOB_STATUS)!" echo "build_success=true" >> $GITHUB_OUTPUT break elif [[ "$JOB_STATUS" == "1" || "$JOB_STATUS" == "2" || "$JOB_STATUS" == "3" || "$JOB_STATUS" == "4" || "$JOB_STATUS" == "6" || "$JOB_STATUS" == "7" || "$JOB_STATUS" == "9" || "$JOB_STATUS" == "10" || "$JOB_STATUS" == "11" ]]; then echo "Build failed (status: $JOB_STATUS)!" echo "build_success=false" >> $GITHUB_OUTPUT break fi fi echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Build still in progress or not found, waiting 5 minutes..." sleep 300 done if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then echo "Timeout waiting for build to complete. PR will remain in draft state." echo "build_success=false" >> $GITHUB_OUTPUT fi - name: Mark PR ready for review or merge if build succeeded if: steps.hydra-build.outputs.build_success == 'true' run: | PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" if [[ -z "$PR_NUMBER" ]]; then PR_NUMBER="${{ steps.create-pr.outputs.pr_number }}" fi # Get repository information REPO_OWNER=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 1) REPO_NAME=$(echo "$GITHUB_REPOSITORY" | cut -d '/' -f 2) # Mark PR as ready for review (not draft) curl -s -X PATCH \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"draft": false}' \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${PR_NUMBER}" # Add comment about successful build curl -s -X POST \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"body": "Hydra build succeeded! Merging PR."}' \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" # Merge the PR curl -s -X POST \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{ "merge_method": "merge", "commit_title": "'"${{ env.PR_TITLE }}"'", "commit_message": "Automated merge after successful Hydra build for the Earth system." }' \ "${{ env.FORGEJO_API_URL }}/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${PR_NUMBER}/merge"