-
-
Notifications
You must be signed in to change notification settings - Fork 377
all-maintainers.nix: init; flake/dev/generate-all-maintainers: init #3525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,254 @@ | ||||||
| name: Update maintainers list | ||||||
|
|
||||||
| on: | ||||||
| push: | ||||||
| branches: | ||||||
| - main | ||||||
| paths: | ||||||
| - lib/maintainers.nix | ||||||
| schedule: | ||||||
| # Update every Monday at 9 AM UTC | ||||||
| - cron: "0 9 * * 1" | ||||||
| workflow_dispatch: | ||||||
| inputs: | ||||||
| create_pr: | ||||||
| description: "Create PR even if no changes" | ||||||
| required: false | ||||||
| default: false | ||||||
| type: boolean | ||||||
|
|
||||||
| jobs: | ||||||
| update-maintainers: | ||||||
| runs-on: ubuntu-latest | ||||||
| if: github.repository_owner == 'nix-community' || github.event_name == 'workflow_dispatch' | ||||||
| # Permissions required for workflow | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this makes it sound like this is the canonical place where the permissions are defined. However we actually have them in two places, and this is the less important of the two since it is only used on forks that don't have an APP_ID.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why this would be less important? These are required for a token to get generated with permissions required, the others are just to limit the scope of permissions granted to an app.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These dictate the permissions of I said they're less important because
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Understood, as part of previous response. Guess its differing opinion on what's important. I think declaring the app token permissions as less because they are optional for functionality. But, the app token permissions are just a couple lines lower with the exact same naming convention and didn't think it warranted copy/paste for same explanation. |
||||||
| # `contents`: to update maintainers file | ||||||
| # `pull-requests`: to create pr | ||||||
| # `issues`: to label pr | ||||||
| permissions: | ||||||
| contents: write | ||||||
| pull-requests: write | ||||||
| issues: write | ||||||
| env: | ||||||
| pr_branch: update/maintainers-${{ github.ref_name }} | ||||||
| steps: | ||||||
| - name: Create GitHub App token | ||||||
| uses: actions/create-github-app-token@v2 | ||||||
| if: vars.CI_APP_ID | ||||||
| id: app-token | ||||||
| with: | ||||||
| app-id: ${{ vars.CI_APP_ID }} | ||||||
| private-key: ${{ secrets.CI_APP_PRIVATE_KEY }} | ||||||
khaneliman marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| permission-contents: write | ||||||
| permission-pull-requests: write | ||||||
| permission-issues: write | ||||||
|
|
||||||
| - name: Get GitHub App user info | ||||||
| id: user-info | ||||||
| if: vars.CI_APP_ID | ||||||
| env: | ||||||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||||||
| slug: ${{ steps.app-token.outputs.app-slug }} | ||||||
MattSturgeon marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| run: | | ||||||
| name="$slug[bot]" | ||||||
| id=$(gh api "/users/$name" --jq .id) | ||||||
| { | ||||||
| echo "id=$id" | ||||||
| echo "name=$name" | ||||||
| echo "email=$id+$name@users.noreply.github.com" | ||||||
| } >> "$GITHUB_OUTPUT" | ||||||
|
|
||||||
| - name: Checkout repository | ||||||
| uses: actions/checkout@v4 | ||||||
| with: | ||||||
| token: ${{ steps.app-token.outputs.token || github.token }} | ||||||
|
|
||||||
| - name: Install Nix | ||||||
| uses: cachix/install-nix-action@v31 | ||||||
| with: | ||||||
| github_access_token: ${{ steps.app-token.outputs.token || github.token }} | ||||||
| extra_nix_config: | | ||||||
| allow-import-from-derivation = false | ||||||
|
|
||||||
| - name: Setup Git | ||||||
| env: | ||||||
| name: ${{ steps.user-info.outputs.name || 'github-actions[bot]' }} | ||||||
| email: ${{ steps.user-info.outputs.email || '41898282+github-actions[bot]@users.noreply.github.com' }} | ||||||
| run: | | ||||||
| git config user.name "$name" | ||||||
| git config user.email "$email" | ||||||
|
|
||||||
| - name: Generate updated maintainers list | ||||||
| run: | | ||||||
| echo "::group::📋 Generating updated generated/all-maintainers.nix..." | ||||||
| nix run .#generate-all-maintainers -- --root . --output generated/all-maintainers.nix | ||||||
| echo "::endgroup::" | ||||||
| echo "::group::🎨 Formatting with nixfmt..." | ||||||
| nix fmt generated/all-maintainers.nix | ||||||
| echo "::endgroup::" | ||||||
|
|
||||||
| - name: Check for changes | ||||||
| id: check-changes | ||||||
| run: | | ||||||
| if git diff --quiet generated/all-maintainers.nix; then | ||||||
| echo "No changes to generated/all-maintainers.nix" | ||||||
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | ||||||
| else | ||||||
| echo "Changes detected in generated/all-maintainers.nix" | ||||||
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | ||||||
| added=$(git diff --numstat generated/all-maintainers.nix | cut -f1) | ||||||
| removed=$(git diff --numstat generated/all-maintainers.nix | cut -f2) | ||||||
| echo "changes_summary=+$added -$removed lines" >> "$GITHUB_OUTPUT" | ||||||
| fi | ||||||
|
|
||||||
| - name: Validate generated file | ||||||
| if: steps.check-changes.outputs.has_changes == 'true' | ||||||
| run: | | ||||||
| echo "🔍 Validating generated generated/all-maintainers.nix..." | ||||||
| if nix-instantiate --eval generated/all-maintainers.nix --strict > /dev/null; then | ||||||
| echo "✅ Generated file has valid Nix syntax" | ||||||
| else | ||||||
| echo "❌ Generated file has invalid Nix syntax" | ||||||
| exit 1 | ||||||
| fi | ||||||
|
|
||||||
| - name: Create update branch | ||||||
| run: | | ||||||
| git branch -D "$pr_branch" || echo "Nothing to delete" | ||||||
| git switch -c "$pr_branch" | ||||||
|
|
||||||
| - name: Get info on the current PR | ||||||
| id: open_pr_info | ||||||
| env: | ||||||
| GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }} | ||||||
| run: | | ||||||
| # Query for info about the already open update PR | ||||||
| info=$( | ||||||
| gh api graphql -F owner='{owner}' -F repo='{repo}' -F branch="$pr_branch" -f query=' | ||||||
| query($owner:String!, $repo:String!, $branch:String!) { | ||||||
| repository(owner: $owner, name: $repo) { | ||||||
| pullRequests(first: 1, states: OPEN, headRefName: $branch) { | ||||||
| nodes { | ||||||
| number | ||||||
| url | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| ' | jq --raw-output ' | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI
Suggested change
Sometimes it's still worth it to pipe to jq though, e.g. if you want to use We use a similar graphql query elsewhere, but one issue I'm now spotting is that |
||||||
| .data.repository.pullRequests.nodes[] | ||||||
| | to_entries[] | ||||||
| | "\(.key)=\(.value)" | ||||||
| ' | ||||||
| ) | ||||||
| if [[ -n "$info" ]]; then | ||||||
| echo "PR info:" | ||||||
| echo "$info" | ||||||
| echo "$info" >> $GITHUB_OUTPUT | ||||||
| else | ||||||
| echo "No PR is currently open" | ||||||
| fi | ||||||
|
|
||||||
| - name: Fetch current PR's branch | ||||||
| if: steps.open_pr_info.outputs.number | ||||||
| run: | | ||||||
| git fetch origin "$pr_branch" | ||||||
| git branch --set-upstream-to "origin/$pr_branch" | ||||||
|
|
||||||
| - name: Create Pull Request | ||||||
| id: create-pr | ||||||
| if: steps.check-changes.outputs.has_changes == 'true' || github.event.inputs.create_pr == 'true' | ||||||
| env: | ||||||
| GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }} | ||||||
| title: "maintainers: update generated/all-maintainers.nix" | ||||||
| commit_body: | | ||||||
| Automated update of the master maintainers list combining: | ||||||
| - Nixvim specific maintainers from lib/maintainers.nix | ||||||
| - Nixpkgs maintainers referenced in Nixvim modules | ||||||
|
|
||||||
| Changes: ${{ steps.check-changes.outputs.changes_summary || 'No content changes' }} | ||||||
|
|
||||||
| Generated by: flake/dev/generate-all-maintainers/generate-all-maintainers.py | ||||||
| pr_url: ${{ steps.open_pr_info.outputs.url }} | ||||||
| pr_num: ${{ steps.open_pr_info.outputs.number }} | ||||||
| pr_body: | | ||||||
| ## 📋 Summary | ||||||
|
|
||||||
| This PR updates the master maintainers list (`generated/all-maintainers.nix`) which combines: | ||||||
| - **Nixvim specific maintainers** from `lib/maintainers.nix` | ||||||
| - **Nixpkgs maintainers** referenced in Nixvim modules | ||||||
|
|
||||||
| ## 🔄 Changes | ||||||
|
|
||||||
| **Statistics:** ${{ steps.check-changes.outputs.changes_summary || 'No content changes (format/comment updates only)' }} | ||||||
|
|
||||||
| The updated list includes all maintainers needed for review assignments across the Nixvim project. | ||||||
|
|
||||||
| ## 🤖 Automation | ||||||
|
|
||||||
| - **Generated by:** `flake/dev/generate-all-maintainers/generate-all-maintainers.py` | ||||||
| - **Trigger:** ${{ github.event_name == 'schedule' && 'Scheduled weekly update' || 'Manual workflow dispatch' }} | ||||||
| - **Validation:** File syntax verified with `nix-instantiate --eval` | ||||||
|
|
||||||
| ## 📚 Usage | ||||||
|
|
||||||
| This file can be imported and used for maintainer lookups: | ||||||
| ```nix | ||||||
| let allMaintainers = import ./generated/all-maintainers.nix; in | ||||||
| # Access any maintainer by name: allMaintainers.username | ||||||
| ``` | ||||||
|
|
||||||
| --- | ||||||
| 🤖 *This PR was automatically created by the [update-maintainers workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})* | ||||||
| run: | | ||||||
| git add generated/all-maintainers.nix | ||||||
| git commit -m "$title" -m "$commit_body" | ||||||
|
|
||||||
| echo "Pushing to remote branch $pr_branch" | ||||||
| git push --force --set-upstream origin "$pr_branch" | ||||||
|
|
||||||
| if [ -z "$pr_num" ]; then | ||||||
| echo "Creating new pull request." | ||||||
| PR_URL=$( | ||||||
| gh pr create \ | ||||||
| --title "$title" \ | ||||||
| --body "$pr_body" | ||||||
| ) | ||||||
| else | ||||||
| PR_URL=$pr_url | ||||||
| echo "Pull request already exists: $PR_URL" | ||||||
| gh pr edit "$pr_num" --body "$pr_body" | ||||||
| fi | ||||||
|
|
||||||
| echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT" | ||||||
|
|
||||||
| - name: Summary | ||||||
| env: | ||||||
| has_changes: ${{ steps.check-changes.outputs.has_changes }} | ||||||
| changes: ${{ steps.check-changes.outputs.changes_summary }} | ||||||
| pr_url: ${{ steps.create-pr.outputs.pr_url}} | ||||||
| pr_num: ${{ steps.open_pr_info.outputs.number }} | ||||||
| run: | | ||||||
| if [[ "$has_changes" == "true" ]]; then | ||||||
| if [[ -n "$pr_num" ]]; then | ||||||
| echo "✅ Successfully updated PR with new changes." | ||||||
| echo "$changes" | ||||||
| echo "🔗 PR URL: $pr_url" | ||||||
| echo "### ✅ PR Updated" >> $GITHUB_STEP_SUMMARY | ||||||
| echo "[$pr_url]($pr_url)" >> $GITHUB_STEP_SUMMARY | ||||||
| elif [[ -n "$pr_url" ]]; then | ||||||
| echo "✅ Successfully created PR with maintainer updates." | ||||||
| echo "$changes" | ||||||
| echo "🔗 PR URL: $pr_url" | ||||||
| echo "### ✅ PR Created" >> $GITHUB_STEP_SUMMARY | ||||||
| echo "[$pr_url]($pr_url)" >> $GITHUB_STEP_SUMMARY | ||||||
| else | ||||||
| echo "❌ Failed to create or update pull request." | ||||||
| echo "### ❌ PR Operation Failed" >> $GITHUB_STEP_SUMMARY | ||||||
| echo "A pull request was intended but the URL was not captured. Please check the logs." >> $GITHUB_STEP_SUMMARY | ||||||
| fi | ||||||
| else | ||||||
| echo "ℹ️ No changes detected - maintainers list is up to date." | ||||||
| echo "### ℹ️ No Changes" >> $GITHUB_STEP_SUMMARY | ||||||
| echo "The maintainers list is up-to-date. No PR was created." >> $GITHUB_STEP_SUMMARY | ||||||
| fi | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { self, ... }: | ||
| { | ||
| perSystem = | ||
| { | ||
| lib, | ||
| pkgs, | ||
| ... | ||
| }: | ||
| let | ||
| package = pkgs.writers.writePython3Bin "generate-all-maintainers" { | ||
| # Disable flake8 checks that are incompatible with the ruff ones | ||
| flakeIgnore = [ | ||
| # Thinks shebang is a block comment | ||
| "E265" | ||
| # line too long | ||
| "E501" | ||
| # line break before binary operator | ||
| "W503" | ||
| ]; | ||
| } (builtins.readFile ./generate-all-maintainers.py); | ||
| in | ||
| { | ||
| packages.generate-all-maintainers = package; | ||
|
|
||
| checks.generate-all-maintainers-test = | ||
| pkgs.runCommand "generate-all-maintainers-test" | ||
| { | ||
| nativeBuildInputs = [ package ]; | ||
| } | ||
| '' | ||
| generate-all-maintainers --root ${self} --output $out | ||
| ''; | ||
|
|
||
| devshells.default.commands = [ | ||
| { | ||
| name = "generate-all-maintainers"; | ||
| command = ''${lib.getExe package} "$@"''; | ||
| help = "Generate a single nix file with all `nixvim` and `nixpkgs` maintainer entries"; | ||
| } | ||
| ]; | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Extract maintainers from nixvim configuration using meta.maintainers | ||
| # This script evaluates an empty nixvimConfiguration and extracts the merged maintainer information | ||
| let | ||
| nixvim = import ../../..; | ||
| lib = nixvim.inputs.nixpkgs.lib.extend nixvim.lib.overlay; | ||
|
|
||
| emptyConfig = lib.nixvim.evalNixvim { | ||
| modules = [ { _module.check = false; } ]; | ||
| extraSpecialArgs = { | ||
| pkgs = null; | ||
| }; | ||
| }; | ||
|
|
||
| inherit (emptyConfig.config.meta) maintainers; | ||
|
|
||
| extractMaintainerObjects = | ||
| maintainerData: | ||
| lib.pipe maintainerData [ | ||
| lib.attrValues | ||
| lib.concatLists | ||
| lib.unique | ||
| ]; | ||
|
|
||
| allMaintainerObjects = extractMaintainerObjects maintainers; | ||
|
|
||
| allMaintainerNames = lib.filter (name: name != null) ( | ||
| map (maintainer: maintainer.github) allMaintainerObjects | ||
| ); | ||
|
|
||
| nixvimMaintainers = import ../../../lib/maintainers.nix; | ||
| nixvimMaintainerNames = lib.attrNames nixvimMaintainers; | ||
| partitionedMaintainers = lib.partition (nameValue: lib.elem nameValue.name nixvimMaintainerNames) ( | ||
| lib.attrsToList maintainerDetails | ||
| ); | ||
|
|
||
| maintainerDetails = lib.pipe allMaintainerObjects [ | ||
| (map (obj: { | ||
| name = obj.github; | ||
| value = obj // { | ||
| source = | ||
| if categorizedMaintainers.nixvim ? ${obj.github} then | ||
| "nixvim" | ||
| else if categorizedMaintainers.nixpkgs ? ${obj.github} then | ||
| "nixpkgs" | ||
| else | ||
| throw "${obj.github} is neither a nixvim or nixpkgs maintainer"; | ||
| }; | ||
| })) | ||
| lib.listToAttrs | ||
| ]; | ||
|
|
||
| categorizedMaintainers = { | ||
| nixvim = lib.listToAttrs partitionedMaintainers.right; | ||
| nixpkgs = lib.listToAttrs partitionedMaintainers.wrong; | ||
| }; | ||
|
|
||
| formattedMaintainers = lib.generators.toPretty { | ||
| multiline = true; | ||
| indent = ""; | ||
| } maintainerDetails; | ||
| in | ||
| { | ||
| raw = maintainers; | ||
| names = allMaintainerNames; | ||
| details = maintainerDetails; | ||
| categorized = categorizedMaintainers; | ||
| formatted = formattedMaintainers; | ||
|
|
||
| stats = { | ||
| totalMaintainers = lib.length allMaintainerNames; | ||
| nixvimMaintainers = lib.length (lib.attrNames categorizedMaintainers.nixvim); | ||
| nixpkgsMaintainers = lib.length (lib.attrNames categorizedMaintainers.nixpkgs); | ||
| }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.