git-juggler 0.2.7__tar.gz → 0.2.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {git_juggler-0.2.7 → git_juggler-0.2.10}/PKG-INFO +1 -1
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/hook_events.py +9 -2
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/hooks.py +2 -2
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/activity_models.py +5 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +6 -2
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/agents.py +5 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/ci.py +21 -6
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/app.py +2 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ci/__init__.py +53 -19
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ci/github_actions.py +67 -5
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ci/jenkins.py +267 -23
- git_juggler-0.2.10/src/backend/git_juggler/ci/run_cache.py +214 -0
- git_juggler-0.2.10/src/backend/git_juggler/frontend_dist/assets/index-C6HbHDsB.css +1 -0
- git_juggler-0.2.7/src/backend/git_juggler/frontend_dist/assets/index-B-qpxlvK.js → git_juggler-0.2.10/src/backend/git_juggler/frontend_dist/assets/index-DWGv6_yw.js +15 -15
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/index.html +2 -2
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/schemas.py +14 -0
- git_juggler-0.2.10/src/backend/tests/conftest.py +16 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_agent_hooks.py +45 -0
- git_juggler-0.2.10/src/backend/tests/test_ci.py +113 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_github_actions.py +87 -1
- git_juggler-0.2.10/src/backend/tests/test_jenkins.py +554 -0
- git_juggler-0.2.10/src/backend/tests/test_run_cache.py +77 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/App.css +62 -3
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/App.tsx +69 -18
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/api/client.ts +13 -3
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/api/types.ts +13 -0
- git_juggler-0.2.10/src/frontend/src/components/Agents/AgentActivityPanel.tsx +264 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +9 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Agents/agentFormat.ts +29 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/CIRunPopover.tsx +20 -8
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Commits/CommitList.tsx +14 -16
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Commits/CommitRow.tsx +12 -12
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Commits/UncommittedRow.tsx +14 -18
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Graph/GraphPanel.tsx +30 -34
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/JenkinsSettings.tsx +65 -24
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/MainMenu.tsx +25 -0
- git_juggler-0.2.10/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +308 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Sidebar/RepoList.tsx +40 -4
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Sidebar/Sidebar.tsx +2 -1
- git_juggler-0.2.10/src/frontend/src/components/Sidebar/panelKit.tsx +188 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Tabs/TabsBar.tsx +24 -4
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/agentStatusPopoverData.ts +2 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/ciRunPopoverData.ts +1 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/ci.ts +1 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/graph.ts +23 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/storeCore.ts +272 -14
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/tabs.ts +8 -1
- git_juggler-0.2.10/src/frontend/src/styles/ci.css +53 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/styles/menu.css +52 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/styles/sidebar.css +147 -217
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/agentFormat.test.ts +12 -1
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/agentStatusPopoverData.test.ts +11 -0
- git_juggler-0.2.7/src/backend/git_juggler/frontend_dist/assets/index-KUdo_LCw.css +0 -1
- git_juggler-0.2.7/src/backend/tests/test_ci.py +0 -82
- git_juggler-0.2.7/src/backend/tests/test_jenkins.py +0 -251
- git_juggler-0.2.7/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -85
- git_juggler-0.2.7/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -81
- git_juggler-0.2.7/src/frontend/src/components/Pipelines/StageStrip.tsx +0 -64
- git_juggler-0.2.7/src/frontend/src/styles/ci.css +0 -174
- {git_juggler-0.2.7 → git_juggler-0.2.10}/.github/workflows/build-package.yml +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/.gitignore +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/CLAUDE.md +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/LICENSE +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/README.md +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/notes.txt +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/pyproject.toml +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/resources/git-juggler-demo-1.gif +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/resources/git-juggler-demo-2.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/resources/git-juggler-demo-3.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/resources/git-juggler-demo-4.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/scripts/build-package.sh +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/scripts/dev.sh +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/text.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/config.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/git.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/repos.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/api/terminal.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ci/http.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/config.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/dev_app.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/command.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/data.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/detail.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/diff.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/refs.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/status.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/git/utils.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/main.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/repo_discovery.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/terminal.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/themes.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ui/__init__.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/ui/folder_picker.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_agent_tracking.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_ci_poll.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_ci_poll_route.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_claude_sessions_data.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_file_diff.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_git_data.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_opencode_sessions.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_pick_folder.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_preferences.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_repos.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/tests/test_themes.py +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/.gitignore +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/README.md +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/index.html +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/package-lock.json +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/package.json +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/public/agent-logos/claude.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/public/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/public/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/public/git-juggler.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/public/logo.png +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Graph/branchColor.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/AgentsSettings.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/GitHubSettings.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Search/SearchBox.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Search/TriSwitch.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Terminal/TerminalPanel.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/icons.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/inputs/TextField.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/components/inputs/styles.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/index.css +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/index.tsx +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/appTheme.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/branchVisibility.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/bundledThemes.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/ciPoll.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/color.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/flip.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/formatDate.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/preferenceSender.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/scrollToCommit.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/sideBySide.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/stageWindow.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/stripAnsi.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/swallowNextClick.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/lib/vscodeTheme.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/agents.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/overlayStack.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/overlays.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/preferenceSync.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/preferences.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/repos.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/settings.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/store.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/terminal.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/state/themes.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/styles/commits.css +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/styles/graph.css +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/src/styles/terminal.css +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/ciPoll.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/computeColumns.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/preferenceSender.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/sideBySide.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/stageWindow.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tests/vscodeTheme.test.ts +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tsconfig.app.json +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tsconfig.json +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/tsconfig.node.json +0 -0
- {git_juggler-0.2.7 → git_juggler-0.2.10}/src/frontend/vite.config.ts +0 -0
|
@@ -150,6 +150,7 @@ class AgentHookEventReader:
|
|
|
150
150
|
session_events = self._since_last_prompt(session_events)
|
|
151
151
|
card: ClaudeSession | None = None
|
|
152
152
|
opencode_session: OpenCodeSession | None = None
|
|
153
|
+
parent_opencode_session: OpenCodeSession | None = None
|
|
153
154
|
busy: bool | None = None # what the agent itself reports, when it does
|
|
154
155
|
waiting_for: str | None = None
|
|
155
156
|
if provider == "claude" and registry is not None and session_id:
|
|
@@ -169,9 +170,10 @@ class AgentHookEventReader:
|
|
|
169
170
|
if not session_id:
|
|
170
171
|
continue # the plugin's start event: an instance, not yet a session
|
|
171
172
|
opencode_session = self._opencode_session(session_id, opencode_signature)
|
|
172
|
-
|
|
173
|
-
if opencode_session is None or opencode_session.archived or opencode_session.is_child:
|
|
173
|
+
if opencode_session is None or opencode_session.archived:
|
|
174
174
|
continue
|
|
175
|
+
if opencode_session.parent_id:
|
|
176
|
+
parent_opencode_session = self._opencode_session(opencode_session.parent_id, opencode_signature)
|
|
175
177
|
busy = opencode_session.busy
|
|
176
178
|
|
|
177
179
|
activities: dict[str, AgentWorktreeActivity] = {}
|
|
@@ -240,6 +242,11 @@ class AgentHookEventReader:
|
|
|
240
242
|
name=card.name if card is not None else (opencode_session.title if opencode_session is not None else None),
|
|
241
243
|
details=details,
|
|
242
244
|
waiting_for=waiting_for,
|
|
245
|
+
is_subagent=opencode_session.is_child if opencode_session is not None else False,
|
|
246
|
+
parent_session_id=opencode_session.parent_id if opencode_session is not None else None,
|
|
247
|
+
parent_title=parent_opencode_session.title if parent_opencode_session is not None else None,
|
|
248
|
+
parent_agent=parent_opencode_session.agent if parent_opencode_session is not None else None,
|
|
249
|
+
parent_provider=provider if parent_opencode_session is not None else None,
|
|
243
250
|
)
|
|
244
251
|
)
|
|
245
252
|
return scans, next_change
|
|
@@ -102,8 +102,8 @@ def _claude_snippet_dict() -> dict:
|
|
|
102
102
|
"hooks": {
|
|
103
103
|
"SessionStart": [_claude_hook_entry("SessionStart")],
|
|
104
104
|
"UserPromptSubmit": [_claude_hook_entry("UserPromptSubmit")],
|
|
105
|
-
"PreToolUse": [_claude_hook_entry("PreToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS")],
|
|
106
|
-
"PostToolUse": [_claude_hook_entry("PostToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS")],
|
|
105
|
+
"PreToolUse": [_claude_hook_entry("PreToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task")],
|
|
106
|
+
"PostToolUse": [_claude_hook_entry("PostToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task")],
|
|
107
107
|
"SessionEnd": [_claude_hook_entry("SessionEnd")],
|
|
108
108
|
}
|
|
109
109
|
}
|
{git_juggler-0.2.7 → git_juggler-0.2.10}/src/backend/git_juggler/agents/tracking/activity_models.py
RENAMED
|
@@ -63,3 +63,8 @@ class AgentRepositoryScan:
|
|
|
63
63
|
details: SessionDetails | None = None
|
|
64
64
|
# What the agent is blocked on ("permission prompt", "input needed"), or None.
|
|
65
65
|
waiting_for: str | None = None
|
|
66
|
+
is_subagent: bool = False
|
|
67
|
+
parent_session_id: str | None = None
|
|
68
|
+
parent_title: str | None = None
|
|
69
|
+
parent_agent: str | None = None
|
|
70
|
+
parent_provider: str | None = None
|
|
@@ -27,10 +27,14 @@ class OpenCodeSession:
|
|
|
27
27
|
time_created: int | None
|
|
28
28
|
time_updated: int | None
|
|
29
29
|
archived: bool
|
|
30
|
-
|
|
30
|
+
parent_id: str | None
|
|
31
31
|
busy: bool
|
|
32
32
|
last_prompt: str | None
|
|
33
33
|
|
|
34
|
+
@property
|
|
35
|
+
def is_child(self) -> bool:
|
|
36
|
+
return self.parent_id is not None
|
|
37
|
+
|
|
34
38
|
|
|
35
39
|
def db_signature(path: Path | None) -> tuple | None:
|
|
36
40
|
"""Change marker for the DB (OpenCode uses WAL, so the -wal file moves first).
|
|
@@ -117,7 +121,7 @@ def read_session(path: Path | None, session_id: str, now_ms: int | None = None)
|
|
|
117
121
|
time_created=time_created if isinstance(time_created, int) else None,
|
|
118
122
|
time_updated=time_updated if isinstance(time_updated, int) else None,
|
|
119
123
|
archived=time_archived is not None,
|
|
120
|
-
|
|
124
|
+
parent_id=parent_id if isinstance(parent_id, str) else None,
|
|
121
125
|
busy=busy,
|
|
122
126
|
last_prompt=last_prompt,
|
|
123
127
|
)
|
|
@@ -46,6 +46,11 @@ def _agent_scan_response(scan: AgentRepositoryScan) -> AgentRepositoryScanRespon
|
|
|
46
46
|
name=scan.name,
|
|
47
47
|
details=scan.details.__dict__ if scan.details is not None else None,
|
|
48
48
|
waiting_for=scan.waiting_for,
|
|
49
|
+
is_subagent=scan.is_subagent,
|
|
50
|
+
parent_session_id=scan.parent_session_id,
|
|
51
|
+
parent_title=scan.parent_title,
|
|
52
|
+
parent_agent=scan.parent_agent,
|
|
53
|
+
parent_provider=scan.parent_provider,
|
|
49
54
|
)
|
|
50
55
|
|
|
51
56
|
|
|
@@ -6,8 +6,8 @@ from typing import Annotated
|
|
|
6
6
|
from fastapi import APIRouter, HTTPException, Query
|
|
7
7
|
|
|
8
8
|
from .. import config
|
|
9
|
-
from ..ci import github_actions, jenkins
|
|
10
|
-
from ..ci import
|
|
9
|
+
from ..ci import github_actions, jenkins, run_cache
|
|
10
|
+
from ..ci import get_ci_run_stages, get_ci_runs, get_recent_pipelines, poll_ci_runs
|
|
11
11
|
from ..git.data import get_commit_hashes
|
|
12
12
|
from ..repo_discovery import list_repos, resolve_repo_path
|
|
13
13
|
from ..schemas import (
|
|
@@ -17,6 +17,7 @@ from ..schemas import (
|
|
|
17
17
|
CiStage,
|
|
18
18
|
GitHubConfig,
|
|
19
19
|
JenkinsConfig,
|
|
20
|
+
JenkinsRuleTestRequest,
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
|
|
@@ -76,16 +77,24 @@ def api_ci_stages(repo_id: str, provider: str, run_id: str) -> list[CiStage]:
|
|
|
76
77
|
return stages
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
# The most recent pipelines (running or finished) across all repos, for the
|
|
81
|
+
# Pipelines tab.
|
|
82
|
+
@router.get("/api/ci/recent", response_model=list[ActivePipeline])
|
|
83
|
+
def api_ci_recent(limit: Annotated[int, Query(ge=1, le=50)] = 10) -> list[ActivePipeline]:
|
|
84
|
+
return get_recent_pipelines(
|
|
83
85
|
list_repos(config.load_repo_paths()),
|
|
84
86
|
config.load_github_config(),
|
|
85
87
|
config.load_jenkins_config(),
|
|
88
|
+
limit,
|
|
86
89
|
)
|
|
87
90
|
|
|
88
91
|
|
|
92
|
+
# Forget every stored finished run (and runs the CI servers no longer have).
|
|
93
|
+
@router.post("/api/ci/cache/clear", status_code=204)
|
|
94
|
+
def api_ci_cache_clear() -> None:
|
|
95
|
+
run_cache.clear()
|
|
96
|
+
|
|
97
|
+
|
|
89
98
|
@router.post("/api/ci/github/test", response_model=CiConnectionTestResponse)
|
|
90
99
|
def api_test_github_connection(body: GitHubConfig) -> CiConnectionTestResponse:
|
|
91
100
|
ok, message = github_actions.test_connection(body.model_dump())
|
|
@@ -96,3 +105,9 @@ def api_test_github_connection(body: GitHubConfig) -> CiConnectionTestResponse:
|
|
|
96
105
|
def api_test_jenkins_connection(body: JenkinsConfig) -> CiConnectionTestResponse:
|
|
97
106
|
ok, message = jenkins.test_connection(body.model_dump())
|
|
98
107
|
return CiConnectionTestResponse(ok=ok, message=message)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@router.post("/api/ci/jenkins/rule-test", response_model=CiConnectionTestResponse)
|
|
111
|
+
def api_test_jenkins_rule(body: JenkinsRuleTestRequest) -> CiConnectionTestResponse:
|
|
112
|
+
ok, message = jenkins.test_rule_connection(body.config.model_dump(), body.rule.model_dump(), Path(body.repo_path))
|
|
113
|
+
return CiConnectionTestResponse(ok=ok, message=message)
|
|
@@ -14,12 +14,14 @@ from .api.config import create_config_router
|
|
|
14
14
|
from .api.git import router as git_router
|
|
15
15
|
from .api.repos import router as repos_router
|
|
16
16
|
from .api.terminal import create_terminal_router
|
|
17
|
+
from .ci import run_cache
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def create_app(root_path: Path, frontend_dist: Path | None = None) -> FastAPI:
|
|
20
21
|
app = FastAPI(title="git-juggler")
|
|
21
22
|
app.state.root_path = root_path
|
|
22
23
|
config.ensure_seeded(root_path)
|
|
24
|
+
run_cache.configure(config.CONFIG_DIR / "ci-cache.sqlite")
|
|
23
25
|
|
|
24
26
|
# Only needed for local dev, when the Vite dev server (a different origin)
|
|
25
27
|
# talks to this API directly instead of through its proxy.
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from concurrent.futures import ThreadPoolExecutor
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
+
from datetime import datetime, timezone
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Protocol
|
|
7
8
|
|
|
@@ -27,6 +28,11 @@ class CiProvider(Protocol):
|
|
|
27
28
|
Stages are included for runs that are still going."""
|
|
28
29
|
...
|
|
29
30
|
|
|
31
|
+
def recent_runs(self, repo_path: Path, limit: int) -> list[CiRunInfo]:
|
|
32
|
+
"""The repo's latest runs (at most `limit` per job/workflow listing),
|
|
33
|
+
running or finished, without stages."""
|
|
34
|
+
...
|
|
35
|
+
|
|
30
36
|
|
|
31
37
|
@dataclass
|
|
32
38
|
class GitHubActionsProvider:
|
|
@@ -39,6 +45,9 @@ class GitHubActionsProvider:
|
|
|
39
45
|
def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None, branch_name: str | None) -> list[CiRunInfo]:
|
|
40
46
|
return github_actions.poll_runs(repo_path, self.config, run_ids, head_sha)
|
|
41
47
|
|
|
48
|
+
def recent_runs(self, repo_path: Path, limit: int) -> list[CiRunInfo]:
|
|
49
|
+
return github_actions.get_recent_runs(repo_path, self.config, limit)
|
|
50
|
+
|
|
42
51
|
|
|
43
52
|
@dataclass
|
|
44
53
|
class JenkinsProvider:
|
|
@@ -51,6 +60,9 @@ class JenkinsProvider:
|
|
|
51
60
|
def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None, branch_name: str | None) -> list[CiRunInfo]:
|
|
52
61
|
return jenkins.poll_runs(repo_path, self.config, run_ids, head_sha, branch_name)
|
|
53
62
|
|
|
63
|
+
def recent_runs(self, repo_path: Path, limit: int) -> list[CiRunInfo]:
|
|
64
|
+
return jenkins.get_recent_builds(repo_path, self.config, limit)
|
|
65
|
+
|
|
54
66
|
|
|
55
67
|
def enabled_providers(github_config: dict | None, jenkins_config: dict | None) -> list[CiProvider]:
|
|
56
68
|
providers: list[CiProvider] = []
|
|
@@ -116,32 +128,54 @@ def get_ci_run_stages(
|
|
|
116
128
|
return None
|
|
117
129
|
|
|
118
130
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
def _recent_runs_for_repo(
|
|
132
|
+
repo: RepoSummary, github_config: dict | None, jenkins_config: dict | None, limit: int
|
|
133
|
+
) -> list[tuple[RepoSummary, CiRunInfo]]:
|
|
134
|
+
providers = enabled_providers(github_config, jenkins_config)
|
|
135
|
+
return [(repo, run) for provider in providers for run in provider.recent_runs(Path(repo.path), limit)]
|
|
136
|
+
|
|
122
137
|
|
|
138
|
+
def _run_key(run: CiRunInfo) -> tuple[str, str]:
|
|
139
|
+
return (run.provider, run.run_id or run.url)
|
|
123
140
|
|
|
124
|
-
def _pipeline_key(pipeline: ActivePipeline) -> tuple[str, str]:
|
|
125
|
-
run = pipeline.run
|
|
126
|
-
if run.run_id:
|
|
127
|
-
return (run.provider, run.run_id)
|
|
128
|
-
return (run.provider, run.url)
|
|
129
141
|
|
|
142
|
+
def _created_at_key(run: CiRunInfo) -> datetime:
|
|
143
|
+
# Providers format timestamps differently ("Z" vs "+00:00"), so compare
|
|
144
|
+
# parsed values rather than strings.
|
|
145
|
+
try:
|
|
146
|
+
parsed = datetime.fromisoformat((run.created_at or "").replace("Z", "+00:00"))
|
|
147
|
+
except ValueError:
|
|
148
|
+
return datetime.min.replace(tzinfo=timezone.utc)
|
|
149
|
+
return parsed if parsed.tzinfo else parsed.replace(tzinfo=timezone.utc)
|
|
130
150
|
|
|
131
|
-
|
|
151
|
+
|
|
152
|
+
def get_recent_pipelines(
|
|
132
153
|
repos: list[RepoSummary],
|
|
133
154
|
github_config: dict | None,
|
|
134
155
|
jenkins_config: dict | None,
|
|
156
|
+
limit: int = 10,
|
|
135
157
|
) -> list[ActivePipeline]:
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
|
|
158
|
+
"""The `limit` most recent pipelines across every known repo, running or
|
|
159
|
+
finished, newest first, with their stages. Repos are queried concurrently
|
|
160
|
+
since each one is a network round trip; stages are only fetched for the
|
|
161
|
+
runs that make the cut."""
|
|
162
|
+
if not repos or limit <= 0:
|
|
139
163
|
return []
|
|
140
164
|
with ThreadPoolExecutor(max_workers=min(8, len(repos))) as pool:
|
|
141
|
-
per_repo = pool.map(lambda repo:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
for
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
165
|
+
per_repo = pool.map(lambda repo: _recent_runs_for_repo(repo, github_config, jenkins_config, limit), repos)
|
|
166
|
+
found = [item for group in per_repo for item in group]
|
|
167
|
+
deduped: dict[tuple[str, str], tuple[RepoSummary, CiRunInfo]] = {}
|
|
168
|
+
for repo, run in found:
|
|
169
|
+
deduped.setdefault(_run_key(run), (repo, run))
|
|
170
|
+
newest = sorted(deduped.values(), key=lambda item: _created_at_key(item[1]), reverse=True)[:limit]
|
|
171
|
+
|
|
172
|
+
def with_stages(item: tuple[RepoSummary, CiRunInfo]) -> ActivePipeline:
|
|
173
|
+
repo, run = item
|
|
174
|
+
if run.run_id:
|
|
175
|
+
run.stages = get_ci_run_stages(Path(repo.path), run.provider, run.run_id, github_config, jenkins_config)
|
|
176
|
+
return ActivePipeline(repo_id=repo.id, repo_name=repo.name, run=run)
|
|
177
|
+
|
|
178
|
+
if not newest:
|
|
179
|
+
return []
|
|
180
|
+
with ThreadPoolExecutor(max_workers=min(8, len(newest))) as pool:
|
|
181
|
+
return list(pool.map(with_stages, newest))
|
|
@@ -10,10 +10,12 @@ from urllib.request import Request, urlopen
|
|
|
10
10
|
|
|
11
11
|
from git import Repo
|
|
12
12
|
|
|
13
|
+
from . import run_cache
|
|
13
14
|
from .http import fetch_json
|
|
14
15
|
from ..schemas import CiRunInfo, CiStage
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
PROVIDER = "github_actions"
|
|
17
19
|
GITHUB_RUNS_PER_PAGE = 100
|
|
18
20
|
GITHUB_RUNS_MAX_PAGES = 10
|
|
19
21
|
ACTIVE_RUN_STATUSES = {"queued", "in_progress", "waiting", "pending", "requested"}
|
|
@@ -257,11 +259,47 @@ def _fetch_run_stages(github_config: dict, repo_config: dict, run_id: str) -> li
|
|
|
257
259
|
|
|
258
260
|
|
|
259
261
|
def get_run_stages(repo_path: Path, github_config: dict | None, run_id: str) -> list[CiStage] | None:
|
|
262
|
+
"""A run's jobs/steps; a finished run's come from the run cache once stored."""
|
|
260
263
|
github_config = github_config or {}
|
|
261
264
|
repo_config = _resolve_repo_config(github_config, repo_path)
|
|
262
265
|
if repo_config is None:
|
|
263
266
|
return None
|
|
264
|
-
|
|
267
|
+
cached = run_cache.get(PROVIDER, run_id)
|
|
268
|
+
if cached is not None and cached.run.stages is not None:
|
|
269
|
+
return cached.run.stages
|
|
270
|
+
stages = _fetch_run_stages(github_config, repo_config, run_id)
|
|
271
|
+
if stages is not None and cached is not None:
|
|
272
|
+
run_cache.put_stages(PROVIDER, run_id, stages)
|
|
273
|
+
return stages
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _remember_runs(scope: str, runs: list[tuple[CiRunInfo, set[str]]]) -> None:
|
|
277
|
+
"""Store the finished runs among `runs` (with the commits they match). A
|
|
278
|
+
stored run that changed since (re-run: same id, new attempt) is replaced,
|
|
279
|
+
dropping its old stages."""
|
|
280
|
+
stored = run_cache.get_many(PROVIDER, [info.run_id for info, _ in runs if info.run_id])
|
|
281
|
+
for info, shas in runs:
|
|
282
|
+
cached = stored.get(info.run_id or "")
|
|
283
|
+
if cached is not None:
|
|
284
|
+
if cached.run.updated_at == info.updated_at and cached.run.status == info.status:
|
|
285
|
+
continue
|
|
286
|
+
run_cache.delete(PROVIDER, cached.run.run_id or "")
|
|
287
|
+
run_cache.put(PROVIDER, scope, info, shas)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _unlisted_cached_runs(scope: str, listed: list[CiRunInfo], limit: int | None = None) -> list[run_cache.CachedRun]:
|
|
291
|
+
"""Stored runs of the repo that the live listing didn't return, marked
|
|
292
|
+
`archived` when they fall inside the listed time range (so GitHub no longer
|
|
293
|
+
has them); older ones may just be past the capped listing."""
|
|
294
|
+
listed_ids = {info.run_id for info in listed}
|
|
295
|
+
oldest = min((info.created_at for info in listed if info.created_at), default=None)
|
|
296
|
+
result: list[run_cache.CachedRun] = []
|
|
297
|
+
for cached in run_cache.for_scope_prefixes(PROVIDER, [scope], limit):
|
|
298
|
+
if cached.run.run_id in listed_ids:
|
|
299
|
+
continue
|
|
300
|
+
cached.run.archived = oldest is not None and (cached.run.created_at or "") >= oldest
|
|
301
|
+
result.append(cached)
|
|
302
|
+
return result
|
|
265
303
|
|
|
266
304
|
|
|
267
305
|
def get_active_runs(repo_path: Path, github_config: dict | None, head_sha: str | None = None) -> list[CiRunInfo]:
|
|
@@ -301,6 +339,22 @@ def get_active_runs(repo_path: Path, github_config: dict | None, head_sha: str |
|
|
|
301
339
|
return result
|
|
302
340
|
|
|
303
341
|
|
|
342
|
+
def get_recent_runs(repo_path: Path, github_config: dict | None, limit: int) -> list[CiRunInfo]:
|
|
343
|
+
"""The repo's latest runs, running or finished, newest first, without
|
|
344
|
+
stages. Same single cached call as `get_active_runs`."""
|
|
345
|
+
github_config = github_config or {}
|
|
346
|
+
repo_config = _resolve_repo_config(github_config, repo_path)
|
|
347
|
+
repo_url = _repo_api_url(github_config, repo_config) if repo_config else None
|
|
348
|
+
if repo_url is None:
|
|
349
|
+
return []
|
|
350
|
+
data = _get_json_cached(f"{repo_url}/actions/runs?{urlencode({'per_page': '30'})}", _headers(github_config))
|
|
351
|
+
items = data.get("workflow_runs") if isinstance(data, dict) else None
|
|
352
|
+
listed = [_run_info(run) for run in (items if isinstance(items, list) else []) if isinstance(run, dict)]
|
|
353
|
+
_remember_runs(repo_url, [(info, {info.head_sha} if info.head_sha else set()) for info in listed])
|
|
354
|
+
archived = [cached.run for cached in _unlisted_cached_runs(repo_url, listed, limit) if cached.run.archived]
|
|
355
|
+
return sorted(listed[:limit] + archived, key=lambda info: info.created_at or "", reverse=True)[:limit]
|
|
356
|
+
|
|
357
|
+
|
|
304
358
|
def get_runs_by_id(repo_path: Path, github_config: dict | None, run_ids: list[str]) -> list[CiRunInfo]:
|
|
305
359
|
"""Current state of specific runs (one conditional call per run, plus one
|
|
306
360
|
for the stages of a run that is still going). A run that has finished comes
|
|
@@ -393,12 +447,20 @@ def get_github_actions_runs(repo_path: Path, commit_hashes: set[str], github_con
|
|
|
393
447
|
|
|
394
448
|
by_sha: dict[str, list[CiRunInfo]] = {}
|
|
395
449
|
tags_by_name = _tag_targets(repo_path)
|
|
450
|
+
listed: list[tuple[CiRunInfo, set[str]]] = []
|
|
396
451
|
for run in _fetch_workflow_runs(github_config, repo_config):
|
|
452
|
+
info = _run_info(run)
|
|
397
453
|
sha = _matching_run_sha(run, commit_hashes, tags_by_name)
|
|
398
|
-
|
|
399
|
-
|
|
454
|
+
listed.append((info, {s for s in (info.head_sha, sha) if s}))
|
|
455
|
+
if sha is not None:
|
|
456
|
+
by_sha.setdefault(sha, []).append(info)
|
|
400
457
|
|
|
401
|
-
|
|
402
|
-
|
|
458
|
+
# Runs GitHub no longer lists (pruned past its retention) come from the cache.
|
|
459
|
+
repo_url = _repo_api_url(github_config, repo_config)
|
|
460
|
+
if repo_url is not None:
|
|
461
|
+
_remember_runs(repo_url, listed)
|
|
462
|
+
for cached in _unlisted_cached_runs(repo_url, [info for info, _ in listed]):
|
|
463
|
+
for sha in cached.shas & commit_hashes:
|
|
464
|
+
by_sha.setdefault(sha, []).append(cached.run.model_copy(update={"stages": None}))
|
|
403
465
|
|
|
404
466
|
return by_sha
|