When CI has fewer CPUs than boards, one thread may process multiple
boards. The .config from the first board persists in the thread's work
directory, causing kconfig_changed_since() to return True for the next
board's first commit, triggering an extra reconfig.
Fix by forcing -T4 to ensure each board gets its own thread with an
isolated work directory.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Exclude tools/qconfig.py from coverage since it's a separate tool with
its own tests. Add allow_failures for core buildman modules that don't
have 100% test coverage yet (builder.py, builderthread.py, cfgutil.py,
control.py, toolchain.py).
This fixes the --coverage test returning error code 1.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add automatic detection of Kconfig and defconfig file changes to trigger
reconfiguration. When any Kconfig* file or the board's defconfig is newer
than the board's .config file, buildman forces a reconfigure even if the
build was previously successful.
This is enabled by default and can be disabled with -Z/--no-kconfig-check.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new test_bsettings.py with 12 tests to achieve 100% coverage for
bsettings.py
Series-to: concept
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Series-links: 1:86
When rebasing MRs, two CI pipelines are triggered: one for the push
and another when GitLab detects the MR source branch update. Also,
the MR title can inadvertently be changed during rebases.
Fix this by:
- Removing --run-ci flag from the push-branch command in the review
agent prompt (GitLab triggers an MR pipeline automatically)
- Adding an instruction not to update the MR title during rebases
Update the CI Pipelines documentation to reflect that all pushes
(both initial and updates) skip the push pipeline.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When commits have already been applied to the target branch via a
different path (with different hashes), cherry-picking fails with
conflicts. Pickman has no way to detect this situation, resulting
in confusing MRs and requiring manual intervention.
Add a signal mechanism so the agent can communicate this status back
to pickman. When the first cherry-pick fails with conflicts, the agent
checks if commits are already present in the target branch by matching
subjects. If all commits are found:
- The agent aborts the cherry-pick and writes a signal file
(.pickman-signal) with 'already_applied' status
- Pickman reads this signal and handles it appropriately:
- Marks commits as 'skipped' in the database
- Updates source position to advance past these commits
- Creates an MR with [skipped] prefix to record the attempt
This allows automated workflows to continue without manual intervention.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Fix two issues with push-branch:
1. Handle missing remote branch: When pushing a new branch for the
first time, the fetch to update tracking refs fails because the
branch doesn't exist on the remote yet. Handle this by catching
the fetch failure and using regular --force instead of
--force-with-lease for new branches.
2. Detect aborted cherry-picks: When the agent aborts a cherry-pick
(e.g., because commits are already applied), it may delete the
branch. Verify the branch exists after the agent runs to avoid
attempting to push a non-existent branch.
Cover-letter:
pickman and CI improvements
This series contains pickman enhancements and a CI fix:
- Process MRs oldest first to handle rebases chronologically
- Add --run-ci option for push-branch to trigger pipelines after rebase
- Fix force-push tracking ref issues by fetching before push
- Handle edge cases: new branches and agent-aborted cherry-picks
- Push master to GitHub for ReadTheDocs documentation rebuilds
END
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When using --force-with-lease with an HTTPS URL (instead of a remote
name), git cannot find the tracking refs automatically. This causes
"stale info" errors when the local tracking ref is out of date.
Fix by fetching the branch first to update the tracking ref before
pushing.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a --run-ci flag to push-branch that triggers the CI pipeline instead
of skipping it. This is needed when pushing rebased branches where the
MR pipeline should run to verify the changes.
Update the review agent prompt to use --run-ci when pushing after
rebase operations.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Sort merge requests by created_at ascending so older MRs are processed
before newer ones. This ensures that MRs needing rebase are handled in
chronological order.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add support for pushing branches using the GitLab API token, so commits
appear as coming from the token owner (e.g., a bot account) rather than
the user's configured git credentials.
Add get_push_url() function that creates a token-authenticated URL in
the format https://oauth2:TOKEN@host/project.git. Update push_branch()
to use this URL when available.
Add a new 'push-branch' command that the agent can use for pushing:
./tools/pickman/pickman push-branch <branch> -r <remote> [-f]
Update the review agent prompt to use this command instead of direct
git push, ensuring all pickman commits come from the same account.
Series-to: concept
Cover-letter:
pickman: Improvements for robustness and automation
These changes improve pickman's robustness when interrupted, add the
ability to skip problematic MRs during review, support multiple
concurrent MRs, and ensure all commits come from the pickman bot
account.
This series adds several improvements to pickman:
- Fix rebase-detection for open MRs
- Add skip/unskip support via review comments (pickman: skip/unskip)
- Skip already-processed commits to handle interruptions gracefully
- Handle 409 errors when MR already exists
- Document the commit-selection algorithm and skip feature
- Add --max-mrs option for parallel MR creation
- Run a CI pipeline after review-comment changes
- Use GitLab API token for push authentication (bot-account support)
END
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Remove ci.skip from the push command in the review agent prompt. When
pushing changes after addressing review comments, a new pipeline should
run to verify the changes.
The ci.skip option is still used in gitlab_api.push_branch() for
initial MR creation, since the MR creation itself triggers a pipeline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-developed-by: Claude <noreply@anthropic.com>
Add a --max-mrs option to the step and poll commands to allow multiple
MRs to be open simultaneously. Previously, step would block creating
new MRs if any were pending. Now it checks if the count of active
(non-skipped) MRs is below the limit before creating a new one.
The default is 5 MRs, allowing parallel review of multiple cherry-pick
sets while still preventing unbounded growth.
Each MR targets master independently, so there's no complex retargeting
needed when earlier MRs merge.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add two new sections to README.rst:
- Commit Selection: Explain how pickman groups commits into MRs
based on merge commits, with a concrete example
- Skipping MRs: Document the skip/unskip comment commands
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
If pickman crashes or is interrupted after pushing a branch but before
recording the MR, the next run would try to create an MR for the same
branch and receive a 409 Conflict error. Handle this gracefully by
finding the existing MR and returning its URL.
Add MrCreateError exception class to allow testing without importing
the gitlab module.
Co-developed-by: Claude <noreply@anthropic.com>
When determining the next commits to cherry-pick in get_next_commits(),
check the database and skip commits that are already tracked. This
prevents re-processing if pickman is interrupted after adding commits to
the database but before updating the source position.
The key changes:
- Build list of all merge commits on first-parent chain
- For each merge, get its commits and filter out those in database
- If any unprocessed commits remain, return them
- Otherwise skip to the next merge
- Finally check for remaining commits after all merges
Co-developed-by: Claude <noreply@anthropic.com>
Split the for loop body into a separate function to reduce complexity
and improve readability. The new process_single_mr() function handles
all processing for a single MR including skip/unskip handling, rebase,
and comment addressing.
Co-developed-by: Claude <noreply@anthropic.com>
Allow reviewers to skip an MR by commenting "pickman skip" (or
"pickman: skip", "@pickman skip"). When skipped, the MR title is
updated to include "[skipped]" and pickman continues creating new
MRs for subsequent cherry-picks.
To resume processing a skipped MR, comment "pickman unskip" which
removes the [skipped] tag from the title.
Skipped MRs are ignored for rebase processing and don't block
creation of new MRs.
Fix some double-quotes while here.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The GitLab list() API returns inaccurate detailed_merge_status values.
Fetch the full MR details for open MRs to get accurate rebase status.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add pylint disable comments for too-many-arguments, too-many-locals,
and too-many-branches warnings on functions that necessarily have
complex logic due to the nature of building agent prompts and
processing MR reviews.
Co-developed-by: Claude <noreply@anthropic.com>
Extract build_review_context() and build_review_prompt() from
run_review_agent() to reduce function complexity and improve
readability.
Fix the use of 'crosfw' while we are here.
Co-developed-by: Claude <noreply@anthropic.com>
Check GitLab merge request status to detect when a rebase is needed,
even if there are no review comments. This handles the case where
GitLab shows "Merge request must be rebased" or "Merge conflicts must
be resolved".
Add has_conflicts and needs_rebase fields to PickmanMr namedtuple,
populated from GitLab's detailed_merge_status and diverged_commits_count
fields.
Update process_mr_reviews() to trigger the agent for rebase when needed,
even without review comments. The agent prompt is adjusted based on
whether it needs to handle comments, rebase, or both.
Also:
- Pass MR description to agent for context from previous work
- After processing reviews, restore the original branch
- Use -o ci.skip when pushing to avoid duplicate pipelines
Series-to: concept
Series-links: 2:81 1:80
Cover-letter:
pickman: Improve handling of rebasing
This little series includes a few patches to ensure that rebases are
made against -master and detection for needing a rebase without the user
having explicitly request it.
It also passes the previous context to the agent when handling reviews,
which should improve its response.
END
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Series-version: 2
Add git fetch to ensure pickman uses the latest remote state:
- Before creating a new MR, fetch the remote first
- Before processing review comments, fetch the remote
This ensures that when creating MRs, the base branch (ci/master) is
up to date, and when the agent handles review comments that request
a rebase, it has the latest commits available.
Also update the agent prompt to use the configured remote/target for
rebase operations instead of a hardcoded value.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add pickman test suite to the existing tool test job alongside binman,
buildman, dtoc and patman tests.
Series-to: concept
Cover-letter:
pickman: Refine the feature set
This series adds a few more features and tweaks to make pickman work
better in practice.
The main issue is that the branch logic is not correct, so it selects
the wrong branch once one has already been applied.
Other improvements in this series:
- stop polling if an error occurs
- allow using a config file for the gitlab credentials (so we can set up
a 'pickman' user), along with a command to check gitlab access
- add the Claude log to the merge request, when it responds to comments
- maintain comments in a database so we know what was addressed
- add a command to figure out how many merges are pending and to show
the next 10
- measure test coverage, improve it (not 100%) and add to CI
END
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Remove SJG_LAB CI variable from push since lab tests are now
triggered automatically for MR pipelines via .gitlab-ci.yml rules.
Keep ci.skip to avoid running a pipeline on push; the MR pipeline
will run when the merge request is created.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add check-gitlab command to verify GitLab permissions for the
configured token. This helps diagnose issues like 403 Forbidden
errors when trying to create merge requests.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add support for storing a GitLab API token in ~/.config/pickman.conf
instead of an environment variable. This allows using a dedicated bot
account for pickman without affecting the user's personal GitLab
credentials.
Config file format:
[gitlab]
token = glpat-xxxxxxxxxxxxxxxxxxxx
This falls back to GITLAB_TOKEN environment variable if config file is
not present.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Change poll to stop on errors rather than continuing, since errors
like permission denied are not recoverable by retrying.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add command to show the total number of merge commits remaining to be
processed from a source branch. This helps track progress through a
large backlog of merges.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Update the review agent to:
- Create local branch with version suffix (-v2, -v3, etc.)
- Force push to original remote branch to update existing MR
- Use --keep-empty when rebasing to preserve empty merge commits
After processing comments:
- Mark them as processed in database to avoid reprocessing
- Update MR description with agent conversation log
- Append review handling to .pickman-history
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add function to update a merge request's description via the GitLab API.
This is used to update the MR with the agent's conversation log after
processing review comments.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add schema v3 with a processed_comment table to track which MR comments
have been addressed by the review agent. This prevents re-processing
the same comments when running review or poll commands.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The regex for extracting commit hashes from MR descriptions was matching
short numbers like "1" from the conversation log (e.g., "- 1 board built").
Fix by requiring commit hashes to be at least 7 characters, which is the
minimum length for git short hashes.
Shorten the argument name to 'desc' while we are here.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new next-merges command that shows the next N merges to be applied
from a source branch, useful for seeing what's coming up.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Fix get_next_commits() to use --first-parent when finding the next merge
commit. Without this, git log returns commits in topological order which
can find merges from different subsystems that were merged later, rather
than following the mainline merge order.
The fix uses a two-step approach:
1. Use --first-parent to find the next merge on the mainline
2. Get all commits up to that merge (without --first-parent to include
the merged branch's commits)
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Fix the logic for determining if an MR's last commit is newer than the
current database position. The check should verify that current is an
ancestor of the MR's commit (meaning the MR's commit is newer), not the
other way around.
Add a test to cover this.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Change get_pickman_mrs() and get_mr_comments() to return lists of named
tuples instead of dicts. This provides cleaner attribute access (e.g.
merge_req.iid instead of merge_req['iid']) and better code documentation.
Add PickmanMr and MrComment named tuples to gitlab_api.py.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Extract the core 'apply' logic into execute_apply() which handles
database operations, agent invocation, and MR creation. This separates
the non-git logic from do_apply() which now handles setup, history
writing, and branch restoration.
Also move branch restoration to the end of do_apply() using a ret
variable to ensure we always restore the branch even if MR creation
fails.
Add tests for execute_apply() covering success, failure, push, and
push failure cases.
Rename conversation_log to conv_log for brevity.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Extract the setup logic from do_apply() into prepare_apply() which:
- Gets the next commits from the source branch
- Validates the source exists and has commits
- Saves the original branch
- Generates or uses provided branch name
- Deletes existing branch if needed
- Prints info about what will be applied
Returns (ApplyInfo, return_code) tuple where ApplyInfo contains commits,
branch_name, original_branch, and merge_found. This makes the setup
logic testable independently from the agent and git operations.
Add tests for prepare_apply():
- test_prepare_apply_error: Test error handling for unknown source
- test_prepare_apply_no_commits: Test when no commits to apply
- test_prepare_apply_with_commits: Test successful preparation
- test_prepare_apply_custom_branch: Test custom branch name
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Extract get_history() from write_history() to separate the history
content generation and file I/O from the git operations. The function
now:
- Takes a filename parameter
- Reads existing content from the file
- Writes updated content to the file
- Returns both the content and the git commit message
This makes the history generation logic fully testable without needing
to mock git operations.
Add tests for get_history():
- test_get_history_empty: Test with no existing file
- test_get_history_with_existing: Test appending to existing content
- test_get_history_replaces_existing_branch: Test replacing entry
- test_get_history_multiple_commits: Test commit message with multiple
commits
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add tests to improve test coverage for control.py:
- test_poll_continues_on_step_error: Test poll warning on step error
- test_format_history_summary: Test history summary formatting
- test_get_next_commits_with_empty_lines: Test empty line handling
- test_commit_source_resolve_error: Test commit resolution failure
- test_unknown_command: Test unknown command handling
- test_review_with_mrs_no_comments: Test review with open MRs
This improves control.py coverage from 63% to 67%.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add support for running pickman tests with code coverage checking,
similar to binman. Use test_util.run_test_suites() for running tests
and test_util.run_test_coverage() for coverage checking.
Options added to the 'test' command:
-P: Number of processes for parallel test execution
-T: Run with coverage checking
-v: Verbosity level (0-4)
The coverage check allows failures for modules that require external
services (agent.py, gitlab_api.py, control.py, database.py) since
these cannot easily achieve 100% coverage in unit tests.
Also update test_util.py to recognize 'pickman' as using the 'test'
subcommand like binman and patman.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add tests for the remaining uncovered database.py code paths:
- Duplicate database creation error
- Already open error when opening twice
- Already closed error when closing twice
- rollback() method
This brings database.py to 100% test coverage.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
- Remove unused commit_hashes variable in agent.py
- Add pylint disable for too-many-public-methods in database.py
- Add pylint disable for too-many-arguments in database.py functions
- Add pylint disable for too-many-branches in control.py do_apply()
- Add pylint disable for too-many-lines in ftest.py
- Prefix unused mock arguments with underscore in ftest.py
This brings the pylint score to 10.00/10.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Move datetime, re, and time imports from inside functions to the top
of the file, following Python style guidelines.
Also fix a few pylint warnings while here.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add terminal.capture() context manager to tests that may produce
terminal output, ensuring tests run silently unless explicitly
checking output content.
Fix a few pyline warnings while here.
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Have the step command also process review comments when there are open
MRs. This integrates the review functionality into step, so a single
poll loop can handle the full workflow: process merged MRs, handle
review comments, and create new MRs when ready.
Refactor process_mr_reviews() as a helper function used by both
do_review() and do_step().
Series-to: concept
Series-cc: heinrich
Cover-letter:
pickman: Add a manager for cherry-picks
This series adds pickman, a tool to help manage cherry-picking commits
between branches. It's designed to automate the tedious process of
cherry-picking large numbers of commits from one branch to another,
such as when maintaining a downstream branch.
Key features:
- Track source branches and cherry-pick progress in a SQLite database
- Find commits to cherry-pick up to the next merge commit
- Use Claude AI agent to automate cherry-picks with conflict resolution
- Create GitLab merge requests for review
- Handle MR review comments automatically
- Poll for continuous automated cherry-picking
The tool integrates with GitLab for MR management and uses the Claude
Agent SDK for automated cherry-picking and conflict resolution.
This tool is *very* experimental (so don't use it yourself!), but should
become usable sometime in the new year.
END
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add automatic database updates when pickman MRs are merged. The step
command now:
1. Checks for merged pickman MRs via GitLab API
2. Parses the MR description to find the source branch and last commit
3. Updates the database's last_commit for the source branch
4. Then proceeds to check for open MRs and create new ones as before
This removes the need to manually run commit-source after each MR is
merged, enabling fully automated cherry-pick workflows with poll.
Add get_merged_pickman_mrs() and refactor get_open_pickman_mrs() to use
a common get_pickman_mrs() function with a state parameter.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Add a workflow section at the top of the README explaining the typical
usage pattern for pickman: setup with add-source, cherry-pick with
apply -p, review/merge, update with commit-source, and repeat.
Also mention the step and review commands for automated workflows.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>