Skip to content

Add ghprojects: declarative GitHub Projects V2 management#4815

Open
lyarwood wants to merge 1 commit into
kubevirt:mainfrom
lyarwood:ghprojects-scaffold
Open

Add ghprojects: declarative GitHub Projects V2 management#4815
lyarwood wants to merge 1 commit into
kubevirt:mainfrom
lyarwood:ghprojects-scaffold

Conversation

@lyarwood
Copy link
Copy Markdown
Member

@lyarwood lyarwood commented Mar 11, 2026

Summary

  • Scaffolds a new pkg/ghprojects package and robots/ghprojects CLI for managing GitHub Projects V2 boards declaratively from YAML configuration
  • Follows the same desired-state reconciliation pattern as peribolos: dry-run by default, --confirm to apply, granular --fix-projects/--fix-fields/--fix-views flags
  • Uses the shurcooL/githubv4 GraphQL library already vendored in this repo
  • Includes a dump subcommand to export live project state back to YAML

Closes #4814

@kubevirt-bot kubevirt-bot added the dco-signoff: no Indicates the PR's author has not DCO signed all their commits. label Mar 11, 2026
@kubevirt-bot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign davidvossel for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubevirt-bot kubevirt-bot requested review from dhiller and enp0s3 March 11, 2026 09:24
@lyarwood lyarwood force-pushed the ghprojects-scaffold branch from 931e49a to 9dae116 Compare March 11, 2026 09:25
@kubevirt-bot kubevirt-bot added dco-signoff: yes Indicates the PR's author has DCO signed all their commits. and removed dco-signoff: no Indicates the PR's author has not DCO signed all their commits. labels Mar 11, 2026
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In client.GetOrgID, q.Organization.ID is a githubv4.ID, so q.Organization.ID.(string) will panic/does not compile; it should be converted with string(q.Organization.ID) to match how IDs are handled elsewhere.
  • In updateProject, you pre-fetch fields and views via GetProjectFields/GetProjectViews and assign them to actual, but reconcileFields/reconcileViews immediately call those methods again; consider passing the already-fetched data into the reconcile functions to avoid redundant API calls.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `client.GetOrgID`, `q.Organization.ID` is a `githubv4.ID`, so `q.Organization.ID.(string)` will panic/does not compile; it should be converted with `string(q.Organization.ID)` to match how IDs are handled elsewhere.
- In `updateProject`, you pre-fetch fields and views via `GetProjectFields`/`GetProjectViews` and assign them to `actual`, but `reconcileFields`/`reconcileViews` immediately call those methods again; consider passing the already-fetched data into the reconcile functions to avoid redundant API calls.

## Individual Comments

### Comment 1
<location path="pkg/ghprojects/client/client.go" line_range="32" />
<code_context>
+	if err := c.gql.Query(ctx, &q, vars); err != nil {
+		return "", fmt.Errorf("querying org ID for %s: %w", login, err)
+	}
+	return q.Organization.ID.(string), nil
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid unsafe type assertion when converting githubv4.ID to string

`q.Organization.ID` is a `githubv4.ID`, not an interface, so `.(string)` will panic. Use `string(q.Organization.ID)` instead to convert safely and avoid a runtime panic.
</issue_to_address>

### Comment 2
<location path="pkg/ghprojects/reconcile/reconcile.go" line_range="13-17" />
<code_context>
+	"kubevirt.io/project-infra/pkg/ghprojects/config"
+)
+
+// Options controls reconciliation behavior.
+type Options struct {
+	Confirm     bool // If false, dry-run only (log what would happen).
+	FixProjects bool // Create/update projects.
+	FixFields   bool // Create/update/delete custom fields.
+	FixViews    bool // Create/update views.
+}
</code_context>
<issue_to_address>
**issue:** Options comment overstates current field reconciliation capabilities

`FixFields` is documented as "Create/update/delete custom fields", but `reconcileFields` only creates missing fields and logs type mismatches; it doesn’t handle renames, option changes, or deletions. Please either adjust the comment to describe the current behavior or extend the implementation to match the option name/description so `--fix-fields` isn’t misleading.
</issue_to_address>

### Comment 3
<location path="pkg/ghprojects/dump/dump.go" line_range="64-65" />
<code_context>
+			})
+		}
+
+		key := slugify(p.Title)
+		orgProjects.Projects[key] = proj
+	}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Slug generation can lead to empty or colliding project keys

Using `slugify(p.Title)` as the map key can yield an empty string (titles without alphanumerics) or collisions (different titles normalizing to the same slug), which will silently overwrite entries in `orgProjects.Projects`. Consider detecting empty/colliding slugs and either deriving a unique key (e.g., append project number) or returning an error so callers can handle it.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread pkg/ghprojects/client/client.go Outdated
Comment thread pkg/ghprojects/reconcile/reconcile.go Outdated
Comment thread pkg/ghprojects/dump/dump.go
@kubevirt-bot kubevirt-bot added dco-signoff: no Indicates the PR's author has not DCO signed all their commits. and removed dco-signoff: yes Indicates the PR's author has DCO signed all their commits. labels Mar 11, 2026
Scaffolds a new package and CLI tool for managing GitHub Projects V2
boards from YAML configuration, following the same desired-state
reconciliation pattern as peribolos.

Includes:
- config: YAML types and loader with validation
- client: GitHub GraphQL API wrapper for Projects V2 (projects, fields, views)
- reconcile: desired-state reconciler with dry-run, per-resource fix flags
- dump: export live GitHub project state back to YAML
- robots/ghprojects: CLI entrypoint with sync and dump subcommands

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Lee Yarwood <lyarwood@redhat.com>
@lyarwood lyarwood force-pushed the ghprojects-scaffold branch from 0f2772a to 5bfcacd Compare March 11, 2026 09:35
@kubevirt-bot kubevirt-bot added dco-signoff: yes Indicates the PR's author has DCO signed all their commits. and removed dco-signoff: no Indicates the PR's author has not DCO signed all their commits. labels Mar 11, 2026
@lyarwood
Copy link
Copy Markdown
Member Author

All three review comments have been addressed in the latest force-push:

  1. Unsafe type assertionGetOrgID now uses a safe comma-ok assertion with an error return
  2. Redundant API callsreconcileFields/reconcileViews now accept pre-fetched data as parameters instead of re-fetching
  3. FixFields comment — updated to accurately say "Create missing custom fields"
  4. Slug collisions — empty slugs fall back to project-<number>, collisions get the number appended as suffix

@kubevirt-bot
Copy link
Copy Markdown
Contributor

@lyarwood: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
build-test-label-analyzer-image 5bfcacd link true /test build-test-label-analyzer-image
build-release-blocker-image 5bfcacd link true /test build-release-blocker-image
build-flake-report-creator-image 5bfcacd link true /test build-flake-report-creator-image
build-referee-image 5bfcacd link true /test build-referee-image
build-pr-creator-image 5bfcacd link true /test build-pr-creator-image
Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@kubevirt-bot
Copy link
Copy Markdown
Contributor

There has been no activity on this PR for 45 days.
To protect limited CI resources, it has been automatically labelled 'stale'.
This PR will automatically rot after an additional 14 days of inactivity, and will be closed shortly after that.

What you can do:

  • If the PR is waiting on you to respond to a question or feedback and/or update the PR, please do so.
  • You can mark the PR as fresh and remove the label with the following command: /remove-lifecycle stale
  • If this PR is safe to close now, please help the project by closing it with: /close
  • If you need attention on this PR from a reviewer, you can raise it on the agenda of the relevant SIG meeting or KubeVirt Community meeting, or ping the kubevirt-dev slack channel.

/lifecycle stale

@kubevirt-bot kubevirt-bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add declarative management of GitHub Projects V2 boards

2 participants