Repository navigation
feat(automations): sync agent prompts to cloud - #967
Conversation
✅ Deploy Preview for devsydev canceled.
|
✅ Deploy Preview for images-devsy-sh canceled.
|
|
All contributors have signed the CLA. |
|
If you're new to commit signing, there are different ways to set it up: Sign commits with
|
📝 WalkthroughWalkthroughThe automation tool now requires named agents and supports OpenHands Cloud prompt synchronization. It adds apply and dry-run modes, API handling, drift reporting, synchronization tests, and removes the existing agents structure tests. ChangesAutomation synchronization
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SyncCLI
participant LocalAgentFiles
participant SyncClient
participant OpenHandsAPI
SyncCLI->>SyncClient: start synchronization
SyncClient->>OpenHandsAPI: list automations with Bearer token
OpenHandsAPI-->>SyncClient: return paginated automation records
SyncClient->>LocalAgentFiles: read local agent.md prompt
SyncClient->>OpenHandsAPI: patch drifted prompt
OpenHandsAPI-->>SyncClient: return update status
SyncClient-->>SyncCLI: report synchronization results
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Security | 1 critical |
🟢 Metrics 82 complexity · 8 duplication
Metric Results Complexity 82 Duplication 8
AI Reviewer: run a review on demand. To trigger the first review automatically, go to your organization or repository integration settings. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@hack/automations/main.go`:
- Around line 76-79: Move agent validation to immediately after loadConfig() and
before the sync branch in main, reusing the existing validateAgent function.
Ensure every configured agent is validated before runSync can return early,
while preserving the normal loop’s existing behavior without duplicate
validation.
In `@hack/automations/sync.go`:
- Around line 111-117: Update the error formatting in the synchronization
response handling to use lowercase status prefixes and change the HTTP literal
to lowercase. Apply the same lowercase wording to the related output strings in
the code covered by the corresponding synchronization flow, while preserving the
existing error details and formatting structure.
- Around line 73-77: Set a bounded timeout on the http.Client created in both
syncClient constructors, including the constructor returning the client shown
near the return statement and the other constructor identified by the comment.
Preserve the existing client configuration while ensuring API calls cannot block
indefinitely.
- Line 94: Normalize c.host by removing trailing slashes before concatenating it
with path in the http.NewRequest call, ensuring hosts ending in “/” produce a
single-separator URL while preserving the existing request behavior.
- Around line 187-190: Update the cloud automation indexing logic around byName
to detect duplicate a.Name values and fail instead of overwriting the earlier
automation. Preserve the existing name-based lookup for unique names, and use
the surrounding function’s established error-return mechanism to report the
duplicate.
🪄 Autofix
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2e93f74f-13ca-423a-a092-759aa8bb4076
📒 Files selected for processing (5)
AGENTS.mdhack/automations/agents.yamlhack/automations/main.gohack/automations/sync.gohack/automations/sync_test.go
| if resp.StatusCode < 200 || resp.StatusCode >= 300 { | ||
| return data, fmt.Errorf( | ||
| "%s %s: HTTP %d: %s", | ||
| method, | ||
| path, | ||
| resp.StatusCode, | ||
| truncate(string(data), 500), |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use lowercase synchronization output strings.
Change the uppercase status prefixes and the HTTP error literal to lowercase. As per coding guidelines, use lowercase log messages and logging strings throughout Go code.
Also applies to: 203-230
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@hack/automations/sync.go` around lines 111 - 117, Update the error formatting
in the synchronization response handling to use lowercase status prefixes and
change the HTTP literal to lowercase. Apply the same lowercase wording to the
related output strings in the code covered by the corresponding synchronization
flow, while preserving the existing error details and formatting structure.
Source: Coding guidelines
Adds a sync tool to hack/automations that pushes the repo-generated .agents/agents/*/agent.md prompts to the live OpenHands Cloud automations, closing the gap where editing agents.yaml left deployed automations stale. - hack/automations/sync.go (stdlib-only): list cloud automations, match by name, PATCH prompt on drift. -dry-run previews drift (exits non-zero). - hack/automations/main.go: -sync/-dry-run flags; automation_name field. - hack/automations/agents.yaml: backfill automation_name for all 14 agents. - hack/automations/sync_test.go: 6 tests with httptest server. - AGENTS.md: document generate/check/sync workflow. Co-authored-by: openhands <openhands@all-hands.dev>
e9382be to
59cf470
Compare
Fixes Applied SuccessfullyFixed 2 file(s) based on 5 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 2 file(s) based on 5 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@hack/automations/sync_test.go`:
- Around line 154-175: Extend TestSyncMissingAndDisabledReported or add a
focused syncWith test using two cloud automations with identical Name values.
Assert syncWith returns an error and verify the test server records no patch
requests, covering the duplicate-name rejection branch.
In `@hack/automations/sync.go`:
- Around line 239-241: Replace os.ReadFile in the agent prompt loading path with
pkg/sharedfile.ReadFile so agent.md must be a regular non-symlink file before
its contents are used. Add a regression test covering a symlinked agent.md and
verify it is rejected without sending or processing the linked file’s contents.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a431b736-e418-476e-a302-9908a76346a3
📒 Files selected for processing (4)
agents_test.gohack/automations/main.gohack/automations/sync.gohack/automations/sync_test.go
💤 Files with no reviewable changes (1)
- agents_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- hack/automations/main.go
| func TestSyncMissingAndDisabledReported(t *testing.T) { | ||
| agentsDir := tempAgentsDir(t) | ||
| const missingID = "sync-missing" | ||
| const disabledID = "sync-disabled" | ||
| writeAgentFile(t, agentsDir, missingID, "p") | ||
| writeAgentFile(t, agentsDir, disabledID, "p") | ||
|
|
||
| ts := newSyncTestServer(t, []cloudAutomation{ | ||
| {ID: "dis1", Name: "Daily Sync Disabled", Prompt: "p", Enabled: false}, | ||
| }) | ||
| cfg := config{Agents: []agent{ | ||
| {ID: missingID, AutomationName: "Daily Sync Missing"}, | ||
| {ID: disabledID, AutomationName: "Daily Sync Disabled"}, | ||
| }} | ||
| client := newSyncClientWith(ts.srv.URL, "test-token") | ||
| if err := syncWith(cfg, client, agentsDir, false); err != nil { | ||
| t.Fatalf("syncWith: %v", err) | ||
| } | ||
| if len(ts.promptByID) != 0 { | ||
| t.Errorf("missing/disabled automations must not be patched") | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a duplicate-name regression test.
syncWith rejects duplicate cloud automation names in hack/automations/sync.go lines 187-191, but no test covers that branch. Add two automations with the same Name. Assert that syncWith returns an error and sends no patch request.
As per coding guidelines, add or update relevant unit tests for code changes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@hack/automations/sync_test.go` around lines 154 - 175, Extend
TestSyncMissingAndDisabledReported or add a focused syncWith test using two
cloud automations with identical Name values. Assert syncWith returns an error
and verify the test server records no patch requests, covering the
duplicate-name rejection branch.
Source: Coding guidelines
| local, err := os.ReadFile(filepath.Join(ctx.agentsDir, a.ID, "agent.md")) | ||
| if err != nil { | ||
| return 0, fmt.Errorf("read %s: %w", filepath.Join(ctx.agentsDir, a.ID, "agent.md"), err) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Use the no-follow reader for agent prompts.
os.ReadFile follows symbolic links. A symlinked agent.md can expose a process-readable file and, when the matching enabled automation has drift, send its content to OpenHands Cloud. Use pkg/sharedfile/sharedfile.go ReadFile, which accepts only regular non-symlink files. Add a symlink regression test.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@hack/automations/sync.go` around lines 239 - 241, Replace os.ReadFile in the
agent prompt loading path with pkg/sharedfile.ReadFile so agent.md must be a
regular non-symlink file before its contents are used. Add a regression test
covering a symlinked agent.md and verify it is rejected without sending or
processing the linked file’s contents.
What
Adds a sync tool to
hack/automationsthat pushes the repo-generated.agents/agents/*/agent.mdprompts to the live OpenHands Cloud automations, closing the gap where editingagents.yamland regeneratingagent.mdleft the deployed cloud automations stale.Why
hack/automationsalready generates.agents/agents/*/agent.mdfromagents.yaml(the prompt content), but the 14 running OpenHands Cloud automations are a separate thing created via the automation API. When the generated prompts were updated (e.g. boundedcurl --max-time 300, echoed install progress, the GitHub App signed-commit flow, the PR-duplicate guard), nothing pushed those changes to the cloud, so the deployed automations drifted from the repo. A live dry-run before this change reported13 drifted, 1 okout of 14.Changes
hack/automations/sync.go(new, stdlib-only) — a sync client that lists cloud automations (GET /api/automation/v1, paginated), matches each repo agent to its cloud automation by name, compares the livepromptagainst the local generatedagent.md, andPATCHesprompton drifted automations (PATCH /api/automation/v1/{id}). Auth viaOPENHANDS_API_KEY;OPENHANDS_HOSToverrides the API base (defaults tohttps://app.all-hands.dev).hack/automations/main.go— wires-syncand-dry-runflags; adds anautomation_namefield to theagentstruct (validated) that maps each agent to its cloud automation name.hack/automations/agents.yaml— backfillsautomation_namefor all 14 agents (e.g.ci-optimizer→Daily CI Optimizer). This is metadata only and does not appear in the generated prompts (-checkis clean, no drift).hack/automations/sync_test.go(new) — 6 tests using anhttptestserver: patch-on-drift, dry-run reports drift without patching (non-zero exit), up-to-date noop, missing/disabled handling, token-required error, auth-header sent. Tests use an isolated tempagentsDirso fixtures never leak into the committed.agents/tree scanned by the rootTestAgentsStructure.AGENTS.md— documents the generate / check / sync workflow.Usage
Verification
task cli:format— cleantask cli:lint:ci— 0 issuesgo run ./hack/automations -check— idempotent (generated files match disk)go test ./hack/automations/— all 10 tests pass (4 existing generator + 6 new sync)go test -run TestAgentsStructure .— passes (no fixture leakage)-sync -dry-runafter applying:0 synced, 0 drifted, 14 ok, 0 missing, 0 disabledThis PR was created by an AI agent (OpenHands) on behalf of the user.
Summary by CodeRabbit
New Features
Bug Fixes
Tests