Skip to content

feat(code): multi-repo support for cloud tasks#68673

Open
adboio wants to merge 2 commits into
masterfrom
07-02-feat_code_multi-repo_support_for_cloud_tasks
Open

feat(code): multi-repo support for cloud tasks#68673
adboio wants to merge 2 commits into
masterfrom
07-02-feat_code_multi-repo_support_for_cloud_tasks

Conversation

@adboio

@adboio adboio commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

it's not intuitive to have a cloud task work across more than one repo. current common workaround is just prompting the agent to clone another repo

code app PR: PostHog/code#3184

^ merge after that one is deployed

Changes

this PR only solves the "start a cloud task with >1 repos" problem. multi-PR support & UX in future PRs!

  • mirroring local task -- adds additional_repositories field on the Task model
  • new cloud tasks will clone one primary repo + all additional repos
  • updated github token refresh mechanism to ensure tokens are refreshed for all additional repos

How did you test this code?

manually, local dev

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

🤖 Agent context

Autonomy: Human-driven (agent-assisted) - or - Fully autonomous

adboio commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Migration SQL Changes

Hey 👋, we've detected some migrations on this PR. Here's the SQL output for each migration, make sure they make sense:

products/tasks/backend/migrations/0047_task_additional_repositories.py

BEGIN;
--
-- Add field additional_repositories to task
--
ALTER TABLE "posthog_task" ADD COLUMN "additional_repositories" varchar(255)[] DEFAULT '{}' NOT NULL;
ALTER TABLE "posthog_task" ALTER COLUMN "additional_repositories" DROP DEFAULT;
COMMIT;

Last updated: 2026-07-06 21:02 UTC (f790a04)

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🔍 Migration Risk Analysis

We've analyzed your migrations for potential risks.

Summary: 0 Safe | 1 Needs Review | 0 Blocked

⚠️ Needs Review

May have performance impact

tasks.0047_task_additional_repositories
  └─ #1 ⚠️ AddField
     Adding NOT NULL field with callable default (list) - verify it's stable
     model: task, field: additional_repositories, default: list

Last updated: 2026-07-06 21:02 UTC (f790a04)

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "chore: update OpenAPI generated types" | Re-trigger Greptile

Comment thread products/tasks/backend/temporal/process_task/workflow.py Outdated
Comment on lines +443 to +469
additional_clones: list[PreparedClone] = []
if should_inject_github_token:
for additional_repository in ctx.additional_repositories:
try:
additional_token = (
get_sandbox_github_token(
ctx.github_integration_id,
run_id=ctx.run_id,
state=ctx.state,
task=task,
repository=additional_repository,
)
or ""
)
except Exception as e:
raise GitHubAuthenticationError(
f"Failed to get GitHub token for additional repository {additional_repository}",
{"repository": additional_repository, "task_id": ctx.task_id, "error": str(e)},
cause=e,
)
additional_clones.append(
PreparedClone(
repository=additional_repository,
github_token=additional_token,
shallow_clone=shallow_clone,
)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Silent skip of additional repos when no GitHub token

additional_clones is only populated when should_inject_github_token is True. If it is False (e.g., has_github_credentials is unset), every entry in ctx.additional_repositories is silently dropped — no warning is logged and the workflow proceeds without cloning those repos. A task that lists public additional repos alongside an unauthenticated primary clone would end up with the extra repos missing and no indication to the user. Consider at minimum emitting a debug log when ctx.additional_repositories is non-empty but should_inject_github_token is False, similar to the "Creating environment without repository" log already present.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Bundle size — 🔺 +1.1 KiB (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 76.92 MiB · 🔺 +1.1 KiB (+0.0%)

File Size Δ vs base
render-query/src/render-query/render-query.js 25.44 MiB 🔺 +1.1 KiB (+0.0%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.20 MiB · 22 files no change ███░░░░░░░ 28.0% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
11.58 MiB · 2,951 files no change █████████░ 89.9% of 12.87 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
668 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
458.3 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/driving-hogzilla.mjs
302.9 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/coffee-run.mjs
275.2 KiB ../node_modules/.pnpm/posthog-js@1.396.9/node_modules/posthog-js/dist/rrweb.js
266.9 KiB ../node_modules/.pnpm/@posthog+icons@0.37.4_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
253.2 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/chart-hog.mjs
239.2 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/x-ray.mjs
224.6 KiB src/taxonomy/core-filter-definitions-by-group.json
213.1 KiB ../node_modules/.pnpm/posthog-js@1.396.9/node_modules/posthog-js/dist/module.js
211.6 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/money.mjs
185.9 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/hogpatch.mjs

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Dist folder size — 🟢 -289.33 MiB (-16.5%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1466.36 MiB · 🟢 -289.33 MiB (-16.5%)

Playwright — all passed

All tests passed.

View test results →

@adboio adboio force-pushed the 07-02-feat_code_multi-repo_support_for_cloud_tasks branch 2 times, most recently from e748e0c to 676815d Compare July 6, 2026 16:06
@adboio adboio force-pushed the 07-02-feat_code_multi-repo_support_for_cloud_tasks branch from 676815d to 3c4059a Compare July 6, 2026 20:35
- fix stale workflow comment: additional_clones is built on resume too
- log when additional repositories are skipped for lack of GitHub credentials
- drop the primary repo from additional_repositories at the model so a PATCH
  without `repository` can't reintroduce it
- don't pass --additionalDirectories for repos that were never cloned
- pass additional_repositories in the ee TaskDetailDTO test construction (mypy)

Generated-By: PostHog Code
Task-Id: 14a9edb6-2aee-41c8-b1e1-618f8189ecdc
@adboio adboio marked this pull request as ready for review July 6, 2026 20:59
@adboio adboio requested review from a team and tatoalo July 6, 2026 20:59
@assign-reviewers-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-self-driving (ee/hogai/)

Soft owners come from CODEOWNERS-soft and each product's product.yaml. Generated files and lockfiles are ignored when deciding ownership.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "fix(code): address multi-repo review com..." | Re-trigger Greptile

def _normalize_repository_identifier(value: str) -> str:
"""Validate an ``organization/repo`` identifier and return it lowercased."""
parts = value.split("/")
if len(parts) != 2 or not parts[0] or not parts[1]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Medium: Path traversal in additional repository paths

../.. and org/.. pass this validation and are later converted into paths like /tmp/workspace/repos/../.. for --additionalDirectories. An authenticated user can create or update a task with that extra repository and make the agent server treat /tmp or the repos parent as an allowed workspace directory, which exposes files outside the cloned repos such as the sandbox env file. Reject ./.. and constrain both owner and repo to GitHub-safe path components here and in the model save() validation.

@veria-ai

veria-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR overview

This pull request adds multi-repository support for cloud tasks, allowing tasks to reference additional repositories that are passed to the agent as extra workspace directories. The touched backend serializer logic handles creation and update validation for those additional repository entries.

There is one open security issue in the additional-repository validation path. Because repository path components are not sufficiently constrained, an authenticated user could submit traversal segments that cause the agent to treat directories outside the cloned repositories as allowed workspace locations, potentially exposing sensitive local files such as sandbox environment data. No issues have been addressed yet, so the current security posture still depends on tightening this validation before merge.

Open issues (1)

Fixed/addressed: 0 · PR risk: 7/10

@trunk-io

trunk-io Bot commented Jul 6, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant