Commit Graph

233 Commits

Author SHA1 Message Date
Mondo Diaz
9a2b323fd8 Fix PackageArtifactResponse missing sha256 and version fields
- Add sha256 field to list_package_artifacts response (artifact ID is SHA256)
- Add version field to PackageArtifactResponse schema
- Add version field to frontend PackageArtifact type
- Update getArtifactVersion to prefer direct version field
2026-02-05 09:15:09 -06:00
Mondo Diaz
6b3522aef2 Fix migrations 008 and 011 to handle removed tags table 2026-02-05 09:15:09 -06:00
Mondo Diaz
f37d3e3e9a Fix migration 005 to not create indexes on removed tags table 2026-02-05 09:15:09 -06:00
Mondo Diaz
308057784e Fix tests for tag removal and version behavior
- Fix upload response to return actual version (not requested version)
  when artifact already has a version in the package
- Update ref_count tests to use multiple packages (one version per
  artifact per package design constraint)
- Remove allow_public_internet references from upstream caching tests
- Update consistency check test to not assert global system health
- Add versions field to artifact schemas
- Fix dependencies resolution to handle removed tag constraint
2026-02-05 09:15:09 -06:00
Mondo Diaz
86c95bea2b Fix remaining tag references in tests
- Update CacheRequest test to use version field
- Fix upload_test_file calls that still used tag parameter
- Update artifact history test to check versions instead of tags
- Update artifact stats tests to check versions instead of tags
- Fix garbage collection tests to delete versions instead of tags
- Remove TestGlobalTags class (endpoint removed)
- Update project/package stats tests to check version_count
- Fix upload_test_file fixture in test_download_verification
2026-02-05 09:15:09 -06:00
Mondo Diaz
cc5d67abd6 Update tests for tag removal
- Remove Tag/TagHistory model tests from unit tests
- Update CacheSettings tests to remove allow_public_internet field
- Replace tag= with version= in upload_test_file calls
- Update test assertions to use versions instead of tags
- Remove tests for tag: prefix downloads (now uses version:)
- Update dependency tests for version-only schema
2026-02-05 09:15:09 -06:00
Mondo Diaz
eb287edbda Remove obsolete tag support test from DragDropUpload
The tag functionality was removed in the previous commit, so
this test that expected a 'tag' field in the upload FormData
is no longer valid.
2026-02-05 09:15:09 -06:00
Mondo Diaz
86e971381a Remove tag system, use versions only for artifact references
Tags were mutable aliases that caused confusion alongside the immutable
version system. This removes tags entirely, keeping only PackageVersion
for artifact references.

Changes:
- Remove tags and tag_history tables (migration 012)
- Remove Tag model, TagRepository, and 6 tag API endpoints
- Update cache system to create versions instead of tags
- Update frontend to display versions instead of tags
- Remove tag-related schemas and types
- Update artifact cleanup service for version-based ref_count
2026-02-05 09:15:09 -06:00
Mondo Diaz
cf2fe5151f Remove superuser-only session_replication_role from factory reset 2026-02-05 09:15:09 -06:00
Mondo Diaz
2ae479146f Use same variable pattern as integration tests for reset job 2026-02-05 09:15:09 -06:00
Mondo Diaz
a0dad73db0 Add shell-level debug for password variable 2026-02-05 09:15:09 -06:00
Mondo Diaz
b40c53d308 Add debug to detect hidden characters in password 2026-02-05 09:15:09 -06:00
Mondo Diaz
f04149b410 Fix invalid sort field error on package artifact listing
The artifacts endpoint only supports sorting by: created_at, size, original_name
But the frontend was defaulting to 'name' (from the old tags endpoint).

- Change default sort from 'name' to 'created_at'
- Change default order from 'asc' to 'desc' (newest first)
- Remove sortable flag from version/tags columns (not DB fields)
- Add sortable flag to original_name and size columns
2026-02-05 09:15:09 -06:00
Mondo Diaz
aa851ab445 Add debug output to reset_feature job for auth troubleshooting 2026-02-05 09:15:09 -06:00
Mondo Diaz
9313942f53 Fix self-dependency detection to strip PyPI extras brackets
The circular dependency error '_pypi/psutil → _pypi/psutil' occurred because
dependencies with extras like 'psutil[test]' weren't being recognized as
self-dependencies. The comparison 'psutil[test] != psutil' failed.

- Add _normalize_pypi_package_name() helper that strips extras brackets
  and normalizes separators per PEP 503
- Update _detect_package_cycle to use normalized names for cycle detection
- Update check_circular_dependencies to use normalized initial path
- Simplify self-dependency check in resolve_dependencies to use helper
2026-02-05 09:15:09 -06:00
Mondo Diaz
9a795a301a Fix circular dependency resolution by switching to artifact-centric display
- Add artifact: prefix handling in resolve_dependencies for direct artifact
  ID references, enabling dependency resolution for tagless artifacts
- Refactor PackagePage from tag-based to artifact-based data display
- Add PackageArtifact type with tags array for artifact-centric API responses
- Update download URLs to use artifact:ID prefix when no tags exist
- Conditionally show "View Ensure File" only when artifact has tags
2026-02-05 09:15:09 -06:00
Mondo Diaz
9f13221012 Fix progress bar CSS scoping conflict between upload and dashboard 2026-02-05 09:15:09 -06:00
Mondo Diaz
a99381aafb Add reset job after integration tests on feature branches 2026-02-05 09:15:09 -06:00
Mondo Diaz
d422ed5cd8 Fix self-dependency check to use case-insensitive PyPI name normalization 2026-02-05 09:15:09 -06:00
Mondo Diaz
b2a8c7cfcc Pass upstream policy errors through PyPI proxy to users
- Add _parse_upstream_error() to extract policy messages from JFrog/Artifactory
- Pass through 403 and other 4xx errors with detailed messages
- Pin babel and electron-to-chromium to older versions for CI compatibility
2026-02-05 09:15:09 -06:00
Mondo Diaz
eb11efd001 Pin lodash to 4.17.21 to avoid immature package policy block 2026-02-05 09:15:09 -06:00
Mondo Diaz
02e69c65ee Move Dashboard and Teams from navbar to user dropdown menu
Cleaner navbar with just Projects and Docs links.
Dashboard and Teams are now in the user menu dropdown.
2026-02-05 09:15:09 -06:00
Mondo Diaz
34d98f52cb Fix circular dependency error message to show actual cycle path
The error was hardcoding [pkg_key, pkg_key] regardless of actual cycle.
Now tracks the path through dependencies to report the real cycle.
2026-02-05 09:15:09 -06:00
Mondo Diaz
29fa53d174 Replace custom dependency graph with React Flow
- Install reactflow and dagre for professional graph visualization
- Use dagre for automatic tree layout (top-to-bottom)
- Custom styled nodes with package name, version, and size
- Built-in zoom/pan controls and minimap
- Click nodes to navigate to package page
- Cleaner, more professional appearance
2026-02-05 09:15:09 -06:00
Mondo Diaz
63de1ce672 Improve dependency UI: rename to DependGraph, hide empty Used By
- Rename "Dependency Graph" modal title to "DependGraph"
- Hide "Used By" section when no packages depend on this package
2026-02-05 09:15:09 -06:00
Mondo Diaz
0b85f37abd Fix circular dependency detection and hide empty graph modal
- Add artifact-level self-dependency check (skip if dep resolves to same artifact)
- Close dependency graph modal if package has no dependencies to show
  (only root package with no children and no missing deps)
2026-02-05 09:15:09 -06:00
Mondo Diaz
101152f87f Skip self-dependencies in dependency resolver
PyPI packages can have self-referential dependencies for extras
(e.g., pytest[testing] depends on pytest). These were incorrectly
detected as circular dependencies. Now we skip them.
2026-02-05 09:15:09 -06:00
Mondo Diaz
3a09accfe6 Fix [object Object] error when API returns structured error detail
The backend returns detail as an object for some errors (circular dependency,
conflicts, etc.). The API client now JSON.stringifies object details so they
can be properly parsed by error handlers like DependencyGraph.
2026-02-05 09:15:09 -06:00
Mondo Diaz
88765b4f50 Show missing dependencies in dependency graph instead of failing
When dependencies are not cached on the server (common since we removed
proactive caching), the dependency graph now:
- Continues resolving what it can find
- Shows missing dependencies in a separate section with amber styling
- Displays the constraint and which package required them
- Updates the header stats to show "X cached • Y not cached"

This provides a better user experience than showing an error when
some dependencies haven't been downloaded yet.
2026-02-05 09:15:09 -06:00
Mondo Diaz
152af0a852 Fix dependency graph error for invalid version constraints
When a dependency has an invalid version constraint like '>=' (without
a version number), the resolver now treats it as a wildcard and returns
the latest available version instead of failing with 'Dependency not found'.

This handles malformed metadata that may have been stored from PyPI packages.
2026-02-05 09:15:09 -06:00
Mondo Diaz
31edadf3ad Remove proactive PyPI dependency caching feature
The background task queue for proactively caching package dependencies was
causing server instability and unnecessary growth. The PyPI proxy now only
caches packages on-demand when users request them.

Removed:
- PyPI cache worker (background task queue and worker pool)
- PyPICacheTask model and related database schema
- Cache management API endpoints (/pypi/cache/*)
- Background Jobs admin dashboard
- Dependency extraction and queueing logic

Kept:
- On-demand package caching (still works when users request packages)
- Async httpx for non-blocking downloads (prevents health check failures)
- URL-based cache lookups for deduplication
2026-02-05 09:15:09 -06:00
Mondo Diaz
2136e1f0c5 Center text in jobs table columns 2026-02-05 09:15:09 -06:00
Mondo Diaz
ff25677b16 Convert PyPI proxy from sync to async httpx to prevent event loop blocking
The pypi_download_file, pypi_simple_index, and pypi_package_versions endpoints
were using synchronous httpx.Client inside async functions. When upstream PyPI
servers respond slowly, this blocked the entire FastAPI event loop, preventing
health checks from responding. Kubernetes would then kill the pod after the
liveness probe timed out.

Changes:
- httpx.Client → httpx.AsyncClient
- client.get() → await client.get()
- response.iter_bytes() → response.aiter_bytes()

This ensures the event loop remains responsive during slow upstream downloads,
allowing health checks to succeed even when downloads take 20+ seconds.
2026-02-05 09:15:09 -06:00
Mondo Diaz
0a6dad9af0 Add cancel job button and improve jobs table UI
- Remove "All Jobs" title
- Move Status column to front of table
- Add Cancel button for in-progress jobs
- Add cancel endpoint: POST /pypi/cache/cancel/{package_name}
- Add btn-danger CSS styling
2026-02-05 09:15:09 -06:00
Mondo Diaz
36cf288526 Stream downloads to temp file to reduce memory usage
- Download packages in 64KB chunks to temp file instead of loading into memory
- Upload to S3 from temp file (streaming)
- Clean up temp file after processing
- Reduces memory footprint from 2x file size to 1x file size
2026-02-05 09:15:09 -06:00
Mondo Diaz
7008d913bf Increase memory to 1Gi and reduce workers to 1 for stability 2026-02-05 09:15:09 -06:00
Mondo Diaz
46e8c7df70 Add PyPI cache config and bump memory in values-prod.yaml 2026-02-05 09:15:08 -06:00
Mondo Diaz
a3929bfb17 Add PyPI cache config and bump memory in values-stage.yaml 2026-02-05 09:15:08 -06:00
Mondo Diaz
db2805a36c Add PyPI cache config and bump memory in values-dev.yaml 2026-02-05 09:15:08 -06:00
Mondo Diaz
7a6e270d63 Add PyPI cache worker config and increase memory limit
- Add orchard.pypiCache config section to helm values
- Set default workers to 2 (reduced from 5 to limit memory)
- Bump pod memory from 512Mi to 768Mi (request=limit)
- Add ORCHARD_PYPI_CACHE_* env vars to deployment template
2026-02-05 09:15:08 -06:00
Mondo Diaz
df4f9d168b Redesign jobs dashboard with unified table and progress bar
- Add overall progress bar showing completed/active/failed counts
- Unify all job types into single table with Type column
- Simplify status to Working/Pending/Failed badges
- Remove NPM "Coming Soon" section
- Add get_recent_activity() function for future activity feed
- Fix dark mode CSS using CSS variables
2026-02-05 09:15:08 -06:00
Mondo Diaz
1f98caa73c Improve Active Workers table and recover stale tasks
Backend:
- Add _recover_stale_tasks() to reset tasks stuck in 'in_progress'
  from previous crashes (tasks >5 min old get reset to pending)
- Called automatically on startup

Frontend:
- Fix dark mode colors using CSS variables instead of hardcoded values
- Add elapsed time column showing how long task has been running
- Add spinning indicator next to package name
- Add status badge (Running/Stale?)
- Highlight stale tasks (>5 min) in amber
- Auto-updates every 5 seconds with existing refresh
2026-02-05 09:15:08 -06:00
Mondo Diaz
a485852a6f Add Active Workers table to Background Jobs dashboard
Shows currently processing cache tasks in a dynamic table with:
- Package name and version constraint being cached
- Recursion depth and attempt number
- Start timestamp
- Pulsing indicator to show live activity

Backend changes:
- Add get_active_tasks() function to pypi_cache_worker.py
- Add GET /pypi/cache/active endpoint to pypi_proxy.py

Frontend changes:
- Add PyPICacheActiveTask type
- Add getPyPICacheActiveTasks() API function
- Add Active Workers section with animated table
- Auto-refreshes every 5 seconds with existing data
2026-02-05 09:15:08 -06:00
Mondo Diaz
5517048f05 Fix nested dependency depth tracking in PyPI cache worker
When the cache worker downloaded a package through the proxy, dependencies
were always queued with depth=0 instead of depth+1. This meant depth limits
weren't properly enforced for nested dependencies.

Changes:
- Add cache-depth query parameter to pypi_download_file endpoint
- Worker now passes its current depth when fetching packages
- Dependencies are queued at cache_depth+1 instead of hardcoded 0
- Add tests for depth tracking behavior
2026-02-05 09:15:08 -06:00
Mondo Diaz
c7eca269f4 Fix jobs dashboard showing misleading completion message
The dashboard was showing "All jobs completed successfully" whenever
there were no failed tasks, even if there were pending or in-progress
jobs. Now shows:
- "All jobs completed" only when pending=0 and in_progress=0
- "Jobs are processing. No failures yet." when jobs are in queue
2026-02-05 09:15:08 -06:00
Mondo Diaz
6a3a875a9c Add security fixes and code cleanup for PyPI cache
- Add require_admin authentication to cache management endpoints
- Add limit validation (1-500) on failed tasks query
- Add thread lock for worker pool thread safety
- Fix exception handling with separate recovery DB session
- Remove obsolete design doc
2026-02-05 09:15:08 -06:00
Mondo Diaz
a39b6f098f Add Background Jobs dashboard for admin users
New admin page at /admin/jobs showing:
- PyPI cache job status (pending, in-progress, completed, failed)
- Failed task list with error details
- Retry individual packages or retry all failed
- Auto-refresh every 5 seconds (toggleable)
- Placeholder for future NPM cache jobs

Accessible from admin dropdown menu as "Background Jobs".
2026-02-05 09:15:08 -06:00
Mondo Diaz
e0562195df Add robust PyPI dependency caching with task queue
Replace unbounded thread spawning with managed worker pool:
- New pypi_cache_tasks table tracks caching jobs
- Thread pool with 5 workers (configurable via ORCHARD_PYPI_CACHE_WORKERS)
- Automatic retries with exponential backoff (30s, 60s, then fail)
- Deduplication to prevent duplicate caching attempts

New API endpoints for visibility and control:
- GET /pypi/cache/status - queue health summary
- GET /pypi/cache/failed - list failed tasks with errors
- POST /pypi/cache/retry/{package} - retry single package
- POST /pypi/cache/retry-all - retry all failed packages

This fixes silent failures in background dependency caching where
packages would fail to cache without any tracking or retry mechanism.
2026-02-05 09:15:08 -06:00
Mondo Diaz
db7d0bb7c4 Add design doc for PyPI cache robustness improvements 2026-02-05 09:15:08 -06:00
Mondo Diaz
4a287d46c8 Fix proactive dependency caching HTTPS redirect issue
When background threads fetch from our own proxy using the request's
base_url, it returns http:// but ingress requires https://. The 308
redirect was dropping trailing slashes, causing requests to hit the
frontend catch-all route instead of /pypi/simple/.

Force HTTPS explicitly in the background caching function to avoid
the redirect entirely.
2026-02-05 09:15:08 -06:00