openhands-automation 1.0.0a1__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.
- openhands_automation-1.0.0a1/.dockerignore +32 -0
- openhands_automation-1.0.0a1/.github/workflows/ci.yml +56 -0
- openhands_automation-1.0.0a1/.github/workflows/ghcr-build.yml +115 -0
- openhands_automation-1.0.0a1/.github/workflows/pr-review-by-openhands.yml +48 -0
- openhands_automation-1.0.0a1/.github/workflows/pypi-release.yml +40 -0
- openhands_automation-1.0.0a1/.github/workflows/tag-image.yml +58 -0
- openhands_automation-1.0.0a1/.github/workflows/tests.yml +49 -0
- openhands_automation-1.0.0a1/.github/workflows/trigger-deploy-preview.yml +235 -0
- openhands_automation-1.0.0a1/.gitignore +193 -0
- openhands_automation-1.0.0a1/.pre-commit-config.yaml +45 -0
- openhands_automation-1.0.0a1/.python-version +1 -0
- openhands_automation-1.0.0a1/AGENTS.md +213 -0
- openhands_automation-1.0.0a1/LICENSE +21 -0
- openhands_automation-1.0.0a1/Makefile +96 -0
- openhands_automation-1.0.0a1/PKG-INFO +128 -0
- openhands_automation-1.0.0a1/README.md +97 -0
- openhands_automation-1.0.0a1/alembic.ini +37 -0
- openhands_automation-1.0.0a1/containers/Dockerfile +45 -0
- openhands_automation-1.0.0a1/frontend/.env.example +3 -0
- openhands_automation-1.0.0a1/frontend/.eslintrc +73 -0
- openhands_automation-1.0.0a1/frontend/.gitignore +10 -0
- openhands_automation-1.0.0a1/frontend/.husky/pre-commit +2 -0
- openhands_automation-1.0.0a1/frontend/.prettierrc.json +3 -0
- openhands_automation-1.0.0a1/frontend/README.md +234 -0
- openhands_automation-1.0.0a1/frontend/__mocks__/zustand.ts +60 -0
- openhands_automation-1.0.0a1/frontend/commitlint.config.cjs +3 -0
- openhands_automation-1.0.0a1/frontend/package-lock.json +12444 -0
- openhands_automation-1.0.0a1/frontend/package.json +109 -0
- openhands_automation-1.0.0a1/frontend/playwright.config.ts +25 -0
- openhands_automation-1.0.0a1/frontend/public/mockServiceWorker.js +349 -0
- openhands_automation-1.0.0a1/frontend/react-router.config.ts +28 -0
- openhands_automation-1.0.0a1/frontend/scripts/check-translation-completeness.cjs +102 -0
- openhands_automation-1.0.0a1/frontend/scripts/dev-proxy.mjs +101 -0
- openhands_automation-1.0.0a1/frontend/scripts/make-i18n-translations.cjs +51 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/api/automation-service.test.ts +180 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/api/openhands-service.test.ts +61 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/automation-card.test.tsx +136 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/automation-group.test.tsx +53 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/create-instructions.test.tsx +123 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/delete-confirmation-modal.test.tsx +71 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/active-status-badge.test.tsx +15 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx +156 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/activity-log-section.test.tsx +98 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/activity-section.test.tsx +79 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/back-link.test.tsx +29 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/branch-badge.test.tsx +15 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/config-field.test.tsx +29 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/configuration-section.test.tsx +76 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/detail-header.test.tsx +119 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/not-found-state.test.tsx +24 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/plugin-chip.test.tsx +15 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/plugins-section.test.tsx +21 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/prompt-section.test.tsx +21 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/run-status-badge.test.tsx +28 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/detail/section-card.test.tsx +17 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/empty-state.test.tsx +66 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/error-state.test.tsx +23 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/kebab-menu.test.tsx +63 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/metadata-chip.test.tsx +17 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/permission-gating.test.tsx +141 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/search-input.test.tsx +28 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/automations/toggle-switch.test.tsx +38 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/reauth-modal.test.tsx +12 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/shared/modals/modal-backdrop.test.tsx +76 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/components/shared/modals/modal-body.test.tsx +61 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-auto-login.test.ts +102 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-cross-tab-state.test.ts +54 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-escape-key.test.ts +53 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-has-permission.test.ts +83 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-is-authed.test.ts +75 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-language-sync.test.ts +142 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-me.test.ts +61 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-org-sync.test.ts +129 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/hooks/use-theme-sync.test.ts +114 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/routes/automation-detail.test.tsx +277 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/routes/automations-list.test.tsx +242 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/routes/root-layout.test.tsx +173 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/stores/user-store.test.ts +42 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/utils/generate-auth-url.test.ts +88 -0
- openhands_automation-1.0.0a1/frontend/src/__tests__/utils/local-storage.test.ts +60 -0
- openhands_automation-1.0.0a1/frontend/src/api/automation-service.ts +71 -0
- openhands_automation-1.0.0a1/frontend/src/api/axios-clients.ts +21 -0
- openhands_automation-1.0.0a1/frontend/src/api/openhands-service.ts +16 -0
- openhands_automation-1.0.0a1/frontend/src/assets/branding/openhands-logo.svg +9 -0
- openhands_automation-1.0.0a1/frontend/src/components/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/automation-card-skeleton.tsx +19 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/automation-card.tsx +102 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/automation-group.tsx +40 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/create-instructions.tsx +119 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/delete-confirmation-modal.tsx +70 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/active-status-badge.tsx +24 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/activity-log-item.tsx +69 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/activity-log-section.tsx +78 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/activity-section.tsx +77 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/back-link.tsx +18 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/branch-badge.tsx +11 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/config-field.tsx +17 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/configuration-section.tsx +85 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/detail-header.tsx +67 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/detail-skeleton.tsx +28 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/not-found-state.tsx +23 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/plugin-chip.tsx +11 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/plugins-section.tsx +26 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/prompt-section.tsx +25 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/run-status-badge.tsx +58 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/detail/section-card.tsx +17 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/empty-state.tsx +22 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/error-state.tsx +27 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/kebab-menu.tsx +69 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/metadata-chip.tsx +13 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/search-input.tsx +25 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/status-badge.tsx +11 -0
- openhands_automation-1.0.0a1/frontend/src/components/automations/toggle-switch.tsx +33 -0
- openhands_automation-1.0.0a1/frontend/src/components/error-boundary.tsx +48 -0
- openhands_automation-1.0.0a1/frontend/src/components/reauth-modal.tsx +22 -0
- openhands_automation-1.0.0a1/frontend/src/components/shared/modals/modal-backdrop.tsx +35 -0
- openhands_automation-1.0.0a1/frontend/src/components/shared/modals/modal-body.tsx +32 -0
- openhands_automation-1.0.0a1/frontend/src/constants/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/entry.client.tsx +37 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-auth-url.ts +8 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-auto-login.ts +59 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-automation-detail.ts +23 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-automations.ts +35 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-cross-tab-state.ts +48 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-escape-key.ts +26 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-has-permission.ts +9 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-is-authed.ts +25 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-language-sync.ts +54 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-me.ts +18 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-org-sync.ts +40 -0
- openhands_automation-1.0.0a1/frontend/src/hooks/use-theme-sync.ts +39 -0
- openhands_automation-1.0.0a1/frontend/src/i18n/index.ts +38 -0
- openhands_automation-1.0.0a1/frontend/src/i18n/translation.json +1005 -0
- openhands_automation-1.0.0a1/frontend/src/icons/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/icons/activity.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/bell.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/calendar.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/check-circle.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/chevron-down.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/chevron-left.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/clock.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/cog.svg +4 -0
- openhands_automation-1.0.0a1/frontend/src/icons/database.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/exclamation-circle.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/folder.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/git-branch.svg +6 -0
- openhands_automation-1.0.0a1/frontend/src/icons/kebab-vertical.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/power.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/puzzle.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/search.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/sparkle.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/svg.d.ts +6 -0
- openhands_automation-1.0.0a1/frontend/src/icons/target.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/terminal.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/trash.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/x-circle.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/icons/x-mark.svg +3 -0
- openhands_automation-1.0.0a1/frontend/src/index.css +40 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/auth-handlers.ts +41 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/automation-handlers.ts +123 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/automation-runs.mock.ts +61 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/automations.mock.ts +127 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/browser.ts +4 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/handlers.ts +4 -0
- openhands_automation-1.0.0a1/frontend/src/mocks/node.ts +4 -0
- openhands_automation-1.0.0a1/frontend/src/query-client-config.ts +62 -0
- openhands_automation-1.0.0a1/frontend/src/root.tsx +35 -0
- openhands_automation-1.0.0a1/frontend/src/routes/automation-detail.tsx +93 -0
- openhands_automation-1.0.0a1/frontend/src/routes/automations-list.tsx +144 -0
- openhands_automation-1.0.0a1/frontend/src/routes/root-layout.tsx +93 -0
- openhands_automation-1.0.0a1/frontend/src/routes.ts +13 -0
- openhands_automation-1.0.0a1/frontend/src/stores/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/stores/user-store.ts +22 -0
- openhands_automation-1.0.0a1/frontend/src/tailwind.css +52 -0
- openhands_automation-1.0.0a1/frontend/src/types/.gitkeep +0 -0
- openhands_automation-1.0.0a1/frontend/src/types/automation.ts +48 -0
- openhands_automation-1.0.0a1/frontend/src/types/user.ts +9 -0
- openhands_automation-1.0.0a1/frontend/src/utils/custom-toast-handlers.tsx +42 -0
- openhands_automation-1.0.0a1/frontend/src/utils/generate-auth-url.ts +43 -0
- openhands_automation-1.0.0a1/frontend/src/utils/local-storage.ts +36 -0
- openhands_automation-1.0.0a1/frontend/src/utils/permissions.ts +8 -0
- openhands_automation-1.0.0a1/frontend/src/utils/toast-duration.ts +27 -0
- openhands_automation-1.0.0a1/frontend/src/utils/utils.ts +6 -0
- openhands_automation-1.0.0a1/frontend/tailwind.config.js +65 -0
- openhands_automation-1.0.0a1/frontend/tsconfig.json +30 -0
- openhands_automation-1.0.0a1/frontend/vite.config.ts +83 -0
- openhands_automation-1.0.0a1/frontend/vitest.setup.ts +39 -0
- openhands_automation-1.0.0a1/migrations/env.py +152 -0
- openhands_automation-1.0.0a1/migrations/script.py.mako +25 -0
- openhands_automation-1.0.0a1/migrations/versions/001_initial_schema.py +129 -0
- openhands_automation-1.0.0a1/migrations/versions/002_tarball_uploads.py +64 -0
- openhands_automation-1.0.0a1/migrations/versions/003_event_triggers.py +92 -0
- openhands_automation-1.0.0a1/migrations/versions/004_add_prompt_column.py +29 -0
- openhands_automation-1.0.0a1/openhands/automation/__init__.py +3 -0
- openhands_automation-1.0.0a1/openhands/automation/app.py +306 -0
- openhands_automation-1.0.0a1/openhands/automation/auth.py +372 -0
- openhands_automation-1.0.0a1/openhands/automation/backends/__init__.py +67 -0
- openhands_automation-1.0.0a1/openhands/automation/backends/base.py +145 -0
- openhands_automation-1.0.0a1/openhands/automation/backends/cloud.py +302 -0
- openhands_automation-1.0.0a1/openhands/automation/backends/local.py +180 -0
- openhands_automation-1.0.0a1/openhands/automation/config.py +573 -0
- openhands_automation-1.0.0a1/openhands/automation/constants.py +83 -0
- openhands_automation-1.0.0a1/openhands/automation/db.py +210 -0
- openhands_automation-1.0.0a1/openhands/automation/dispatcher.py +429 -0
- openhands_automation-1.0.0a1/openhands/automation/event_router.py +192 -0
- openhands_automation-1.0.0a1/openhands/automation/event_schemas/__init__.py +126 -0
- openhands_automation-1.0.0a1/openhands/automation/event_schemas/custom.py +108 -0
- openhands_automation-1.0.0a1/openhands/automation/event_schemas/detection.py +136 -0
- openhands_automation-1.0.0a1/openhands/automation/event_schemas/github.py +483 -0
- openhands_automation-1.0.0a1/openhands/automation/exceptions.py +29 -0
- openhands_automation-1.0.0a1/openhands/automation/execution.py +568 -0
- openhands_automation-1.0.0a1/openhands/automation/filter_eval.py +192 -0
- openhands_automation-1.0.0a1/openhands/automation/logger.py +115 -0
- openhands_automation-1.0.0a1/openhands/automation/models.py +312 -0
- openhands_automation-1.0.0a1/openhands/automation/preset_router.py +531 -0
- openhands_automation-1.0.0a1/openhands/automation/presets/__init__.py +9 -0
- openhands_automation-1.0.0a1/openhands/automation/presets/plugin/sdk_main.py +328 -0
- openhands_automation-1.0.0a1/openhands/automation/presets/plugin/setup.sh +23 -0
- openhands_automation-1.0.0a1/openhands/automation/presets/prompt/sdk_main.py +315 -0
- openhands_automation-1.0.0a1/openhands/automation/presets/prompt/setup.sh +23 -0
- openhands_automation-1.0.0a1/openhands/automation/router.py +347 -0
- openhands_automation-1.0.0a1/openhands/automation/scheduler.py +202 -0
- openhands_automation-1.0.0a1/openhands/automation/schemas.py +613 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/__init__.py +18 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/factory.py +36 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/file_store.py +64 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/google_cloud.py +195 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/local.py +125 -0
- openhands_automation-1.0.0a1/openhands/automation/storage/s3.py +340 -0
- openhands_automation-1.0.0a1/openhands/automation/trigger_matcher.py +89 -0
- openhands_automation-1.0.0a1/openhands/automation/uploads.py +323 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/__init__.py +24 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/agent_server.py +161 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/api_key.py +98 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/cron.py +143 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/log_context.py +28 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/run.py +238 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/sandbox.py +212 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/tarball_validation.py +170 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/time.py +15 -0
- openhands_automation-1.0.0a1/openhands/automation/utils/webhook.py +235 -0
- openhands_automation-1.0.0a1/openhands/automation/watchdog.py +284 -0
- openhands_automation-1.0.0a1/openhands/automation/webhook_router.py +275 -0
- openhands_automation-1.0.0a1/pyproject.toml +130 -0
- openhands_automation-1.0.0a1/scripts/test_automation.py +160 -0
- openhands_automation-1.0.0a1/scripts/test_tarball/main.py +130 -0
- openhands_automation-1.0.0a1/scripts/test_tarball/setup.sh +16 -0
- openhands_automation-1.0.0a1/tests/__init__.py +0 -0
- openhands_automation-1.0.0a1/tests/conftest.py +205 -0
- openhands_automation-1.0.0a1/tests/test_auth.py +884 -0
- openhands_automation-1.0.0a1/tests/test_backends.py +428 -0
- openhands_automation-1.0.0a1/tests/test_config.py +392 -0
- openhands_automation-1.0.0a1/tests/test_db.py +264 -0
- openhands_automation-1.0.0a1/tests/test_disable_automation.py +520 -0
- openhands_automation-1.0.0a1/tests/test_dispatcher.py +551 -0
- openhands_automation-1.0.0a1/tests/test_event_router.py +400 -0
- openhands_automation-1.0.0a1/tests/test_event_schemas.py +896 -0
- openhands_automation-1.0.0a1/tests/test_execution.py +300 -0
- openhands_automation-1.0.0a1/tests/test_filter_eval.py +282 -0
- openhands_automation-1.0.0a1/tests/test_frontend.py +188 -0
- openhands_automation-1.0.0a1/tests/test_health.py +42 -0
- openhands_automation-1.0.0a1/tests/test_local_mode.py +332 -0
- openhands_automation-1.0.0a1/tests/test_openapi.py +30 -0
- openhands_automation-1.0.0a1/tests/test_preset_router.py +1133 -0
- openhands_automation-1.0.0a1/tests/test_router.py +1438 -0
- openhands_automation-1.0.0a1/tests/test_scheduler.py +945 -0
- openhands_automation-1.0.0a1/tests/test_storage.py +240 -0
- openhands_automation-1.0.0a1/tests/test_storage_integration.py +252 -0
- openhands_automation-1.0.0a1/tests/test_storage_local.py +362 -0
- openhands_automation-1.0.0a1/tests/test_storage_s3.py +399 -0
- openhands_automation-1.0.0a1/tests/test_storage_s3_integration.py +324 -0
- openhands_automation-1.0.0a1/tests/test_tarball_validation.py +333 -0
- openhands_automation-1.0.0a1/tests/test_uploads.py +192 -0
- openhands_automation-1.0.0a1/tests/test_uploads_integration.py +227 -0
- openhands_automation-1.0.0a1/tests/test_watchdog.py +318 -0
- openhands_automation-1.0.0a1/tests/test_webhook_router.py +292 -0
- openhands_automation-1.0.0a1/tests/test_webhook_utils.py +322 -0
- openhands_automation-1.0.0a1/uv.lock +4109 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Version control
|
|
2
|
+
.git
|
|
3
|
+
.gitignore
|
|
4
|
+
|
|
5
|
+
# CI/CD
|
|
6
|
+
.github
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
.venv
|
|
10
|
+
__pycache__
|
|
11
|
+
*.pyc
|
|
12
|
+
.pytest_cache
|
|
13
|
+
.ruff_cache
|
|
14
|
+
.mypy_cache
|
|
15
|
+
|
|
16
|
+
# Frontend (built inside Docker via multi-stage)
|
|
17
|
+
frontend/node_modules
|
|
18
|
+
frontend/build
|
|
19
|
+
frontend/coverage
|
|
20
|
+
frontend/test-results
|
|
21
|
+
frontend/playwright-report
|
|
22
|
+
frontend/.react-router
|
|
23
|
+
|
|
24
|
+
# IDE / editor
|
|
25
|
+
.vscode
|
|
26
|
+
.idea
|
|
27
|
+
*.swp
|
|
28
|
+
|
|
29
|
+
# Misc
|
|
30
|
+
.pr
|
|
31
|
+
.stakpak
|
|
32
|
+
.pre-commit-config.yaml
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
# .github/workflows/precommit.yml
|
|
3
|
+
name: ci
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: ['**']
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
backend:
|
|
13
|
+
runs-on: ubuntu-24.04
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v5
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: '3.12'
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v7
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --frozen --group dev
|
|
29
|
+
|
|
30
|
+
- name: Run pre-commit (all files)
|
|
31
|
+
run: uv run pre-commit run --all-files --show-diff-on-failure
|
|
32
|
+
|
|
33
|
+
frontend:
|
|
34
|
+
runs-on: ubuntu-24.04
|
|
35
|
+
defaults:
|
|
36
|
+
run:
|
|
37
|
+
working-directory: frontend
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v5
|
|
40
|
+
|
|
41
|
+
- name: Setup Node
|
|
42
|
+
uses: actions/setup-node@v3
|
|
43
|
+
with:
|
|
44
|
+
node-version: 24
|
|
45
|
+
|
|
46
|
+
- name: NPM dependencies
|
|
47
|
+
run: npm ci
|
|
48
|
+
|
|
49
|
+
- name: Lint
|
|
50
|
+
run: npm run lint
|
|
51
|
+
|
|
52
|
+
- name: Test
|
|
53
|
+
run: npm run test
|
|
54
|
+
|
|
55
|
+
- name: Build
|
|
56
|
+
run: npm run build
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Workflow that builds and pushes the automation docker image to ghcr.io
|
|
3
|
+
name: Docker
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
tags:
|
|
10
|
+
- '*'
|
|
11
|
+
pull_request:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
reason:
|
|
15
|
+
description: Reason for manual trigger
|
|
16
|
+
required: true
|
|
17
|
+
default: ''
|
|
18
|
+
|
|
19
|
+
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
|
20
|
+
concurrency:
|
|
21
|
+
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
|
22
|
+
cancel-in-progress: true
|
|
23
|
+
|
|
24
|
+
env:
|
|
25
|
+
REGISTRY: ghcr.io
|
|
26
|
+
IMAGE_NAME: openhands/automation
|
|
27
|
+
RELEVANT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
ghcr_build:
|
|
31
|
+
name: Build and Push Automation Image
|
|
32
|
+
runs-on: ubuntu-24.04
|
|
33
|
+
permissions:
|
|
34
|
+
contents: read
|
|
35
|
+
packages: write
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
41
|
+
|
|
42
|
+
- name: Set up Docker Buildx
|
|
43
|
+
uses: docker/setup-buildx-action@v3
|
|
44
|
+
with:
|
|
45
|
+
driver-opts: network=host
|
|
46
|
+
|
|
47
|
+
- name: Login to GHCR
|
|
48
|
+
uses: docker/login-action@v3
|
|
49
|
+
with:
|
|
50
|
+
registry: ${{ env.REGISTRY }}
|
|
51
|
+
username: ${{ github.repository_owner }}
|
|
52
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
53
|
+
|
|
54
|
+
- name: Get short SHA
|
|
55
|
+
id: short_sha
|
|
56
|
+
run: echo "SHORT_SHA=$(echo ${{ env.RELEVANT_SHA }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
|
57
|
+
|
|
58
|
+
- name: Extract metadata (tags, labels) for Docker
|
|
59
|
+
id: meta
|
|
60
|
+
uses: docker/metadata-action@v5
|
|
61
|
+
with:
|
|
62
|
+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
63
|
+
tags: |
|
|
64
|
+
type=ref,event=branch
|
|
65
|
+
type=ref,event=pr
|
|
66
|
+
type=sha
|
|
67
|
+
type=sha,format=long
|
|
68
|
+
type=semver,pattern={{version}}
|
|
69
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
70
|
+
type=semver,pattern={{major}}
|
|
71
|
+
flavor: |
|
|
72
|
+
latest=auto
|
|
73
|
+
prefix=
|
|
74
|
+
suffix=
|
|
75
|
+
env:
|
|
76
|
+
DOCKER_METADATA_PR_HEAD_SHA: true
|
|
77
|
+
|
|
78
|
+
- name: Build and push Docker image
|
|
79
|
+
if: '!github.event.pull_request.head.repo.fork'
|
|
80
|
+
uses: docker/build-push-action@v5
|
|
81
|
+
with:
|
|
82
|
+
context: .
|
|
83
|
+
file: containers/Dockerfile
|
|
84
|
+
push: true
|
|
85
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
86
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
87
|
+
platforms: linux/amd64
|
|
88
|
+
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
|
|
89
|
+
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
|
|
90
|
+
provenance: true
|
|
91
|
+
sbom: true
|
|
92
|
+
|
|
93
|
+
# Forked repos can't push to GHCR, so we just build to verify the image builds
|
|
94
|
+
- name: Build Docker image (fork)
|
|
95
|
+
if: github.event.pull_request.head.repo.fork
|
|
96
|
+
uses: docker/build-push-action@v5
|
|
97
|
+
with:
|
|
98
|
+
context: .
|
|
99
|
+
file: containers/Dockerfile
|
|
100
|
+
push: false
|
|
101
|
+
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.RELEVANT_SHA }}
|
|
102
|
+
platforms: linux/amd64
|
|
103
|
+
|
|
104
|
+
trigger-deploy-preview:
|
|
105
|
+
name: Trigger Deploy Preview
|
|
106
|
+
needs: ghcr_build
|
|
107
|
+
# Only trigger for PRs from non-fork repos (fork check mirrors the push condition in ghcr_build)
|
|
108
|
+
if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
|
|
109
|
+
uses: ./.github/workflows/trigger-deploy-preview.yml
|
|
110
|
+
with:
|
|
111
|
+
pr_number: ${{ github.event.pull_request.number }}
|
|
112
|
+
head_sha: ${{ github.event.pull_request.head.sha }}
|
|
113
|
+
pr_title: ${{ github.event.pull_request.title }}
|
|
114
|
+
secrets:
|
|
115
|
+
ALLHANDS_BOT_GITHUB_PAT: ${{ secrets.ALLHANDS_BOT_GITHUB_PAT }}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: PR Review by OpenHands
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
# TEMPORARY MITIGATION (Clinejection hardening)
|
|
6
|
+
#
|
|
7
|
+
# We temporarily avoid `pull_request_target` here. We'll restore it after the PR review
|
|
8
|
+
# workflow is fully hardened for untrusted execution.
|
|
9
|
+
pull_request:
|
|
10
|
+
types: [opened, ready_for_review, labeled, review_requested]
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pull-requests: write
|
|
15
|
+
issues: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
pr-review:
|
|
19
|
+
# Note: fork PRs will not have access to repository secrets under `pull_request`.
|
|
20
|
+
# Skip forks to avoid noisy failures until we restore a hardened `pull_request_target` flow.
|
|
21
|
+
if: |
|
|
22
|
+
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
23
|
+
(
|
|
24
|
+
(github.event.action == 'opened' && github.event.pull_request.draft == false) ||
|
|
25
|
+
github.event.action == 'ready_for_review' ||
|
|
26
|
+
(github.event.action == 'labeled' && github.event.label.name == 'review-this') ||
|
|
27
|
+
(
|
|
28
|
+
github.event.action == 'review_requested' &&
|
|
29
|
+
(
|
|
30
|
+
github.event.requested_reviewer.login == 'openhands-agent' ||
|
|
31
|
+
github.event.requested_reviewer.login == 'all-hands-bot'
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
concurrency:
|
|
36
|
+
group: pr-review-${{ github.event.pull_request.number }}
|
|
37
|
+
cancel-in-progress: true
|
|
38
|
+
runs-on: ubuntu-24.04
|
|
39
|
+
steps:
|
|
40
|
+
- name: Run PR Review
|
|
41
|
+
uses: OpenHands/extensions/plugins/pr-review@main
|
|
42
|
+
with:
|
|
43
|
+
llm-model: litellm_proxy/claude-sonnet-4-5-20250929
|
|
44
|
+
llm-base-url: https://llm-proxy.app.all-hands.dev
|
|
45
|
+
review-style: roasted
|
|
46
|
+
llm-api-key: ${{ secrets.LLM_API_KEY }}
|
|
47
|
+
github-token: ${{ secrets.ALLHANDS_BOT_GITHUB_PAT }}
|
|
48
|
+
lmnr-api-key: ${{ secrets.LMNR_SKILLS_API_KEY }}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Publishes the openhands-automation PyPI package using trusted publishing (OIDC).
|
|
3
|
+
# Triggered when a tag is pushed or manually via workflow_dispatch.
|
|
4
|
+
# Requires trusted publishing to be configured in PyPI project settings.
|
|
5
|
+
name: Publish PyPI Package
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags:
|
|
10
|
+
- '*'
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
reason:
|
|
14
|
+
description: Reason for manual trigger
|
|
15
|
+
required: true
|
|
16
|
+
default: ''
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release:
|
|
20
|
+
runs-on: ubuntu-24.04
|
|
21
|
+
permissions:
|
|
22
|
+
id-token: write
|
|
23
|
+
contents: read
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v7
|
|
30
|
+
with:
|
|
31
|
+
version: latest
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Build package
|
|
37
|
+
run: uv build
|
|
38
|
+
|
|
39
|
+
- name: Publish to PyPI
|
|
40
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Adds a git-tag name to existing Docker images.
|
|
3
|
+
# Triggered when a tag is pushed: finds the images built at the tag's commit
|
|
4
|
+
# (tagged `sha-<full>`) and adds the tag name as an alias for the same manifest.
|
|
5
|
+
# Semver tags (X.Y.Z) also get X.Y, X, and latest aliases.
|
|
6
|
+
# No rebuild — pure registry-side retag via `docker buildx imagetools create`.
|
|
7
|
+
name: Tag Docker images
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- '*'
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
retag:
|
|
16
|
+
runs-on: ubuntu-24.04
|
|
17
|
+
permissions:
|
|
18
|
+
packages: write
|
|
19
|
+
steps:
|
|
20
|
+
- name: Login to GHCR
|
|
21
|
+
uses: docker/login-action@v3
|
|
22
|
+
with:
|
|
23
|
+
registry: ghcr.io
|
|
24
|
+
username: ${{ github.repository_owner }}
|
|
25
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
26
|
+
|
|
27
|
+
- name: Set up Docker Buildx
|
|
28
|
+
uses: docker/setup-buildx-action@v3
|
|
29
|
+
|
|
30
|
+
- name: Compute tags
|
|
31
|
+
id: meta
|
|
32
|
+
uses: docker/metadata-action@v5
|
|
33
|
+
with:
|
|
34
|
+
images: ghcr.io/openhands/automation
|
|
35
|
+
flavor: latest=auto
|
|
36
|
+
tags: |
|
|
37
|
+
type=ref,event=tag
|
|
38
|
+
type=semver,pattern={{version}}
|
|
39
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
40
|
+
type=semver,pattern={{major}}
|
|
41
|
+
|
|
42
|
+
- name: Add tags to existing image
|
|
43
|
+
env:
|
|
44
|
+
SRC: ghcr.io/openhands/automation:sha-${{ github.sha }}
|
|
45
|
+
TAGS: ${{ steps.meta.outputs.tags }}
|
|
46
|
+
shell: bash
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
if ! docker buildx imagetools inspect "$SRC" > /dev/null 2>&1; then
|
|
50
|
+
echo "::error::Source image $SRC does not exist. The Docker workflow for commit ${{ github.sha }} may not have completed successfully. Re-run this workflow once the build finishes."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
args=()
|
|
54
|
+
while IFS= read -r tag; do
|
|
55
|
+
[[ -z "$tag" ]] && continue
|
|
56
|
+
args+=(-t "$tag")
|
|
57
|
+
done <<< "$TAGS"
|
|
58
|
+
docker buildx imagetools create "${args[@]}" "$SRC"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Run tests
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: ['**']
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
unit-tests:
|
|
16
|
+
runs-on: ubuntu-24.04
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v5
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v7
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
python-version: '3.12'
|
|
26
|
+
|
|
27
|
+
- name: Install deps
|
|
28
|
+
run: uv sync --frozen --group dev
|
|
29
|
+
|
|
30
|
+
- name: Run unit tests with coverage
|
|
31
|
+
run: |
|
|
32
|
+
rm -f .coverage
|
|
33
|
+
CI=true uv run python -m pytest -vvs \
|
|
34
|
+
--cov=openhands/automation \
|
|
35
|
+
--cov-report=term-missing \
|
|
36
|
+
--cov-report=xml:coverage.xml \
|
|
37
|
+
--cov-fail-under=0 \
|
|
38
|
+
tests/
|
|
39
|
+
|
|
40
|
+
- name: Pytest coverage PR comment
|
|
41
|
+
if: always() && github.event_name == 'pull_request'
|
|
42
|
+
continue-on-error: true
|
|
43
|
+
uses: MishaKav/pytest-coverage-comment@v1
|
|
44
|
+
with:
|
|
45
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
46
|
+
pytest-xml-coverage-path: coverage.xml
|
|
47
|
+
title: Coverage Report
|
|
48
|
+
create-new-comment: false
|
|
49
|
+
hide-report: false
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Workflow that creates/updates a deploy repo PR for automation PRs
|
|
3
|
+
# Called by ghcr-build.yml after Docker build completes successfully
|
|
4
|
+
name: Trigger Deploy Preview
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
workflow_call:
|
|
8
|
+
inputs:
|
|
9
|
+
pr_number:
|
|
10
|
+
description: The automation PR number
|
|
11
|
+
required: true
|
|
12
|
+
type: number
|
|
13
|
+
head_sha:
|
|
14
|
+
description: The commit SHA to deploy
|
|
15
|
+
required: true
|
|
16
|
+
type: string
|
|
17
|
+
pr_title:
|
|
18
|
+
description: The PR title
|
|
19
|
+
required: true
|
|
20
|
+
type: string
|
|
21
|
+
secrets:
|
|
22
|
+
ALLHANDS_BOT_GITHUB_PAT:
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: deploy-preview-${{ inputs.pr_number }}
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
env:
|
|
30
|
+
DEPLOY_REPO: OpenHands/deploy
|
|
31
|
+
BASE_BRANCH: main
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
trigger-deploy:
|
|
35
|
+
name: Create Deploy Preview PR
|
|
36
|
+
runs-on: ubuntu-24.04
|
|
37
|
+
permissions:
|
|
38
|
+
contents: read
|
|
39
|
+
pull-requests: write
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout deploy repo
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
repository: ${{ env.DEPLOY_REPO }}
|
|
45
|
+
ref: ${{ env.BASE_BRANCH }}
|
|
46
|
+
token: ${{ secrets.ALLHANDS_BOT_GITHUB_PAT }}
|
|
47
|
+
fetch-depth: 0
|
|
48
|
+
path: deploy-repo
|
|
49
|
+
|
|
50
|
+
- name: Install yq
|
|
51
|
+
run: |
|
|
52
|
+
YQ_VERSION="v4.52.4"
|
|
53
|
+
YQ_CHECKSUM="0c4d965ea944b64b8fddaf7f27779ee3034e5693263786506ccd1c120f184e8c"
|
|
54
|
+
wget -qO /tmp/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64"
|
|
55
|
+
echo "${YQ_CHECKSUM} /tmp/yq" | sha256sum -c -
|
|
56
|
+
sudo mv /tmp/yq /usr/local/bin/yq
|
|
57
|
+
sudo chmod +x /usr/local/bin/yq
|
|
58
|
+
|
|
59
|
+
- name: Update AUTOMATION_SHA and push
|
|
60
|
+
id: deploy-pr
|
|
61
|
+
run: |
|
|
62
|
+
cd deploy-repo
|
|
63
|
+
BRANCH_NAME="au-pr-${{ inputs.pr_number }}"
|
|
64
|
+
BASE_BRANCH="${{ env.BASE_BRANCH }}"
|
|
65
|
+
|
|
66
|
+
# Configure git
|
|
67
|
+
git config user.name "all-hands-bot"
|
|
68
|
+
git config user.email "all-hands-bot@all-hands.dev"
|
|
69
|
+
|
|
70
|
+
# Fetch all branches
|
|
71
|
+
git fetch origin "$BASE_BRANCH"
|
|
72
|
+
git fetch origin "$BRANCH_NAME" 2>/dev/null || true
|
|
73
|
+
|
|
74
|
+
# Check if branch already exists remotely
|
|
75
|
+
if git rev-parse --verify "origin/$BRANCH_NAME" >/dev/null 2>&1; then
|
|
76
|
+
echo "Branch $BRANCH_NAME exists remotely, checking out and rebasing"
|
|
77
|
+
git checkout -B "$BRANCH_NAME" "origin/$BRANCH_NAME"
|
|
78
|
+
# Rebase onto latest base branch to pick up any upstream changes
|
|
79
|
+
git rebase "origin/$BASE_BRANCH" || {
|
|
80
|
+
echo "Rebase failed, aborting and resetting to base"
|
|
81
|
+
git rebase --abort
|
|
82
|
+
git reset --hard "origin/$BASE_BRANCH"
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
echo "Branch $BRANCH_NAME does not exist, creating from $BASE_BRANCH"
|
|
86
|
+
git checkout -B "$BRANCH_NAME" "origin/$BASE_BRANCH"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Verify deploy.yaml exists
|
|
90
|
+
if [ ! -f .github/workflows/deploy.yaml ]; then
|
|
91
|
+
echo "Error: .github/workflows/deploy.yaml not found" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Update AUTOMATION_SHA in deploy.yaml
|
|
96
|
+
HEAD_SHA="${{ inputs.head_sha }}"
|
|
97
|
+
echo "Updating AUTOMATION_SHA to $HEAD_SHA"
|
|
98
|
+
if ! yq e -i ".env.AUTOMATION_SHA = \"${HEAD_SHA}\"" .github/workflows/deploy.yaml; then
|
|
99
|
+
echo "Error: Failed to update deploy.yaml with yq" >&2
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# Check if there are changes to commit
|
|
104
|
+
if git diff --quiet; then
|
|
105
|
+
echo "No changes to commit - SHA already up to date"
|
|
106
|
+
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
107
|
+
else
|
|
108
|
+
echo "Changes detected, committing"
|
|
109
|
+
git add .github/workflows/deploy.yaml
|
|
110
|
+
git commit -m "Update AUTOMATION_SHA to $HEAD_SHA
|
|
111
|
+
|
|
112
|
+
Automation PR: https://github.com/${{ github.repository }}/pull/${{ inputs.pr_number }}
|
|
113
|
+
Commit: $HEAD_SHA"
|
|
114
|
+
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Push the branch (force-with-lease is safer than force for rebased branches)
|
|
118
|
+
git push --force-with-lease origin "$BRANCH_NAME"
|
|
119
|
+
|
|
120
|
+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
121
|
+
|
|
122
|
+
- name: Create or get deploy PR
|
|
123
|
+
id: manage-pr
|
|
124
|
+
env:
|
|
125
|
+
GH_TOKEN: ${{ secrets.ALLHANDS_BOT_GITHUB_PAT }}
|
|
126
|
+
BRANCH_NAME: ${{ steps.deploy-pr.outputs.branch_name }}
|
|
127
|
+
PR_TITLE: ${{ inputs.pr_title }}
|
|
128
|
+
run: |
|
|
129
|
+
cd deploy-repo
|
|
130
|
+
|
|
131
|
+
# Check if PR already exists for this branch
|
|
132
|
+
if ! EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --base "${{ env.BASE_BRANCH }}" --json number,url --jq '.[0]'); then
|
|
133
|
+
echo "Error: Failed to query existing PRs" >&2
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
|
|
138
|
+
PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
|
|
139
|
+
echo "PR already exists: $PR_URL"
|
|
140
|
+
echo "deploy_pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
|
141
|
+
else
|
|
142
|
+
# Create PR body using heredoc to avoid shell injection from PR title
|
|
143
|
+
PR_BODY=$(cat <<EOF
|
|
144
|
+
**Preview branch for Automation PR #${{ inputs.pr_number }}**
|
|
145
|
+
|
|
146
|
+
This PR was automatically created to test automation changes.
|
|
147
|
+
|
|
148
|
+
**Changes:**
|
|
149
|
+
- \`AUTOMATION_SHA\` → \`${{ inputs.head_sha }}\`
|
|
150
|
+
|
|
151
|
+
**Source PR:** https://github.com/${{ github.repository }}/pull/${{ inputs.pr_number }}
|
|
152
|
+
**Source PR Title:** ${PR_TITLE}
|
|
153
|
+
EOF
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Create new PR against automations-project branch
|
|
157
|
+
if ! PR_URL=$(gh pr create \
|
|
158
|
+
--title "Preview: Automation PR #${{ inputs.pr_number }}" \
|
|
159
|
+
--body "$PR_BODY" \
|
|
160
|
+
--base "${{ env.BASE_BRANCH }}" \
|
|
161
|
+
--head "$BRANCH_NAME"); then
|
|
162
|
+
echo "Error: Failed to create PR" >&2
|
|
163
|
+
exit 1
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
echo "Created new PR: $PR_URL"
|
|
167
|
+
echo "deploy_pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
- name: Comment on automation PR
|
|
171
|
+
uses: actions/github-script@v7
|
|
172
|
+
env:
|
|
173
|
+
DEPLOY_PR_URL: ${{ steps.manage-pr.outputs.deploy_pr_url }}
|
|
174
|
+
HAS_CHANGES: ${{ steps.deploy-pr.outputs.has_changes }}
|
|
175
|
+
with:
|
|
176
|
+
script: |
|
|
177
|
+
const prNumber = ${{ inputs.pr_number }};
|
|
178
|
+
const deployPrUrl = process.env.DEPLOY_PR_URL;
|
|
179
|
+
const headSha = '${{ inputs.head_sha }}';
|
|
180
|
+
const hasChanges = process.env.HAS_CHANGES === 'true';
|
|
181
|
+
|
|
182
|
+
const etTime = new Intl.DateTimeFormat('en-US', {
|
|
183
|
+
timeZone: 'America/New_York',
|
|
184
|
+
year: 'numeric',
|
|
185
|
+
month: 'short',
|
|
186
|
+
day: '2-digit',
|
|
187
|
+
hour: '2-digit',
|
|
188
|
+
minute: '2-digit',
|
|
189
|
+
second: '2-digit',
|
|
190
|
+
hour12: true
|
|
191
|
+
}).format(new Date());
|
|
192
|
+
|
|
193
|
+
const statusMessage = hasChanges
|
|
194
|
+
? 'A deploy preview has been created/updated for this PR.'
|
|
195
|
+
: 'No changes needed - deploy preview is already up to date.';
|
|
196
|
+
|
|
197
|
+
const commentBody = `🚀 **Deploy Preview PR Created/Updated**
|
|
198
|
+
|
|
199
|
+
${statusMessage}
|
|
200
|
+
|
|
201
|
+
**Deploy PR:** ${deployPrUrl}
|
|
202
|
+
**Automation SHA:** \`${headSha}\`
|
|
203
|
+
**Last updated:** ${etTime} ET
|
|
204
|
+
|
|
205
|
+
Once the deploy PR's CI passes, the automation service will be deployed to the feature environment.`;
|
|
206
|
+
|
|
207
|
+
// Find existing bot comment
|
|
208
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
209
|
+
issue_number: prNumber,
|
|
210
|
+
owner: context.repo.owner,
|
|
211
|
+
repo: context.repo.repo
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const botComment = comments.find(comment =>
|
|
215
|
+
comment.user.login === 'github-actions[bot]' &&
|
|
216
|
+
comment.body.includes('Deploy Preview PR Created/Updated')
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (botComment) {
|
|
220
|
+
await github.rest.issues.updateComment({
|
|
221
|
+
comment_id: botComment.id,
|
|
222
|
+
owner: context.repo.owner,
|
|
223
|
+
repo: context.repo.repo,
|
|
224
|
+
body: commentBody
|
|
225
|
+
});
|
|
226
|
+
console.log('Updated existing comment');
|
|
227
|
+
} else {
|
|
228
|
+
await github.rest.issues.createComment({
|
|
229
|
+
issue_number: prNumber,
|
|
230
|
+
owner: context.repo.owner,
|
|
231
|
+
repo: context.repo.repo,
|
|
232
|
+
body: commentBody
|
|
233
|
+
});
|
|
234
|
+
console.log('Created new comment');
|
|
235
|
+
}
|