Skip to content

Commit 34a070c

Browse files
committed
feat(ci): combine all flake update operations
1 parent 286b19a commit 34a070c

File tree

6 files changed

+529
-176
lines changed

6 files changed

+529
-176
lines changed

.github/shared/README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Reusable Workflows
2+
3+
This repository provides a reusable GitHub Actions workflow for automatically updating Nix flakes.
4+
5+
## Workflow
6+
7+
### `update-flake.yml`
8+
9+
A unified workflow that updates both Nix packages and flake inputs in your repository.
10+
11+
#### Features
12+
- Updates all packages and flake inputs in a single workflow run
13+
- Creates individual pull requests for each update
14+
- Runs updates in parallel using GitHub Actions matrix
15+
- Supports filtering specific packages or inputs
16+
- Configurable auto-merge for approved updates
17+
18+
#### Inputs
19+
20+
| Input | Description | Required | Default |
21+
|-------|-------------|----------|---------|
22+
| `packages` | Space-separated list of specific packages to update (empty for all) | No | `''` |
23+
| `inputs` | Space-separated list of specific inputs to update (empty for all) | No | `''` |
24+
| `update-command` | Nix command to run the package update tool | No | `'.#update-packages'` |
25+
| `pr-labels` | Comma-separated list of labels to add to PRs | No | `'dependencies,automated'` |
26+
| `auto-merge` | Enable auto-merge for created pull requests | No | `false` |
27+
28+
#### Secrets
29+
30+
| Secret | Description | Required |
31+
|--------|-------------|----------|
32+
| `github-token` | GitHub token for creating pull requests (GitHub App token recommended) | Yes |
33+
34+
## Usage
35+
36+
Create `.github/workflows/update-flake.yml`:
37+
38+
```yaml
39+
name: Update Flake
40+
on:
41+
schedule:
42+
# Run daily at 2 AM UTC
43+
- cron: '0 2 * * *'
44+
workflow_dispatch:
45+
inputs:
46+
packages:
47+
description: 'Specific packages to update (space-separated, empty for all)'
48+
required: false
49+
default: ''
50+
inputs:
51+
description: 'Specific flake inputs to update (space-separated, empty for all)'
52+
required: false
53+
default: ''
54+
55+
jobs:
56+
generate-token:
57+
runs-on: ubuntu-latest
58+
outputs:
59+
token: ${{ steps.app-token.outputs.token }}
60+
steps:
61+
- name: Generate GitHub App Token
62+
id: app-token
63+
uses: actions/create-github-app-token@v2
64+
with:
65+
app-id: ${{ secrets.APP_ID }}
66+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
67+
68+
update:
69+
needs: generate-token
70+
uses: numtide/nix-ai-tools/.github/shared/update-flake.yml@main
71+
with:
72+
packages: ${{ github.event.inputs.packages }}
73+
inputs: ${{ github.event.inputs.inputs }}
74+
pr-labels: 'dependencies,automated'
75+
auto-merge: true
76+
secrets:
77+
github-token: ${{ needs.generate-token.outputs.token }}
78+
permissions:
79+
contents: write
80+
pull-requests: write
81+
```
82+
83+
## Setting Up GitHub App
84+
85+
Using a GitHub App token is recommended to ensure PR checks run properly:
86+
87+
1. Go to Settings → Developer settings → GitHub Apps → New GitHub App
88+
2. Set these permissions:
89+
- **Repository permissions:**
90+
- Contents: Write
91+
- Pull requests: Write
92+
- Metadata: Read
93+
- Actions: Write (if using auto-merge)
94+
3. Install the app on your repository
95+
4. Add secrets to your repository:
96+
- `APP_ID`: The App ID from the app settings
97+
- `APP_PRIVATE_KEY`: The private key generated for the app
98+
99+
## Package Update Command Requirements
100+
101+
Your update command (default: `.#update-packages`) must support:
102+
- `--list-only` flag that outputs a JSON array of package names
103+
- Accepting a package name as argument to update that specific package
104+
- Proper exit codes (0 for success, non-zero for failure)
105+
106+
Example output for `--list-only`:
107+
```json
108+
["claude-code", "opencode", "gemini-cli"]
109+
```
110+
111+
## How It Works
112+
113+
1. **Discovery**: Finds all packages (via update command) and flake inputs
114+
2. **Matrix Build**: Creates a job matrix combining both types of updates
115+
3. **Parallel Updates**: Each item is updated in its own job
116+
4. **PR Creation**: Creates or updates pull requests with appropriate titles
117+
5. **Summary**: Reports the overall status of all updates
118+
119+
## Pull Request Format
120+
121+
**Package updates:**
122+
- Title: `package-name: 1.0.0 -> 1.1.0`
123+
- Branch: `update/package-name`
124+
125+
**Flake input updates:**
126+
- Title: `flake.lock: Update input-name`
127+
- Branch: `update-input-name`
128+
129+
Both types use squash merging when auto-merge is enabled.

.github/shared/create-update-pr.sh

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Script to create or update a pull request for package/flake updates
5+
# Usage: create-update-pr.sh <type> <name> <current_version> <new_version>
6+
# type: "package" or "flake-input"
7+
# name: package name or input name
8+
# current_version: current version/revision
9+
# new_version: new version/revision
10+
11+
type="$1"
12+
name="$2"
13+
current_version="$3"
14+
new_version="$4"
15+
16+
# Extract optional arguments from environment
17+
pr_labels="${PR_LABELS:-dependencies,automated}"
18+
auto_merge="${AUTO_MERGE:-false}"
19+
20+
# Handle type-specific logic
21+
if [ "$type" = "package" ]; then
22+
branch="update/${name}"
23+
pr_title="${name}: ${current_version} -> ${new_version}"
24+
pr_body="Automated update of ${name} from ${current_version} to ${new_version}."
25+
elif [ "$type" = "flake-input" ]; then
26+
branch="update-${name}"
27+
pr_title="flake.lock: Update ${name}"
28+
29+
pr_body="This PR updates the flake input \`${name}\` to the latest version.
30+
31+
## Changes
32+
- ${name}: \`${current_version}\`\`${new_version}\`"
33+
else
34+
echo "Error: Unknown type '$type'. Must be 'package' or 'flake-input'."
35+
exit 1
36+
fi
37+
38+
# Check if branch exists on remote
39+
if git ls-remote --heads origin "$branch" | grep -q "$branch"; then
40+
echo "Branch exists, checking out..."
41+
git fetch origin "$branch"
42+
git checkout "$branch"
43+
git reset --hard HEAD~1 # Remove the last commit to update it
44+
else
45+
echo "Creating new branch..."
46+
git checkout -b "$branch"
47+
fi
48+
49+
# Stage and commit changes
50+
git add .
51+
52+
if [ "$type" = "flake-input" ]; then
53+
commit_message="$pr_title
54+
55+
${current_version} -> ${new_version}"
56+
else
57+
commit_message="$pr_title"
58+
fi
59+
60+
git commit -m "$commit_message" --signoff
61+
62+
# Push the branch
63+
git push --force origin "$branch"
64+
65+
# Check if PR already exists
66+
pr_number=$(gh pr list --head "$branch" --json number --jq '.[0].number // empty')
67+
68+
if [ -n "$pr_number" ]; then
69+
echo "Updating existing PR #$pr_number"
70+
gh pr edit "$pr_number" \
71+
--title "$pr_title" \
72+
--body "$pr_body"
73+
else
74+
echo "Creating new PR"
75+
# Build label arguments
76+
label_args=""
77+
IFS=',' read -ra labels <<< "$pr_labels"
78+
for label in "${labels[@]}"; do
79+
label_args="$label_args --label \"$(echo "$label" | xargs)\""
80+
done
81+
82+
eval gh pr create \
83+
--title "\"$pr_title\"" \
84+
--body "\"$pr_body\"" \
85+
--base main \
86+
--head "$branch" \
87+
$label_args
88+
89+
# Store PR number for auto-merge
90+
pr_number=$(gh pr list --head "$branch" --json number --jq '.[0].number')
91+
fi
92+
93+
# Enable auto-merge if requested
94+
if [ "$auto_merge" = "true" ] && [ -n "$pr_number" ]; then
95+
echo "Enabling auto-merge for PR #$pr_number"
96+
gh pr merge "$pr_number" --auto --squash || echo "Note: Auto-merge may require branch protection rules"
97+
fi

0 commit comments

Comments
 (0)