Compare commits

...

133 Commits

Author SHA1 Message Date
Robin Shen
9c756c74ca fix: Websocket not closed when session times out (OD-2622) 2025-12-08 22:40:41 +08:00
Robin Shen
241c70d10d chore: Add missing translations 2025-12-06 12:47:31 +08:00
Robin Shen
abcc00f051 chore: Upgrade tika for avoid potential security vulnerabilities 2025-12-06 11:44:26 +08:00
Robin Shen
4051221b95 Merge branch 'main' of https://code.onedev.io/onedev/server 2025-12-06 11:22:03 +08:00
Robin Shen
eff4d2a658 feat: Able to use variables from workspace files for run container step (OD-2620) 2025-12-06 11:21:29 +08:00
Robin Shen
4e7fcd1807
Edit .trivyignore 2025-12-06 01:07:48 +00:00
Robin Shen
8554de9e13 wip: Add page tools to AI chatter 2025-12-05 20:47:30 +08:00
Robin Shen
b3d207a835 chore: Add migration 2025-11-30 10:34:34 +08:00
Robin Shen
136426bcdd chore: Fix compilation error 2025-11-30 09:35:35 +08:00
Robin Shen
86ed0e4aac Merge branch 'main' of https://code.onedev.io/onedev/server 2025-11-30 09:26:35 +08:00
Robin Shen
b9ee6f987b wip: AI user 2025-11-30 09:20:18 +08:00
Robin Shen
44ec074b93 chore: Bump version 2025-11-27 20:35:30 +08:00
Robin Shen
3b0de1e0ec fix: Git Checkout with LFS: Project not found or inaccessible (OD-2612) 2025-11-27 20:34:26 +08:00
Robin Shen
82fe2253ff chore: Bump version 2025-11-27 09:22:25 +08:00
Robin Shen
d392f3def0 chore: Update server-ee module 2025-11-27 09:20:33 +08:00
Robin Shen
a73df6edd1 chore: Update server-ee 2025-11-27 09:15:22 +08:00
Robin Shen
e2150b12fd fix: Validation error when update board spec via project settings api (OD-2610) 2025-11-27 08:46:14 +08:00
Robin Shen
9fdbf390b3 Merge branch 'main' of https://code.onedev.io/onedev/server 2025-11-22 11:34:37 +08:00
Robin Shen
1875200269 chore: Append .git to clone url for facilitate cloudflare rule config 2025-11-22 11:34:08 +08:00
Robin Shen
7b4e6ef9be
Edit incompatibilities.md 2025-11-22 00:40:27 +00:00
Robin Shen
b6970fb247 chore: Minor format change 2025-11-21 14:51:53 +08:00
Robin Shen
061d672774 Merge branch 'main' of https://code.onedev.io/onedev/server 2025-11-17 22:05:20 +08:00
Robin Shen
4220b1130f chore: Update translations 2025-11-17 22:05:12 +08:00
Robin Shen
3e753b6adc
编辑 incompatibilities.md 2025-11-17 13:43:10 +00:00
Robin Shen
682620965c chore: Refresh page when switch light/dark mode to avoid dropdown issue 2025-11-17 20:50:28 +08:00
Robin Shen
13d9281484 fix: Session timeout setting not respected (OD-2604) 2025-11-17 20:03:13 +08:00
Robin Shen
d54de0ec27 chore: Add missing translations 2025-11-17 17:30:27 +08:00
Robin Shen
c6a74dc008 feat: Use natural language for various queries (OD-2600) 2025-11-17 10:33:20 +08:00
Robin Shen
706b2dbdba feat: Suggest pull request title/description via AI (OD-2602) 2025-11-15 08:01:22 +08:00
Robin Shen
a62db8265f chore: Some refactorings 2025-11-13 22:30:33 +08:00
Robin Shen
1eb8bda97e feat: Accurate symbol navigation with help of AI (OD-2601) 2025-11-13 11:00:12 +08:00
Robin Shen
b465a1fd78 feat: Natural language query for issues/builds/pull requests via AI (OD-2600) 2025-11-11 13:38:00 +08:00
Robin Shen
2b9237b8b1 task: Update minimum required Java version to 17 (OD-2597) 2025-11-02 22:59:43 +08:00
Robin Shen
7e77167833 chore: Update EE module 2025-11-02 10:30:53 +08:00
Robin Shen
1157f4e8ba chore: Minor refactoring 2025-11-01 09:50:56 +08:00
Robin Shen
9104c8eb9a chore: Add missing translations 2025-10-31 23:26:58 +08:00
Robin Shen
cb86f2087e fix: NPE when set up server with session timeout property 2025-10-31 23:01:52 +08:00
Robin Shen
8a45bb03f7 feat: Able to run job in different project in post build action (OD-2596) 2025-10-31 18:02:17 +08:00
Robin Shen
3c44f81687 feat: Allow configurable idle session timeout (OD-2587) 2025-10-31 12:04:00 +08:00
Robin Shen
7400902735 feat: Improve branch/tag protection rule to disallow specific file extensions (OD-2588) 2025-10-31 10:36:21 +08:00
Robin Shen
5f5c114cdd chore: Allow spaces in review requirement criterias 2025-10-30 11:29:05 +08:00
Robin Shen
ecace0afe3 feat: Save uploaded avatar as png instead of jpg to avoid quality loss (OD-2594) 2025-10-30 10:37:54 +08:00
Robin Shen
c40ed5b998 fix: Conventional commits: number in scope not allowed. (OD-2593) 2025-10-30 08:09:01 +08:00
Robin Shen
573cf4ebf2 chore: Bump version 2025-10-18 17:09:55 +08:00
Robin Shen
64e83d995b fix: Publish clover coverage in CI: "Invalid input stream" (OD-2579) 2025-10-18 17:08:45 +08:00
Robin Shen
db28406eeb chore: Refactoring 2025-10-12 16:36:15 +08:00
Robin Shen
e0d5a6682f chore: Add missing translation key 2025-10-09 15:18:00 +08:00
Robin Shen
59315b3f81 chore: Usability improvements 2025-10-09 14:55:23 +08:00
Robin Shen
0a5860d153 chore: Package registry refactoring 2025-10-08 20:36:37 +08:00
Robin Shen
25e3991ba9 fix: Unable to delete obsolete project directories on Windows with Java 25 (OD-2574) 2025-10-08 16:28:58 +08:00
Robin Shen
618dbaf297 chore: Refactoring 2025-10-07 21:40:34 +08:00
Robin Shen
b8a4d7cdc2 chore: Bump version 2025-10-02 21:57:06 +08:00
Robin Shen
16a4178fef feat: Accept email as verified if verified_email claim is not present in OIDC provider (OD-2573) 2025-10-02 21:52:46 +08:00
Robin Shen
2f0311fe8a chore: Refactor exception classes 2025-10-01 15:11:42 +08:00
Robin Shen
963bdaa0fa chore: Bump version 2025-09-29 19:55:41 +08:00
Robin Shen
c0c8c585c1 fix: Unable to publish helm package (OD-2570) 2025-09-29 19:54:36 +08:00
Robin Shen
d8cdd0e327 update readme 2025-09-29 10:48:28 +08:00
Robin Shen
6b597c6c5b fix: Update Project Settings API endpoint does not have same validation as frontend, leading to NPE (OD-2568) 2025-09-29 10:16:23 +08:00
Robin Shen
a32ff3f759 fix: PullRepository step fails due to anonymous principal causing null to be returned by SecurityUtils#getUser (OD-2569) 2025-09-29 09:31:59 +08:00
Robin Shen
4fd8bc3bf9 chore: Bump version 2025-09-29 08:46:16 +08:00
Robin Shen
b84c56da53 chore: Various MCP improvements 2025-09-29 08:44:47 +08:00
Robin Shen
1ccdde6087 chore: Bump version 2025-09-28 10:21:10 +08:00
Robin Shen
0c85caedfa feat: Add endpoint to retrieve clone roots in McpHelperResource (OD-2567) 2025-09-28 10:17:12 +08:00
Robin Shen
7eee7de067 fix: Correct project retrieval in runJob method of McpHelperResource 2025-09-27 20:43:54 +08:00
Robin Shen
d0c7a68be9 fix: Update required fields in createPullRequest and issue handling 2025-09-27 10:11:00 +08:00
Robin Shen
b5c65ccb48 chore: Bump version 2025-09-26 21:36:08 +08:00
Robin Shen
e3b23bd5ba fix: .onedev-buildspec kaniko build validation issue (OD-2564) 2025-09-26 21:34:43 +08:00
Robin Shen
a68f9784b0 chore: Refactor step and executor matching 2025-09-26 12:44:31 +08:00
Robin Shen
ab13f659e3 chore: Add missing translations 2025-09-26 11:30:09 +08:00
Robin Shen
d3e227d591 chore: Remove unused callout CSS styles 2025-09-26 09:07:47 +08:00
Robin Shen
f6a2273471 feat: Use plain text description for project instead of markdown (OD-2563) 2025-09-26 08:20:44 +08:00
Robin Shen
aeb94bc272 feat: Support Markdown callouts (OD-2561) 2025-09-25 22:33:20 +08:00
Robin Shen
0defe8b62a fix: Hidden parameters cause build failure (OD-2560) 2025-09-25 20:53:34 +08:00
Robin Shen
a77172ef43 fix: Markdown editor labels are broken (OD-2559) 2025-09-25 20:03:26 +08:00
Robin Shen
6158adefe2 we 2025-09-25 15:30:34 +08:00
Robin Shen
3e16c2fe66 we 2025-09-25 15:10:13 +08:00
Robin Shen
207fcb65e8 chore: Bump version to 13.0.2 2025-09-24 20:32:12 +08:00
Robin Shen
77cf1da58c chore: Various bean validation improvements 2025-09-24 20:30:48 +08:00
Robin Shen
a6fb15d7a9 chore: Bump version 2025-09-20 07:55:34 +08:00
Robin Shen
e8acd1f16c fix: Failed to upgrade /opt/onedev 12.0.10 -> latest (OD-2557) 2025-09-20 07:54:18 +08:00
Robin Shen
aecc79fa11 feat: JDK 25 support (OD-2556) 2025-09-19 09:54:05 +08:00
Robin Shen
146633f2d0 chore: Improved issue defaults 2025-09-18 15:26:14 +08:00
Robin Shen
9b1db1c604 feat: Add user criteria for branch update trigger (OD-2554) 2025-09-17 16:01:43 +08:00
Robin Shen
3939404717 chore: Bump version to 13.0.0 2025-09-15 22:08:31 +08:00
Robin Shen
66a6315e37
feat: MCP tools for issue/build/pull request operations (OD-2552) 2025-09-15 14:02:15 +00:00
Robin Shen
6358860df2 chore: Requires tod 2.0.0 2025-09-15 21:57:07 +08:00
Robin Shen
8bb5dce066 chore: Generate build spec schema to aid AI 2025-09-15 14:25:18 +08:00
Robin Shen
2e187d3cc5 chore: Bump version 2025-09-08 20:56:53 +08:00
Robin Shen
53e91f8520 chore: Improve link mutiple issues UI 2025-09-08 20:55:48 +08:00
Robin Shen
f87ceb0d13 fix: Error when deleting a user (OD-2543) 2025-09-08 14:23:42 +08:00
Robin Shen
2cb6b6b7fa chore: Add MCP build support 2025-09-08 14:04:00 +08:00
Robin Shen
2a1a2a5a43 chore: Add missing translations 2025-09-06 07:59:42 +08:00
Robin Shen
67c4a5c334 chore: Bump dependencies 2025-09-05 23:31:40 +08:00
Robin Shen
af6cf4ad81 chore: Minor UI improvements 2025-09-05 20:12:41 +08:00
Robin Shen
81d14f1274 fix: Error when inserting variables into trivy cache step (OD-2546) 2025-09-05 11:03:52 +08:00
Robin Shen
18a69b8b25 chore: Refactoring input param of issues 2025-09-05 10:04:20 +08:00
Robin Shen
3f024b687b chore: Remove auto merge user field 2025-09-05 08:35:39 +08:00
Robin Shen
78b31191cc feat: Able to support SSO provider without email address (OD-2545) 2025-09-04 21:30:12 +08:00
Robin Shen
9c1373442b fix: Error when deleting a user (OD-2543) 2025-09-01 21:46:55 +08:00
Robin Shen
f717f5ecd8 chore: Make email claim required in OpenID connector 2025-09-01 11:31:44 +08:00
Robin Shen
75578c4c41 chore: Bump version to 12.0.9 2025-09-01 09:00:49 +08:00
Robin Shen
9f3ad0bc11 feat: Able to use conventional commit types configured in branch protection rule for PR title generation (OD-2540) 2025-08-31 20:06:34 +08:00
Robin Shen
1e477e2347 chore: Convert empty string to null for restful services to avoid potential errors 2025-08-31 17:38:08 +08:00
Robin Shen
5df04de721 chore: Refactoring for CI/CD MCP support 2025-08-31 14:45:26 +08:00
Robin Shen
4c626f4f88 chore: Bump version 2025-08-27 13:58:36 +08:00
Robin Shen
b19eedb4b4 feat: Better PR title and description generation (OD-2521) 2025-08-27 13:57:33 +08:00
Robin Shen
afddedc308 fix: Logic to check root incorrect in agent.sh running on Chinese version of Ubuntu (OD-2538) 2025-08-25 22:07:01 +08:00
Robin Shen
7b8e9f7cba fix: Auto filling hanging in Microsoft Edge (OD-2537) 2025-08-25 21:35:12 +08:00
Robin Shen
0004fd0ab6 chore: Minor UI improvements 2025-08-25 17:32:41 +08:00
Robin Shen
f8bd75fa16 MCP support for pull request operations 2025-08-25 16:11:11 +08:00
Robin Shen
f6c8d8ba7f chore: Bump version 2025-08-22 17:43:46 +08:00
Robin Shen
ccda03e517 fix: Exception when opening issues (OD-2533) 2025-08-22 17:42:14 +08:00
Robin Shen
845f20e53c fix: Unable to open issues changed in v12.0.5 2025-08-22 16:26:06 +08:00
Robin Shen
de59055c84 Fix various bugs
fix: Error when opening issue page (OD-2532)
fix: Error when opening user detail page (OD-2531)
2025-08-22 15:57:07 +08:00
Robin Shen
2479d74b13 wip: Add pull request MCP support 2025-08-22 15:16:49 +08:00
Robin Shen
63fe24a8f4 chore: Bump version 2025-08-21 14:37:29 +08:00
Robin Shen
b4ad529e68 feat: Able to check commit message in issue transition rule (OD-2522) 2025-08-20 21:21:03 +08:00
Robin Shen
d9f9fa03e6 chore: Add MCP support for issue operations 2025-08-20 17:16:37 +08:00
Robin Shen
dc4af70ff1 chore: Bump version 2025-08-06 14:11:42 +08:00
Robin Shen
ada4da245b feat: Defer caching scheduled jobs to improve startup speed (OD-2514) 2025-08-06 14:10:30 +08:00
Robin Shen
e4143f75b1 feat: Able to set build description via RESTful api (OD-2513) 2025-08-06 07:38:47 +08:00
Robin Shen
116231f8a5 chore: Remove MCP servlet 2025-08-06 07:17:22 +08:00
Robin Shen
f60d2db271 chore: Bump version to 12.0.3 2025-08-02 15:53:07 +08:00
Robin Shen
249d1784e7 feat: WiP handling for pull request (OD-2507) 2025-08-02 15:51:46 +08:00
Robin Shen
7b0defc133 feat: Able to control JVM thread stack size and Hibernate query plan cache size via environment variables (OD-2510) 2025-08-02 09:48:22 +08:00
Robin Shen
27c0204bb0 chore: Refactoring server side step 2025-08-01 17:09:48 +08:00
Robin Shen
1347345d61 feat: Hide the Jetty version in the http response header (OD-2508) 2025-08-01 14:25:19 +08:00
Robin Shen
b46dfb8637 feat: Improve startup speed when there are many projects (OD-2509) 2025-08-01 12:56:16 +08:00
Robin Shen
af58b47205 Merge branch 'mcp' 2025-07-28 11:17:37 +08:00
Robin Shen
4c0f2c96db chore: Add example MCP servlet 2025-07-28 11:16:58 +08:00
Robin Shen
c01570f4c0 Merge branch 'mcp' 2025-07-28 11:16:06 +08:00
Robin Shen
634279171b midwork 2025-07-27 21:16:21 +08:00
Robin Shen
593db97cd3 midwork 2025-07-26 12:13:19 +08:00
1937 changed files with 32531 additions and 21318 deletions

3
.gitignore vendored
View File

@ -1,6 +1,7 @@
server-product/docker/build/
server-product/docker/onedev-*/
**/target/
target/
bin/
**/.classpath
**/.gitignore
**/.settings

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "server-ee"]
path = server-ee
url = https://code.onedev.io/onedev/server-ee
url = https://code.onedev.io/onedev/server-ee.git

View File

@ -1,2 +1,2 @@
CVE-2024-53299
CVE-2024-57699
CVE-2024-57699

175
CLAUDE.md Normal file
View File

@ -0,0 +1,175 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Build Commands
OneDev uses Maven as its build system with a multi-module structure.
### Essential Commands
- **Build the project**: `mvn clean compile`
- **Run tests**: `mvn test`
- **Package the application**: `mvn clean package`
- **Build without tests**: `mvn clean package -DskipTests`
- **Build specific module**: `mvn clean package -pl server-core`
- **Install to local repository**: `mvn clean install`
### Profiles
- **Community Edition**: `mvn clean package -Pce` (excludes enterprise features)
- **Default/Enterprise**: `mvn clean package` (includes all features)
### Testing
- **Run all tests**: `mvn test`
- **Run specific test class**: `mvn test -Dtest=ClassName`
- **Run tests for specific module**: `mvn test -pl server-core`
## Architecture Overview
OneDev is a comprehensive DevOps platform built with a sophisticated multi-module Maven architecture:
### Core Technology Stack
- **Web Framework**: Apache Wicket 7.18.0 (component-based UI)
- **REST API**: Jersey 2.38 (JAX-RS implementation)
- **Database/ORM**: Hibernate 5.4.24.Final with HikariCP connection pooling
- **Web Server**: Embedded Jetty 9.4.57
- **Dependency Injection**: Google Guice with custom plugin loading
- **Security**: Apache Shiro for authentication/authorization
- **Search**: Apache Lucene 8.7.0
- **Git**: JGit 5.13.3 for Git operations
- **Clustering**: Hazelcast 5.3.5 for distributed coordination
### Module Structure
- **server-core**: Core application logic, entities, and services
- **server-ee**: Enterprise edition features
- **server-plugin**: Plugin framework and all plugin implementations
- **server-product**: Final packaging and deployment artifacts
### Key Subsystems
#### 1. Application Bootstrap
- Main entry point: `server-core/src/main/java/io/onedev/server/OneDev.java`
- Module configuration: `server-core/src/main/java/io/onedev/server/CoreModule.java`
- Handles server lifecycle, clustering, and graceful shutdown
#### 2. Entity Management
Key domain entities and managers in `server-core/src/main/java/io/onedev/server/model/`:
- Project, User, Group, Role management
- Issue tracking with customizable workflows
- Pull request lifecycle and code review
- Build and CI/CD pipeline management
- Package registry operations
#### 3. Git Integration
- Full Git repository management via JGit
- Git hooks for policy enforcement in `server-core/src/main/java/io/onedev/server/git/`
- Code browsing, diff visualization, and blame tracking
- SSH server for Git operations
#### 4. Web Layer (Wicket)
- Component-based UI in `server-core/src/main/java/io/onedev/server/web/`
- AJAX-heavy interface with WebSocket support
- Project browsing, issue boards, pull request review interface
#### 5. REST API (Jersey)
- RESTful services in `server-core/src/main/java/io/onedev/server/rest/`
- Project, User, Build, Issue resources
- WebHook endpoints and package registry APIs
#### 6. CI/CD System
- YAML-based build specifications
- Multi-executor support (Kubernetes, Docker, Shell)
- Real-time log streaming and artifact management
#### 7. Plugin Architecture
- Extensible plugin system in `server-plugin/`
- Categories: build specs, executors, authenticators, importers, notifications, package registries, report processors
- Plugin contributions via Guice modules
## Development Patterns
### Code Organization
- **Package-by-feature**: Organized around business capabilities
- **Dependency Injection**: Guice-based DI throughout the application
- **Interface-based design**: For testability and modularity
- **Custom annotations**: Extensive use for validation and metadata
### Design Patterns Used
- Repository Pattern for data access
- Observer Pattern for event handling
- Command Pattern for Git operations
- Strategy Pattern for pluggable components
- Template Method for build processing
### Testing Strategy
- Unit tests in `src/test/java` directories
- Git operation tests with test repositories
- Component and integration tests
- Utility method tests
- Focus on testing business logic and Git operations
## Common Development Tasks
### Working with Entities
- Entities are in `server-core/src/main/java/io/onedev/server/model/`
- Use corresponding managers for database operations
- Follow JPA/Hibernate patterns for persistence
### Adding REST Endpoints
- Create resources in `server-core/src/main/java/io/onedev/server/rest/resource/`
- Follow Jersey/JAX-RS patterns
- Use existing security annotations for authentication
### Creating Plugins
- Extend `AbstractPlugin` class
- Implement appropriate interfaces for the plugin category
- Add Guice module configuration
- Place in appropriate `server-plugin/server-plugin-*` module
### Working with Git
- Use JGit APIs through OneDev's Git service layer
- Follow patterns in `server-core/src/main/java/io/onedev/server/git/`
- Handle Git operations asynchronously when possible
### Adding Web Components
- Create Wicket components in `server-core/src/main/java/io/onedev/server/web/`
- Follow existing component patterns and CSS frameworks
- Use AJAX for dynamic behavior
## Configuration and Deployment
### Key Configuration Files
- `server-product/system/conf/server.properties`: HTTP/SSH ports, clustering
- `server-product/system/conf/hibernate.properties`: Database configuration
- `server-product/system/conf/logback.xml`: Logging configuration
### Deployment Options
- Standalone JAR with embedded Jetty
- Docker containers (see `server-product/docker/`)
- Kubernetes via Helm charts (see `server-product/helm/`)
### Database Support
- PostgreSQL (recommended for production)
- MySQL/MariaDB
- HSQLDB (development/testing)
## Performance Considerations
### Caching
- Hibernate second-level cache with Hazelcast
- Build artifact caching
- Git object caching
- Web resource bundling
### Clustering
- Hazelcast-based clustering for high availability
- Distributed session management
- Leader election for coordinated operations
## Important Notes
- OneDev uses a custom plugin loading framework
- Git operations are central to the application architecture
- The system supports both community (CE) and enterprise (EE) editions
- Extensive use of Guice for dependency injection and plugin management
- Focus on performance and resource efficiency
- Battle-tested in production environments for 5+ years

7
development.md Normal file
View File

@ -0,0 +1,7 @@
# Conventions
## Service Methods
1. Various service methods will not check permissions, except for those with subject param
1. User param of service methods are used to indicate who performs the action, instead of requiring permission check
1. Service methods will not audit changes, unless stated explicitly

BIN
doc/images/mcp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

View File

@ -43,4 +43,5 @@ OD-2248
OD-2257
OD-2358
OD-2403
OD-2467
OD-2467
OD-2552

34
pom.xml
View File

@ -6,10 +6,10 @@
<parent>
<groupId>io.onedev</groupId>
<artifactId>parent</artifactId>
<version>1.2.3</version>
<version>1.3.0</version>
</parent>
<artifactId>server</artifactId>
<version>12.0.2</version>
<version>13.1.3</version>
<packaging>pom</packaging>
<build>
<finalName>${project.groupId}.${project.artifactId}-${project.version}</finalName>
@ -38,7 +38,7 @@
<plugin>
<groupId>io.onedev</groupId>
<artifactId>plugin-maven</artifactId>
<version>2.6.9</version>
<version>2.7.0</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
@ -227,6 +227,11 @@
<artifactId>wicket-native-websocket-core</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-guice</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
@ -275,7 +280,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>11.9.1</version>
<version>11.28</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
@ -315,7 +320,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.1-jre</version>
<version>33.5.0-jre</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
@ -622,6 +627,16 @@
<artifactId>fastexcel</artifactId>
<version>0.15.7</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
@ -641,8 +656,8 @@
</repository>
</repositories>
<properties>
<commons.version>3.0.13</commons.version>
<agent.version>2.2.17</agent.version>
<commons.version>3.1.1</commons.version>
<agent.version>2.3.3</agent.version>
<slf4j.version>2.0.9</slf4j.version>
<logback.version>1.4.14</logback.version>
<antlr.version>4.7.2</antlr.version>
@ -654,9 +669,10 @@
<shiro.version>1.13.0</shiro.version>
<jgit.version>5.13.3.202401111512-r</jgit.version>
<flexmark.version>0.64.8</flexmark.version>
<groovy.version>3.0.19</groovy.version>
<groovy.version>3.0.25</groovy.version>
<servlet.version>3.1.0</servlet.version>
<jackson.version>2.15.0</jackson.version>
<tika.version>1.28.3</tika.version>
<tika.version>3.2.2</tika.version>
<langchain4j.version>1.8.0</langchain4j.version>
</properties>
</project>

View File

@ -148,6 +148,14 @@ query to get notified of interesting events.
![issue query](./doc/images/issue-query.gif)
## 🤖 MCP server to interact with OneDev via AI agents
MCP server for managing issues, pull requests, and builds. Streamline DevOps workflows, configure CI/CD jobs,
and investigate build failures through conversations.
[**Tutorial**](https://docs.onedev.io/tutorials/misc/working-with-mcp)
![mcp](./doc/images/mcp.png)
## 🎛️ Dashboard for teams and users
Arrange gadgets in custom dashboard to get important information

View File

@ -7,7 +7,7 @@
<parent>
<groupId>io.onedev</groupId>
<artifactId>server</artifactId>
<version>12.0.2</version>
<version>13.1.3</version>
</parent>
<build>
<plugins>
@ -274,6 +274,10 @@
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-native-websocket-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-guice</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
@ -371,20 +375,18 @@
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.2</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk7</artifactId>
<version>${kotlin.version}</version>
</dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
</dependencies>
<properties>
<kotlin.version>1.9.23</kotlin.version>

View File

@ -0,0 +1,327 @@
package dev.langchain4j.internal;
import java.util.Random;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.langchain4j.Internal;
import dev.langchain4j.exception.LangChain4jException;
import dev.langchain4j.exception.NonRetriableException;
import io.onedev.server.exception.ExceptionUtils;
/**
* Utility class for retrying actions.
*/
@Internal
public final class RetryUtils {
private static final Random RANDOM = new Random();
private RetryUtils() {}
private static final Logger log = LoggerFactory.getLogger(RetryUtils.class);
/**
* This method returns a RetryPolicy.Builder.
*
* @return A RetryPolicy.Builder.
*/
public static RetryPolicy.Builder retryPolicyBuilder() {
return new RetryPolicy.Builder();
}
/**
* This class encapsulates a retry policy.
*/
public static final class RetryPolicy {
/**
* This class encapsulates a retry policy builder.
*/
public static final class Builder {
private int maxRetries = 2;
private int delayMillis = 1000;
private double jitterScale = 0.2;
private double backoffExp = 1.5;
/**
* Construct a RetryPolicy.Builder.
*/
public Builder() {}
/**
* Sets the default maximum number of retries.
*
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @return {@code this}
*/
public Builder maxRetries(int maxRetries) {
this.maxRetries = maxRetries;
return this;
}
/**
* Sets the base delay in milliseconds.
*
* <p>The delay is calculated as follows:
* <ol>
* <li>Calculate the raw delay in milliseconds as
* {@code delayMillis * Math.pow(backoffExp, retry)}.</li>
* <li>Calculate the jitter delay in milliseconds as
* {@code rawDelayMs + rand.nextInt((int) (rawDelayMs * jitterScale))}.</li>
* <li>Sleep for the jitter delay in milliseconds.</li>
* </ol>
*
* @param delayMillis The delay in milliseconds.
* @return {@code this}
*/
public Builder delayMillis(int delayMillis) {
this.delayMillis = delayMillis;
return this;
}
/**
* Sets the jitter scale.
*
* <p>The jitter delay in milliseconds is calculated as
* {@code rawDelayMs + rand.nextInt((int) (rawDelayMs * jitterScale))}.
*
* @param jitterScale The jitter scale.
* @return {@code this}
*/
public Builder jitterScale(double jitterScale) {
this.jitterScale = jitterScale;
return this;
}
/**
* Sets the backoff exponent.
*
* @param backoffExp The backoff exponent.
* @return {@code this}
*/
public Builder backoffExp(double backoffExp) {
this.backoffExp = backoffExp;
return this;
}
/**
* Builds a RetryPolicy.
*
* @return A RetryPolicy.
*/
public RetryPolicy build() {
return new RetryPolicy(maxRetries, delayMillis, jitterScale, backoffExp);
}
}
private final int maxRetries;
private final int delayMillis;
private final double jitterScale;
private final double backoffExp;
/**
* Construct a RetryPolicy.
*
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @param delayMillis The delay in milliseconds.
* @param jitterScale The jitter scale.
* @param backoffExp The backoff exponent.
*/
public RetryPolicy(int maxRetries, int delayMillis, double jitterScale, double backoffExp) {
this.maxRetries = maxRetries;
this.delayMillis = delayMillis;
this.jitterScale = jitterScale;
this.backoffExp = backoffExp;
}
/**
* This method returns the raw delay in milliseconds after a given retry.
*
* @param retry The retry number.
* @return The raw delay in milliseconds.
*/
public double rawDelayMs(int retry) {
return delayMillis * Math.pow(backoffExp, retry);
}
/**
* This method returns the jitter delay in milliseconds after a given retry.
*
* @param retry The retry number.
* @return The jitter delay in milliseconds.
*/
public int jitterDelayMillis(int retry) {
double delay = rawDelayMs(retry);
double jitter = delay * jitterScale;
return (int) (delay + RANDOM.nextInt((int) jitter));
}
/**
* This method sleeps after a given retry.
*
* @param retry The retry number.
*/
@JacocoIgnoreCoverageGenerated
public void sleep(int retry) {
try {
Thread.sleep(jitterDelayMillis(retry));
} catch (InterruptedException ignored) {
// pass
}
}
/**
* This method attempts to execute a given action up to 3 times with an exponential backoff.
* If the action fails on all attempts, it throws a RuntimeException.
*
* @param action The action to be executed.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public <T> T withRetry(Callable<T> action) {
return withRetry(action, maxRetries);
}
/**
* This method attempts to execute a given action up to a specified number of times with an exponential backoff.
* If the action fails on all attempts, it throws a RuntimeException.
*
* @param action The action to be executed.
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public <T> T withRetry(Callable<T> action, int maxRetries) {
int retry = 0;
while (true) {
try {
return action.call();
} catch (NonRetriableException e) {
throw e;
} catch (Exception e) {
if (retry >= maxRetries || ExceptionUtils.find(e, InterruptedException.class) != null) {
throw e instanceof RuntimeException re ? re : new LangChain4jException(e);
}
log.warn(
"A retriable exception occurred. Remaining retries: %s of %s"
.formatted(maxRetries - retry, maxRetries),
e);
sleep(retry);
}
retry++;
}
}
}
/**
* Default retry policy used by {@link #withRetry(Callable)}.
*/
public static final RetryPolicy DEFAULT_RETRY_POLICY = retryPolicyBuilder()
.maxRetries(2)
.delayMillis(500)
.jitterScale(0.2)
.backoffExp(1.5)
.build();
/**
* This method attempts to execute a given action up to 3 times with an exponential backoff.
* If the action fails on all attempts, it throws a RuntimeException.
*
* @param action The action to be executed.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public static <T> T withRetry(Callable<T> action) {
return DEFAULT_RETRY_POLICY.withRetry(action);
}
/**
* This method attempts to execute a given action up to a specified number of times with an exponential backoff.
* If the action fails on all attempts, it throws a RuntimeException.
*
* @param action The action to be executed.
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public static <T> T withRetry(Callable<T> action, int maxRetries) {
return DEFAULT_RETRY_POLICY.withRetry(action, maxRetries);
}
/**
* This method attempts to execute a given action up to a specified number of times with an exponential backoff.
* If the action fails on all attempts, it throws a RuntimeException.
*
* @param action The action to be executed.
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @throws RuntimeException if the action fails on all attempts.
*/
public static void withRetry(Runnable action, int maxRetries) {
DEFAULT_RETRY_POLICY.withRetry(
() -> {
action.run();
return null;
},
maxRetries);
}
/**
* This method attempts to execute a given action up to 3 times with an exponential backoff.
* If the action fails, the Exception causing the failure will be mapped with the default {@link ExceptionMapper}.
*
* @param action The action to be executed.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public static <T> T withRetryMappingExceptions(Callable<T> action) {
return withRetry(() -> ExceptionMapper.DEFAULT.withExceptionMapper(action));
}
/**
* This method attempts to execute a given action up to a specified number of times with an exponential backoff.
* If the action fails, the Exception causing the failure will be mapped with the default {@link ExceptionMapper}.
*
* @param action The action to be executed.
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public static <T> T withRetryMappingExceptions(Callable<T> action, int maxRetries) {
return withRetryMappingExceptions(action, maxRetries, ExceptionMapper.DEFAULT);
}
/**
* This method attempts to execute a given action up to a specified number of times with an exponential backoff.
* If the action fails, the Exception causing the failure will be mapped with the provided {@link ExceptionMapper}.
*
* @param action The action to be executed.
* @param maxRetries The maximum number of retries.
* The action can be executed up to {@code maxRetries + 1} times.
* @param exceptionMapper The ExceptionMapper used to translate the exception that caused the failure of the action invocation.
* @param <T> The type of the result of the action.
* @return The result of the action if it is successful.
* @throws RuntimeException if the action fails on all attempts.
*/
public static <T> T withRetryMappingExceptions(
Callable<T> action, int maxRetries, ExceptionMapper exceptionMapper) {
return withRetry(() -> exceptionMapper.withExceptionMapper(action), maxRetries);
}
}

View File

@ -3,6 +3,7 @@ package io.onedev.server;
import static com.google.common.collect.Lists.newArrayList;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@ -23,6 +24,9 @@ import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.validation.Configuration;
import javax.validation.Path;
import javax.validation.Path.Node;
import javax.validation.TraversableResolver;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@ -71,8 +75,11 @@ import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.commons.utils.StringUtils;
import io.onedev.k8shelper.KubernetesHelper;
import io.onedev.k8shelper.OsInfo;
import io.onedev.server.attachment.AttachmentManager;
import io.onedev.server.attachment.DefaultAttachmentManager;
import io.onedev.server.ai.McpHelperResource;
import io.onedev.server.annotation.Shallow;
import io.onedev.server.attachment.AttachmentService;
import io.onedev.server.attachment.DefaultAttachmentService;
import io.onedev.server.buildspec.BuildSpecSchemaResource;
import io.onedev.server.buildspec.job.log.instruction.LogInstruction;
import io.onedev.server.cluster.ClusterResource;
import io.onedev.server.codequality.CodeProblemContribution;
@ -85,182 +92,10 @@ import io.onedev.server.commandhandler.ResetAdminPassword;
import io.onedev.server.commandhandler.RestoreDatabase;
import io.onedev.server.commandhandler.Translate;
import io.onedev.server.commandhandler.Upgrade;
import io.onedev.server.data.DataManager;
import io.onedev.server.data.DefaultDataManager;
import io.onedev.server.entitymanager.AccessTokenAuthorizationManager;
import io.onedev.server.entitymanager.AccessTokenManager;
import io.onedev.server.entitymanager.AgentAttributeManager;
import io.onedev.server.entitymanager.AgentLastUsedDateManager;
import io.onedev.server.entitymanager.AgentManager;
import io.onedev.server.entitymanager.AgentTokenManager;
import io.onedev.server.entitymanager.AlertManager;
import io.onedev.server.entitymanager.BaseAuthorizationManager;
import io.onedev.server.entitymanager.BuildDependenceManager;
import io.onedev.server.entitymanager.BuildLabelManager;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.BuildMetricManager;
import io.onedev.server.entitymanager.BuildParamManager;
import io.onedev.server.entitymanager.BuildQueryPersonalizationManager;
import io.onedev.server.entitymanager.CodeCommentManager;
import io.onedev.server.entitymanager.CodeCommentMentionManager;
import io.onedev.server.entitymanager.CodeCommentQueryPersonalizationManager;
import io.onedev.server.entitymanager.CodeCommentReplyManager;
import io.onedev.server.entitymanager.CodeCommentStatusChangeManager;
import io.onedev.server.entitymanager.CodeCommentTouchManager;
import io.onedev.server.entitymanager.CommitQueryPersonalizationManager;
import io.onedev.server.entitymanager.DashboardGroupShareManager;
import io.onedev.server.entitymanager.DashboardManager;
import io.onedev.server.entitymanager.DashboardUserShareManager;
import io.onedev.server.entitymanager.DashboardVisitManager;
import io.onedev.server.entitymanager.EmailAddressManager;
import io.onedev.server.entitymanager.GitLfsLockManager;
import io.onedev.server.entitymanager.GpgKeyManager;
import io.onedev.server.entitymanager.GroupAuthorizationManager;
import io.onedev.server.entitymanager.GroupManager;
import io.onedev.server.entitymanager.IssueAuthorizationManager;
import io.onedev.server.entitymanager.IssueChangeManager;
import io.onedev.server.entitymanager.IssueCommentManager;
import io.onedev.server.entitymanager.IssueCommentReactionManager;
import io.onedev.server.entitymanager.IssueCommentRevisionManager;
import io.onedev.server.entitymanager.IssueDescriptionRevisionManager;
import io.onedev.server.entitymanager.IssueFieldManager;
import io.onedev.server.entitymanager.IssueLinkManager;
import io.onedev.server.entitymanager.IssueManager;
import io.onedev.server.entitymanager.IssueMentionManager;
import io.onedev.server.entitymanager.IssueQueryPersonalizationManager;
import io.onedev.server.entitymanager.IssueReactionManager;
import io.onedev.server.entitymanager.IssueScheduleManager;
import io.onedev.server.entitymanager.IssueStateHistoryManager;
import io.onedev.server.entitymanager.IssueTouchManager;
import io.onedev.server.entitymanager.IssueVoteManager;
import io.onedev.server.entitymanager.IssueWatchManager;
import io.onedev.server.entitymanager.IssueWorkManager;
import io.onedev.server.entitymanager.IterationManager;
import io.onedev.server.entitymanager.JobCacheManager;
import io.onedev.server.entitymanager.LabelSpecManager;
import io.onedev.server.entitymanager.LinkAuthorizationManager;
import io.onedev.server.entitymanager.LinkSpecManager;
import io.onedev.server.entitymanager.MembershipManager;
import io.onedev.server.entitymanager.PackBlobManager;
import io.onedev.server.entitymanager.PackBlobReferenceManager;
import io.onedev.server.entitymanager.PackLabelManager;
import io.onedev.server.entitymanager.PackManager;
import io.onedev.server.entitymanager.PackQueryPersonalizationManager;
import io.onedev.server.entitymanager.PendingSuggestionApplyManager;
import io.onedev.server.entitymanager.ProjectLabelManager;
import io.onedev.server.entitymanager.ProjectLastEventDateManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.PullRequestAssignmentManager;
import io.onedev.server.entitymanager.PullRequestChangeManager;
import io.onedev.server.entitymanager.PullRequestCommentManager;
import io.onedev.server.entitymanager.PullRequestCommentReactionManager;
import io.onedev.server.entitymanager.PullRequestCommentRevisionManager;
import io.onedev.server.entitymanager.PullRequestDescriptionRevisionManager;
import io.onedev.server.entitymanager.PullRequestLabelManager;
import io.onedev.server.entitymanager.PullRequestManager;
import io.onedev.server.entitymanager.PullRequestMentionManager;
import io.onedev.server.entitymanager.PullRequestQueryPersonalizationManager;
import io.onedev.server.entitymanager.PullRequestReactionManager;
import io.onedev.server.entitymanager.PullRequestReviewManager;
import io.onedev.server.entitymanager.PullRequestTouchManager;
import io.onedev.server.entitymanager.PullRequestUpdateManager;
import io.onedev.server.entitymanager.PullRequestWatchManager;
import io.onedev.server.entitymanager.ReviewedDiffManager;
import io.onedev.server.entitymanager.RoleManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.entitymanager.SshKeyManager;
import io.onedev.server.entitymanager.StopwatchManager;
import io.onedev.server.entitymanager.UserAuthorizationManager;
import io.onedev.server.entitymanager.UserInvitationManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.entitymanager.impl.DefaultAccessTokenAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultAccessTokenManager;
import io.onedev.server.entitymanager.impl.DefaultAgentAttributeManager;
import io.onedev.server.entitymanager.impl.DefaultAgentLastUsedDateManager;
import io.onedev.server.entitymanager.impl.DefaultAgentManager;
import io.onedev.server.entitymanager.impl.DefaultAgentTokenManager;
import io.onedev.server.entitymanager.impl.DefaultAlertManager;
import io.onedev.server.entitymanager.impl.DefaultBaseAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultBuildDependenceManager;
import io.onedev.server.entitymanager.impl.DefaultBuildLabelManager;
import io.onedev.server.entitymanager.impl.DefaultBuildManager;
import io.onedev.server.entitymanager.impl.DefaultBuildMetricManager;
import io.onedev.server.entitymanager.impl.DefaultBuildParamManager;
import io.onedev.server.entitymanager.impl.DefaultBuildQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentMentionManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentReplyManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentStatusChangeManager;
import io.onedev.server.entitymanager.impl.DefaultCodeCommentTouchManager;
import io.onedev.server.entitymanager.impl.DefaultCommitQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultDashboardGroupShareManager;
import io.onedev.server.entitymanager.impl.DefaultDashboardManager;
import io.onedev.server.entitymanager.impl.DefaultDashboardUserShareManager;
import io.onedev.server.entitymanager.impl.DefaultDashboardVisitManager;
import io.onedev.server.entitymanager.impl.DefaultEmailAddressManager;
import io.onedev.server.entitymanager.impl.DefaultGitLfsLockManager;
import io.onedev.server.entitymanager.impl.DefaultGpgKeyManager;
import io.onedev.server.entitymanager.impl.DefaultGroupAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultGroupManager;
import io.onedev.server.entitymanager.impl.DefaultIssueAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultIssueChangeManager;
import io.onedev.server.entitymanager.impl.DefaultIssueCommentManager;
import io.onedev.server.entitymanager.impl.DefaultIssueCommentReactionManager;
import io.onedev.server.entitymanager.impl.DefaultIssueCommentRevisionManager;
import io.onedev.server.entitymanager.impl.DefaultIssueDescriptionRevisionManager;
import io.onedev.server.entitymanager.impl.DefaultIssueFieldManager;
import io.onedev.server.entitymanager.impl.DefaultIssueLinkManager;
import io.onedev.server.entitymanager.impl.DefaultIssueManager;
import io.onedev.server.entitymanager.impl.DefaultIssueMentionManager;
import io.onedev.server.entitymanager.impl.DefaultIssueQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultIssueReactionManager;
import io.onedev.server.entitymanager.impl.DefaultIssueScheduleManager;
import io.onedev.server.entitymanager.impl.DefaultIssueStateHistoryManager;
import io.onedev.server.entitymanager.impl.DefaultIssueTouchManager;
import io.onedev.server.entitymanager.impl.DefaultIssueVoteManager;
import io.onedev.server.entitymanager.impl.DefaultIssueWatchManager;
import io.onedev.server.entitymanager.impl.DefaultIssueWorkManager;
import io.onedev.server.entitymanager.impl.DefaultIterationManager;
import io.onedev.server.entitymanager.impl.DefaultJobCacheManager;
import io.onedev.server.entitymanager.impl.DefaultLabelSpecManager;
import io.onedev.server.entitymanager.impl.DefaultLinkAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultLinkSpecManager;
import io.onedev.server.entitymanager.impl.DefaultMembershipManager;
import io.onedev.server.entitymanager.impl.DefaultPackBlobManager;
import io.onedev.server.entitymanager.impl.DefaultPackBlobReferenceManager;
import io.onedev.server.entitymanager.impl.DefaultPackLabelManager;
import io.onedev.server.entitymanager.impl.DefaultPackManager;
import io.onedev.server.entitymanager.impl.DefaultPackQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultPendingSuggestionApplyManager;
import io.onedev.server.entitymanager.impl.DefaultProjectLabelManager;
import io.onedev.server.entitymanager.impl.DefaultProjectLastEventDateManager;
import io.onedev.server.entitymanager.impl.DefaultProjectManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestAssignmentManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestChangeManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestCommentManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestCommentReactionManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestCommentRevisionManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestDescriptionRevisionManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestLabelManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestMentionManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestQueryPersonalizationManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestReactionManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestReviewManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestTouchManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestUpdateManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestWatchManager;
import io.onedev.server.entitymanager.impl.DefaultReviewedDiffManager;
import io.onedev.server.entitymanager.impl.DefaultRoleManager;
import io.onedev.server.entitymanager.impl.DefaultSettingManager;
import io.onedev.server.entitymanager.impl.DefaultSshKeyManager;
import io.onedev.server.entitymanager.impl.DefaultStopwatchManager;
import io.onedev.server.entitymanager.impl.DefaultUserAuthorizationManager;
import io.onedev.server.entitymanager.impl.DefaultUserInvitationManager;
import io.onedev.server.entitymanager.impl.DefaultUserManager;
import io.onedev.server.entityreference.DefaultReferenceChangeManager;
import io.onedev.server.entityreference.ReferenceChangeManager;
import io.onedev.server.data.DataService;
import io.onedev.server.data.DefaultDataService;
import io.onedev.server.entityreference.DefaultReferenceChangeService;
import io.onedev.server.entityreference.ReferenceChangeService;
import io.onedev.server.event.DefaultListenerRegistry;
import io.onedev.server.event.ListenerRegistry;
import io.onedev.server.exception.handler.ExceptionHandler;
@ -275,23 +110,23 @@ import io.onedev.server.git.hook.GitPreReceiveChecker;
import io.onedev.server.git.location.GitLocation;
import io.onedev.server.git.service.DefaultGitService;
import io.onedev.server.git.service.GitService;
import io.onedev.server.git.signatureverification.DefaultSignatureVerificationManager;
import io.onedev.server.git.signatureverification.SignatureVerificationManager;
import io.onedev.server.git.signatureverification.DefaultSignatureVerificationService;
import io.onedev.server.git.signatureverification.SignatureVerificationService;
import io.onedev.server.git.signatureverification.SignatureVerifier;
import io.onedev.server.jetty.DefaultJettyManager;
import io.onedev.server.jetty.DefaultJettyService;
import io.onedev.server.jetty.DefaultSessionDataStoreFactory;
import io.onedev.server.jetty.JettyManager;
import io.onedev.server.job.DefaultJobManager;
import io.onedev.server.jetty.JettyService;
import io.onedev.server.job.DefaultJobService;
import io.onedev.server.job.DefaultResourceAllocator;
import io.onedev.server.job.JobManager;
import io.onedev.server.job.JobService;
import io.onedev.server.job.ResourceAllocator;
import io.onedev.server.job.log.DefaultLogManager;
import io.onedev.server.job.log.LogManager;
import io.onedev.server.mail.DefaultMailManager;
import io.onedev.server.mail.MailManager;
import io.onedev.server.markdown.DefaultMarkdownManager;
import io.onedev.server.job.log.DefaultLogService;
import io.onedev.server.job.log.LogService;
import io.onedev.server.mail.DefaultMailService;
import io.onedev.server.mail.MailService;
import io.onedev.server.markdown.DefaultMarkdownService;
import io.onedev.server.markdown.HtmlProcessor;
import io.onedev.server.markdown.MarkdownManager;
import io.onedev.server.markdown.MarkdownService;
import io.onedev.server.model.support.administration.GroovyScript;
import io.onedev.server.model.support.administration.authenticator.Authenticator;
import io.onedev.server.notification.BuildNotificationManager;
@ -302,21 +137,21 @@ import io.onedev.server.notification.PackNotificationManager;
import io.onedev.server.notification.PullRequestNotificationManager;
import io.onedev.server.notification.WebHookManager;
import io.onedev.server.pack.PackFilter;
import io.onedev.server.persistence.DefaultIdManager;
import io.onedev.server.persistence.DefaultSessionFactoryManager;
import io.onedev.server.persistence.DefaultSessionManager;
import io.onedev.server.persistence.DefaultTransactionManager;
import io.onedev.server.persistence.DefaultIdService;
import io.onedev.server.persistence.DefaultSessionFactoryService;
import io.onedev.server.persistence.DefaultSessionService;
import io.onedev.server.persistence.DefaultTransactionService;
import io.onedev.server.persistence.HibernateInterceptor;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.IdService;
import io.onedev.server.persistence.PersistListener;
import io.onedev.server.persistence.PrefixedNamingStrategy;
import io.onedev.server.persistence.SessionFactoryManager;
import io.onedev.server.persistence.SessionFactoryProvider;
import io.onedev.server.persistence.SessionFactoryService;
import io.onedev.server.persistence.SessionInterceptor;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.SessionProvider;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.persistence.TransactionInterceptor;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.TransactionService;
import io.onedev.server.persistence.annotation.Sessional;
import io.onedev.server.persistence.annotation.Transactional;
import io.onedev.server.persistence.dao.Dao;
@ -327,16 +162,16 @@ import io.onedev.server.rest.JerseyConfigurator;
import io.onedev.server.rest.ResourceConfigProvider;
import io.onedev.server.rest.WebApplicationExceptionHandler;
import io.onedev.server.rest.resource.ProjectResource;
import io.onedev.server.search.code.CodeIndexManager;
import io.onedev.server.search.code.CodeSearchManager;
import io.onedev.server.search.code.DefaultCodeIndexManager;
import io.onedev.server.search.code.DefaultCodeSearchManager;
import io.onedev.server.search.entitytext.CodeCommentTextManager;
import io.onedev.server.search.entitytext.DefaultCodeCommentTextManager;
import io.onedev.server.search.entitytext.DefaultIssueTextManager;
import io.onedev.server.search.entitytext.DefaultPullRequestTextManager;
import io.onedev.server.search.entitytext.IssueTextManager;
import io.onedev.server.search.entitytext.PullRequestTextManager;
import io.onedev.server.search.code.CodeIndexService;
import io.onedev.server.search.code.CodeSearchService;
import io.onedev.server.search.code.DefaultCodeIndexService;
import io.onedev.server.search.code.DefaultCodeSearchService;
import io.onedev.server.search.entitytext.CodeCommentTextService;
import io.onedev.server.search.entitytext.DefaultCodeCommentTextService;
import io.onedev.server.search.entitytext.DefaultIssueTextService;
import io.onedev.server.search.entitytext.DefaultPullRequestTextService;
import io.onedev.server.search.entitytext.IssueTextService;
import io.onedev.server.search.entitytext.PullRequestTextService;
import io.onedev.server.security.BasicAuthenticationFilter;
import io.onedev.server.security.BearerAuthenticationFilter;
import io.onedev.server.security.CodePullAuthorizationSource;
@ -348,26 +183,208 @@ import io.onedev.server.security.DefaultWebSecurityManager;
import io.onedev.server.security.FilterChainConfigurator;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.security.realm.GeneralAuthorizingRealm;
import io.onedev.server.service.AccessTokenAuthorizationService;
import io.onedev.server.service.AccessTokenService;
import io.onedev.server.service.AgentAttributeService;
import io.onedev.server.service.AgentLastUsedDateService;
import io.onedev.server.service.AgentService;
import io.onedev.server.service.AgentTokenService;
import io.onedev.server.service.AlertService;
import io.onedev.server.service.BaseAuthorizationService;
import io.onedev.server.service.BuildDependenceService;
import io.onedev.server.service.BuildLabelService;
import io.onedev.server.service.BuildMetricService;
import io.onedev.server.service.BuildParamService;
import io.onedev.server.service.BuildQueryPersonalizationService;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.CodeCommentMentionService;
import io.onedev.server.service.CodeCommentQueryPersonalizationService;
import io.onedev.server.service.CodeCommentReplyService;
import io.onedev.server.service.CodeCommentService;
import io.onedev.server.service.CodeCommentStatusChangeService;
import io.onedev.server.service.CodeCommentTouchService;
import io.onedev.server.service.CommitQueryPersonalizationService;
import io.onedev.server.service.DashboardGroupShareService;
import io.onedev.server.service.DashboardService;
import io.onedev.server.service.DashboardUserShareService;
import io.onedev.server.service.DashboardVisitService;
import io.onedev.server.service.EmailAddressService;
import io.onedev.server.service.GitLfsLockService;
import io.onedev.server.service.GpgKeyService;
import io.onedev.server.service.GroupAuthorizationService;
import io.onedev.server.service.GroupEntitlementService;
import io.onedev.server.service.GroupService;
import io.onedev.server.service.IssueAuthorizationService;
import io.onedev.server.service.IssueChangeService;
import io.onedev.server.service.IssueCommentReactionService;
import io.onedev.server.service.IssueCommentRevisionService;
import io.onedev.server.service.IssueCommentService;
import io.onedev.server.service.IssueDescriptionRevisionService;
import io.onedev.server.service.IssueFieldService;
import io.onedev.server.service.IssueLinkService;
import io.onedev.server.service.IssueMentionService;
import io.onedev.server.service.IssueQueryPersonalizationService;
import io.onedev.server.service.IssueReactionService;
import io.onedev.server.service.IssueScheduleService;
import io.onedev.server.service.IssueService;
import io.onedev.server.service.IssueStateHistoryService;
import io.onedev.server.service.IssueTouchService;
import io.onedev.server.service.IssueVoteService;
import io.onedev.server.service.IssueWatchService;
import io.onedev.server.service.IssueWorkService;
import io.onedev.server.service.IterationService;
import io.onedev.server.service.JobCacheService;
import io.onedev.server.service.LabelSpecService;
import io.onedev.server.service.LinkAuthorizationService;
import io.onedev.server.service.LinkSpecService;
import io.onedev.server.service.MembershipService;
import io.onedev.server.service.PackBlobReferenceService;
import io.onedev.server.service.PackBlobService;
import io.onedev.server.service.PackLabelService;
import io.onedev.server.service.PackQueryPersonalizationService;
import io.onedev.server.service.PackService;
import io.onedev.server.service.PendingSuggestionApplyService;
import io.onedev.server.service.ProjectEntitlementService;
import io.onedev.server.service.ProjectLabelService;
import io.onedev.server.service.ProjectLastEventDateService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.PullRequestAssignmentService;
import io.onedev.server.service.PullRequestChangeService;
import io.onedev.server.service.PullRequestCommentReactionService;
import io.onedev.server.service.PullRequestCommentRevisionService;
import io.onedev.server.service.PullRequestCommentService;
import io.onedev.server.service.PullRequestDescriptionRevisionService;
import io.onedev.server.service.PullRequestLabelService;
import io.onedev.server.service.PullRequestMentionService;
import io.onedev.server.service.PullRequestQueryPersonalizationService;
import io.onedev.server.service.PullRequestReactionService;
import io.onedev.server.service.PullRequestReviewService;
import io.onedev.server.service.PullRequestService;
import io.onedev.server.service.PullRequestTouchService;
import io.onedev.server.service.PullRequestUpdateService;
import io.onedev.server.service.PullRequestWatchService;
import io.onedev.server.service.ReviewedDiffService;
import io.onedev.server.service.RoleService;
import io.onedev.server.service.SettingService;
import io.onedev.server.service.SshKeyService;
import io.onedev.server.service.SsoAccountService;
import io.onedev.server.service.SsoProviderService;
import io.onedev.server.service.StopwatchService;
import io.onedev.server.service.UserAuthorizationService;
import io.onedev.server.service.UserEntitlementService;
import io.onedev.server.service.UserInvitationService;
import io.onedev.server.service.UserService;
import io.onedev.server.service.impl.DefaultAccessTokenAuthorizationService;
import io.onedev.server.service.impl.DefaultAccessTokenService;
import io.onedev.server.service.impl.DefaultAgentAttributeService;
import io.onedev.server.service.impl.DefaultAgentLastUsedDateService;
import io.onedev.server.service.impl.DefaultAgentService;
import io.onedev.server.service.impl.DefaultAgentTokenService;
import io.onedev.server.service.impl.DefaultAlertService;
import io.onedev.server.service.impl.DefaultBaseAuthorizationService;
import io.onedev.server.service.impl.DefaultBuildDependenceService;
import io.onedev.server.service.impl.DefaultBuildLabelService;
import io.onedev.server.service.impl.DefaultBuildMetricService;
import io.onedev.server.service.impl.DefaultBuildParamService;
import io.onedev.server.service.impl.DefaultBuildQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultBuildService;
import io.onedev.server.service.impl.DefaultCodeCommentMentionService;
import io.onedev.server.service.impl.DefaultCodeCommentQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultCodeCommentReplyService;
import io.onedev.server.service.impl.DefaultCodeCommentService;
import io.onedev.server.service.impl.DefaultCodeCommentStatusChangeService;
import io.onedev.server.service.impl.DefaultCodeCommentTouchService;
import io.onedev.server.service.impl.DefaultCommitQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultDashboardGroupShareService;
import io.onedev.server.service.impl.DefaultDashboardService;
import io.onedev.server.service.impl.DefaultDashboardUserShareService;
import io.onedev.server.service.impl.DefaultDashboardVisitService;
import io.onedev.server.service.impl.DefaultEmailAddressService;
import io.onedev.server.service.impl.DefaultGitLfsLockService;
import io.onedev.server.service.impl.DefaultGpgKeyService;
import io.onedev.server.service.impl.DefaultGroupAuthorizationService;
import io.onedev.server.service.impl.DefaultGroupEntitlementService;
import io.onedev.server.service.impl.DefaultGroupService;
import io.onedev.server.service.impl.DefaultIssueAuthorizationService;
import io.onedev.server.service.impl.DefaultIssueChangeService;
import io.onedev.server.service.impl.DefaultIssueCommentReactionService;
import io.onedev.server.service.impl.DefaultIssueCommentRevisionService;
import io.onedev.server.service.impl.DefaultIssueCommentService;
import io.onedev.server.service.impl.DefaultIssueDescriptionRevisionService;
import io.onedev.server.service.impl.DefaultIssueFieldService;
import io.onedev.server.service.impl.DefaultIssueLinkService;
import io.onedev.server.service.impl.DefaultIssueMentionService;
import io.onedev.server.service.impl.DefaultIssueQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultIssueReactionService;
import io.onedev.server.service.impl.DefaultIssueScheduleService;
import io.onedev.server.service.impl.DefaultIssueService;
import io.onedev.server.service.impl.DefaultIssueStateHistoryService;
import io.onedev.server.service.impl.DefaultIssueTouchService;
import io.onedev.server.service.impl.DefaultIssueVoteService;
import io.onedev.server.service.impl.DefaultIssueWatchService;
import io.onedev.server.service.impl.DefaultIssueWorkService;
import io.onedev.server.service.impl.DefaultIterationService;
import io.onedev.server.service.impl.DefaultJobCacheService;
import io.onedev.server.service.impl.DefaultLabelSpecService;
import io.onedev.server.service.impl.DefaultLinkAuthorizationService;
import io.onedev.server.service.impl.DefaultLinkSpecService;
import io.onedev.server.service.impl.DefaultMembershipService;
import io.onedev.server.service.impl.DefaultPackBlobReferenceService;
import io.onedev.server.service.impl.DefaultPackBlobService;
import io.onedev.server.service.impl.DefaultPackLabelService;
import io.onedev.server.service.impl.DefaultPackQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultPackService;
import io.onedev.server.service.impl.DefaultPendingSuggestionApplyService;
import io.onedev.server.service.impl.DefaultProjectEntitlementService;
import io.onedev.server.service.impl.DefaultProjectLabelService;
import io.onedev.server.service.impl.DefaultProjectLastEventDateService;
import io.onedev.server.service.impl.DefaultProjectService;
import io.onedev.server.service.impl.DefaultPullRequestAssignmentService;
import io.onedev.server.service.impl.DefaultPullRequestChangeService;
import io.onedev.server.service.impl.DefaultPullRequestCommentReactionService;
import io.onedev.server.service.impl.DefaultPullRequestCommentRevisionService;
import io.onedev.server.service.impl.DefaultPullRequestCommentService;
import io.onedev.server.service.impl.DefaultPullRequestDescriptionRevisionService;
import io.onedev.server.service.impl.DefaultPullRequestLabelService;
import io.onedev.server.service.impl.DefaultPullRequestMentionService;
import io.onedev.server.service.impl.DefaultPullRequestQueryPersonalizationService;
import io.onedev.server.service.impl.DefaultPullRequestReactionService;
import io.onedev.server.service.impl.DefaultPullRequestReviewService;
import io.onedev.server.service.impl.DefaultPullRequestService;
import io.onedev.server.service.impl.DefaultPullRequestTouchService;
import io.onedev.server.service.impl.DefaultPullRequestUpdateService;
import io.onedev.server.service.impl.DefaultPullRequestWatchService;
import io.onedev.server.service.impl.DefaultReviewedDiffService;
import io.onedev.server.service.impl.DefaultRoleService;
import io.onedev.server.service.impl.DefaultSettingService;
import io.onedev.server.service.impl.DefaultSshKeyService;
import io.onedev.server.service.impl.DefaultSsoAccountService;
import io.onedev.server.service.impl.DefaultSsoProviderService;
import io.onedev.server.service.impl.DefaultStopwatchService;
import io.onedev.server.service.impl.DefaultUserAuthorizationService;
import io.onedev.server.service.impl.DefaultUserEntitlementService;
import io.onedev.server.service.impl.DefaultUserInvitationService;
import io.onedev.server.service.impl.DefaultUserService;
import io.onedev.server.ssh.CommandCreator;
import io.onedev.server.ssh.DefaultSshAuthenticator;
import io.onedev.server.ssh.DefaultSshManager;
import io.onedev.server.ssh.DefaultSshService;
import io.onedev.server.ssh.SshAuthenticator;
import io.onedev.server.ssh.SshManager;
import io.onedev.server.ssh.SshService;
import io.onedev.server.taskschedule.DefaultTaskScheduler;
import io.onedev.server.taskschedule.TaskScheduler;
import io.onedev.server.updatecheck.DefaultUpdateCheckManager;
import io.onedev.server.updatecheck.UpdateCheckManager;
import io.onedev.server.updatecheck.DefaultUpdateCheckService;
import io.onedev.server.updatecheck.UpdateCheckService;
import io.onedev.server.util.ScriptContribution;
import io.onedev.server.util.concurrent.BatchWorkManager;
import io.onedev.server.util.concurrent.DefaultBatchWorkManager;
import io.onedev.server.util.concurrent.DefaultWorkExecutor;
import io.onedev.server.util.concurrent.WorkExecutor;
import io.onedev.server.util.concurrent.BatchWorkExecutionService;
import io.onedev.server.util.concurrent.DefaultBatchWorkExecutionService;
import io.onedev.server.util.concurrent.DefaultWorkExecutionService;
import io.onedev.server.util.concurrent.WorkExecutionService;
import io.onedev.server.util.jackson.ObjectMapperConfigurator;
import io.onedev.server.util.jackson.ObjectMapperProvider;
import io.onedev.server.util.jackson.git.GitObjectMapperConfigurator;
import io.onedev.server.util.jackson.hibernate.HibernateObjectMapperConfigurator;
import io.onedev.server.util.oauth.DefaultOAuthTokenManager;
import io.onedev.server.util.oauth.OAuthTokenManager;
import io.onedev.server.util.oauth.DefaultOAuthTokenService;
import io.onedev.server.util.oauth.OAuthTokenService;
import io.onedev.server.util.xstream.CollectionConverter;
import io.onedev.server.util.xstream.HibernateProxyConverter;
import io.onedev.server.util.xstream.MapConverter;
@ -376,15 +393,17 @@ import io.onedev.server.util.xstream.ReflectionConverter;
import io.onedev.server.util.xstream.StringConverter;
import io.onedev.server.util.xstream.VersionedDocumentConverter;
import io.onedev.server.validation.MessageInterpolator;
import io.onedev.server.validation.ShallowValidatorProvider;
import io.onedev.server.validation.ValidatorProvider;
import io.onedev.server.web.DefaultUrlManager;
import io.onedev.server.web.DefaultUrlService;
import io.onedev.server.web.DefaultWicketFilter;
import io.onedev.server.web.DefaultWicketServlet;
import io.onedev.server.web.ResourcePackScopeContribution;
import io.onedev.server.web.UrlManager;
import io.onedev.server.web.SessionListener;
import io.onedev.server.web.UrlService;
import io.onedev.server.web.WebApplication;
import io.onedev.server.web.avatar.AvatarManager;
import io.onedev.server.web.avatar.DefaultAvatarManager;
import io.onedev.server.web.avatar.AvatarService;
import io.onedev.server.web.avatar.DefaultAvatarService;
import io.onedev.server.web.component.diff.DiffRenderer;
import io.onedev.server.web.component.markdown.SourcePositionTrackExtension;
import io.onedev.server.web.component.markdown.emoji.EmojiExtension;
@ -397,24 +416,24 @@ import io.onedev.server.web.exceptionhandler.PageExpiredExceptionHandler;
import io.onedev.server.web.page.layout.AdministrationSettingContribution;
import io.onedev.server.web.page.project.blob.render.BlobRenderer;
import io.onedev.server.web.page.project.setting.ProjectSettingContribution;
import io.onedev.server.web.upload.DefaultUploadManager;
import io.onedev.server.web.upload.UploadManager;
import io.onedev.server.web.upload.DefaultUploadService;
import io.onedev.server.web.upload.UploadService;
import io.onedev.server.web.websocket.AlertEventBroadcaster;
import io.onedev.server.web.websocket.BuildEventBroadcaster;
import io.onedev.server.web.websocket.CodeCommentEventBroadcaster;
import io.onedev.server.web.websocket.CommitIndexedBroadcaster;
import io.onedev.server.web.websocket.DefaultWebSocketManager;
import io.onedev.server.web.websocket.DefaultWebSocketService;
import io.onedev.server.web.websocket.IssueEventBroadcaster;
import io.onedev.server.web.websocket.PullRequestEventBroadcaster;
import io.onedev.server.web.websocket.WebSocketManager;
import io.onedev.server.xodus.CommitInfoManager;
import io.onedev.server.xodus.DefaultCommitInfoManager;
import io.onedev.server.xodus.DefaultIssueInfoManager;
import io.onedev.server.xodus.DefaultPullRequestInfoManager;
import io.onedev.server.xodus.DefaultVisitInfoManager;
import io.onedev.server.xodus.IssueInfoManager;
import io.onedev.server.xodus.PullRequestInfoManager;
import io.onedev.server.xodus.VisitInfoManager;
import io.onedev.server.web.websocket.WebSocketService;
import io.onedev.server.xodus.CommitInfoService;
import io.onedev.server.xodus.DefaultCommitInfoService;
import io.onedev.server.xodus.DefaultIssueInfoService;
import io.onedev.server.xodus.DefaultPullRequestInfoService;
import io.onedev.server.xodus.DefaultVisitInfoService;
import io.onedev.server.xodus.IssueInfoService;
import io.onedev.server.xodus.PullRequestInfoService;
import io.onedev.server.xodus.VisitInfoService;
import nl.altindag.ssl.SSLFactory;
/**
@ -428,8 +447,8 @@ public class CoreModule extends AbstractPluginModule {
super.configure();
bind(ListenerRegistry.class).to(DefaultListenerRegistry.class);
bind(JettyManager.class).to(DefaultJettyManager.class);
bind(ServletContextHandler.class).toProvider(DefaultJettyManager.class);
bind(JettyService.class).to(DefaultJettyService.class);
bind(ServletContextHandler.class).toProvider(DefaultJettyService.class);
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(Singleton.class);
@ -440,8 +459,31 @@ public class CoreModule extends AbstractPluginModule {
.messageInterpolator(new MessageInterpolator());
return configuration.buildValidatorFactory();
}).in(Singleton.class);
bind(ValidatorFactory.class).annotatedWith(Shallow.class).toProvider(() -> {
Configuration<?> configuration = Validation
.byDefaultProvider()
.configure()
.traversableResolver(new TraversableResolver() {
@Override
public boolean isReachable(Object traversableObject, Node traversableProperty,
Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return true;
}
@Override
public boolean isCascadable(Object traversableObject, Node traversableProperty,
Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return false;
}
})
.messageInterpolator(new MessageInterpolator());
return configuration.buildValidatorFactory();
}).in(Singleton.class);
bind(Validator.class).toProvider(ValidatorProvider.class).in(Singleton.class);
bind(Validator.class).annotatedWith(Shallow.class).toProvider(ShallowValidatorProvider.class).in(Singleton.class);
configurePersistence();
configureSecurity();
@ -455,122 +497,127 @@ public class CoreModule extends AbstractPluginModule {
* HK2 to guice bridge can only search in explicit bindings in Guice
*/
bind(SshAuthenticator.class).to(DefaultSshAuthenticator.class);
bind(SshManager.class).to(DefaultSshManager.class);
bind(MarkdownManager.class).to(DefaultMarkdownManager.class);
bind(SettingManager.class).to(DefaultSettingManager.class);
bind(DataManager.class).to(DefaultDataManager.class);
bind(SshService.class).to(DefaultSshService.class);
bind(MarkdownService.class).to(DefaultMarkdownService.class);
bind(SettingService.class).to(DefaultSettingService.class);
bind(DataService.class).to(DefaultDataService.class);
bind(TaskScheduler.class).to(DefaultTaskScheduler.class);
bind(PullRequestCommentManager.class).to(DefaultPullRequestCommentManager.class);
bind(CodeCommentManager.class).to(DefaultCodeCommentManager.class);
bind(PullRequestManager.class).to(DefaultPullRequestManager.class);
bind(PullRequestUpdateManager.class).to(DefaultPullRequestUpdateManager.class);
bind(ProjectManager.class).to(DefaultProjectManager.class);
bind(ProjectLastEventDateManager.class).to(DefaultProjectLastEventDateManager.class);
bind(UserInvitationManager.class).to(DefaultUserInvitationManager.class);
bind(PullRequestReviewManager.class).to(DefaultPullRequestReviewManager.class);
bind(BuildManager.class).to(DefaultBuildManager.class);
bind(BuildDependenceManager.class).to(DefaultBuildDependenceManager.class);
bind(JobManager.class).to(DefaultJobManager.class);
bind(JobCacheManager.class).to(DefaultJobCacheManager.class);
bind(LogManager.class).to(DefaultLogManager.class);
bind(MailManager.class).to(DefaultMailManager.class);
bind(IssueManager.class).to(DefaultIssueManager.class);
bind(IssueFieldManager.class).to(DefaultIssueFieldManager.class);
bind(BuildParamManager.class).to(DefaultBuildParamManager.class);
bind(UserAuthorizationManager.class).to(DefaultUserAuthorizationManager.class);
bind(GroupAuthorizationManager.class).to(DefaultGroupAuthorizationManager.class);
bind(PullRequestWatchManager.class).to(DefaultPullRequestWatchManager.class);
bind(RoleManager.class).to(DefaultRoleManager.class);
bind(CommitInfoManager.class).to(DefaultCommitInfoManager.class);
bind(IssueInfoManager.class).to(DefaultIssueInfoManager.class);
bind(VisitInfoManager.class).to(DefaultVisitInfoManager.class);
bind(BatchWorkManager.class).to(DefaultBatchWorkManager.class);
bind(WorkExecutor.class).to(DefaultWorkExecutor.class);
bind(GroupManager.class).to(DefaultGroupManager.class);
bind(IssueMentionManager.class).to(DefaultIssueMentionManager.class);
bind(PullRequestMentionManager.class).to(DefaultPullRequestMentionManager.class);
bind(CodeCommentMentionManager.class).to(DefaultCodeCommentMentionManager.class);
bind(MembershipManager.class).to(DefaultMembershipManager.class);
bind(PullRequestChangeManager.class).to(DefaultPullRequestChangeManager.class);
bind(CodeCommentReplyManager.class).to(DefaultCodeCommentReplyManager.class);
bind(CodeCommentStatusChangeManager.class).to(DefaultCodeCommentStatusChangeManager.class);
bind(AttachmentManager.class).to(DefaultAttachmentManager.class);
bind(PullRequestInfoManager.class).to(DefaultPullRequestInfoManager.class);
bind(PullRequestCommentService.class).to(DefaultPullRequestCommentService.class);
bind(CodeCommentService.class).to(DefaultCodeCommentService.class);
bind(PullRequestService.class).to(DefaultPullRequestService.class);
bind(PullRequestUpdateService.class).to(DefaultPullRequestUpdateService.class);
bind(ProjectService.class).to(DefaultProjectService.class);
bind(ProjectLastEventDateService.class).to(DefaultProjectLastEventDateService.class);
bind(UserInvitationService.class).to(DefaultUserInvitationService.class);
bind(PullRequestReviewService.class).to(DefaultPullRequestReviewService.class);
bind(BuildService.class).to(DefaultBuildService.class);
bind(BuildDependenceService.class).to(DefaultBuildDependenceService.class);
bind(JobService.class).to(DefaultJobService.class);
bind(JobCacheService.class).to(DefaultJobCacheService.class);
bind(LogService.class).to(DefaultLogService.class);
bind(MailService.class).to(DefaultMailService.class);
bind(IssueService.class).to(DefaultIssueService.class);
bind(IssueFieldService.class).to(DefaultIssueFieldService.class);
bind(BuildParamService.class).to(DefaultBuildParamService.class);
bind(UserAuthorizationService.class).to(DefaultUserAuthorizationService.class);
bind(GroupAuthorizationService.class).to(DefaultGroupAuthorizationService.class);
bind(PullRequestWatchService.class).to(DefaultPullRequestWatchService.class);
bind(RoleService.class).to(DefaultRoleService.class);
bind(CommitInfoService.class).to(DefaultCommitInfoService.class);
bind(IssueInfoService.class).to(DefaultIssueInfoService.class);
bind(VisitInfoService.class).to(DefaultVisitInfoService.class);
bind(BatchWorkExecutionService.class).to(DefaultBatchWorkExecutionService.class);
bind(WorkExecutionService.class).to(DefaultWorkExecutionService.class);
bind(GroupService.class).to(DefaultGroupService.class);
bind(IssueMentionService.class).to(DefaultIssueMentionService.class);
bind(PullRequestMentionService.class).to(DefaultPullRequestMentionService.class);
bind(CodeCommentMentionService.class).to(DefaultCodeCommentMentionService.class);
bind(MembershipService.class).to(DefaultMembershipService.class);
bind(PullRequestChangeService.class).to(DefaultPullRequestChangeService.class);
bind(CodeCommentReplyService.class).to(DefaultCodeCommentReplyService.class);
bind(CodeCommentStatusChangeService.class).to(DefaultCodeCommentStatusChangeService.class);
bind(AttachmentService.class).to(DefaultAttachmentService.class);
bind(PullRequestInfoService.class).to(DefaultPullRequestInfoService.class);
bind(PullRequestNotificationManager.class);
bind(CommitNotificationManager.class);
bind(BuildNotificationManager.class);
bind(PackNotificationManager.class);
bind(IssueNotificationManager.class);
bind(CodeCommentNotificationManager.class);
bind(CodeCommentManager.class).to(DefaultCodeCommentManager.class);
bind(AccessTokenManager.class).to(DefaultAccessTokenManager.class);
bind(UserManager.class).to(DefaultUserManager.class);
bind(IssueWatchManager.class).to(DefaultIssueWatchManager.class);
bind(IssueChangeManager.class).to(DefaultIssueChangeManager.class);
bind(IssueVoteManager.class).to(DefaultIssueVoteManager.class);
bind(IssueWorkManager.class).to(DefaultIssueWorkManager.class);
bind(IterationManager.class).to(DefaultIterationManager.class);
bind(IssueCommentManager.class).to(DefaultIssueCommentManager.class);
bind(IssueQueryPersonalizationManager.class).to(DefaultIssueQueryPersonalizationManager.class);
bind(PullRequestQueryPersonalizationManager.class).to(DefaultPullRequestQueryPersonalizationManager.class);
bind(CodeCommentQueryPersonalizationManager.class).to(DefaultCodeCommentQueryPersonalizationManager.class);
bind(CommitQueryPersonalizationManager.class).to(DefaultCommitQueryPersonalizationManager.class);
bind(BuildQueryPersonalizationManager.class).to(DefaultBuildQueryPersonalizationManager.class);
bind(PackQueryPersonalizationManager.class).to(DefaultPackQueryPersonalizationManager.class);
bind(PullRequestAssignmentManager.class).to(DefaultPullRequestAssignmentManager.class);
bind(SshKeyManager.class).to(DefaultSshKeyManager.class);
bind(BuildMetricManager.class).to(DefaultBuildMetricManager.class);
bind(ReferenceChangeManager.class).to(DefaultReferenceChangeManager.class);
bind(GitLfsLockManager.class).to(DefaultGitLfsLockManager.class);
bind(IssueScheduleManager.class).to(DefaultIssueScheduleManager.class);
bind(LinkSpecManager.class).to(DefaultLinkSpecManager.class);
bind(IssueLinkManager.class).to(DefaultIssueLinkManager.class);
bind(IssueStateHistoryManager.class).to(DefaultIssueStateHistoryManager.class);
bind(LinkAuthorizationManager.class).to(DefaultLinkAuthorizationManager.class);
bind(EmailAddressManager.class).to(DefaultEmailAddressManager.class);
bind(GpgKeyManager.class).to(DefaultGpgKeyManager.class);
bind(IssueTextManager.class).to(DefaultIssueTextManager.class);
bind(PullRequestTextManager.class).to(DefaultPullRequestTextManager.class);
bind(CodeCommentTextManager.class).to(DefaultCodeCommentTextManager.class);
bind(PendingSuggestionApplyManager.class).to(DefaultPendingSuggestionApplyManager.class);
bind(IssueAuthorizationManager.class).to(DefaultIssueAuthorizationManager.class);
bind(DashboardManager.class).to(DefaultDashboardManager.class);
bind(DashboardUserShareManager.class).to(DefaultDashboardUserShareManager.class);
bind(DashboardGroupShareManager.class).to(DefaultDashboardGroupShareManager.class);
bind(DashboardVisitManager.class).to(DefaultDashboardVisitManager.class);
bind(LabelSpecManager.class).to(DefaultLabelSpecManager.class);
bind(ProjectLabelManager.class).to(DefaultProjectLabelManager.class);
bind(BuildLabelManager.class).to(DefaultBuildLabelManager.class);
bind(PackLabelManager.class).to(DefaultPackLabelManager.class);
bind(PullRequestLabelManager.class).to(DefaultPullRequestLabelManager.class);
bind(IssueTouchManager.class).to(DefaultIssueTouchManager.class);
bind(PullRequestTouchManager.class).to(DefaultPullRequestTouchManager.class);
bind(CodeCommentTouchManager.class).to(DefaultCodeCommentTouchManager.class);
bind(AlertManager.class).to(DefaultAlertManager.class);
bind(UpdateCheckManager.class).to(DefaultUpdateCheckManager.class);
bind(StopwatchManager.class).to(DefaultStopwatchManager.class);
bind(PackManager.class).to(DefaultPackManager.class);
bind(PackBlobManager.class).to(DefaultPackBlobManager.class);
bind(PackBlobReferenceManager.class).to(DefaultPackBlobReferenceManager.class);
bind(AccessTokenAuthorizationManager.class).to(DefaultAccessTokenAuthorizationManager.class);
bind(ReviewedDiffManager.class).to(DefaultReviewedDiffManager.class);
bind(OAuthTokenManager.class).to(DefaultOAuthTokenManager.class);
bind(IssueReactionManager.class).to(DefaultIssueReactionManager.class);
bind(IssueCommentReactionManager.class).to(DefaultIssueCommentReactionManager.class);
bind(PullRequestReactionManager.class).to(DefaultPullRequestReactionManager.class);
bind(PullRequestCommentReactionManager.class).to(DefaultPullRequestCommentReactionManager.class);
bind(IssueCommentRevisionManager.class).to(DefaultIssueCommentRevisionManager.class);
bind(PullRequestCommentRevisionManager.class).to(DefaultPullRequestCommentRevisionManager.class);
bind(IssueDescriptionRevisionManager.class).to(DefaultIssueDescriptionRevisionManager.class);
bind(PullRequestDescriptionRevisionManager.class).to(DefaultPullRequestDescriptionRevisionManager.class);
bind(BaseAuthorizationManager.class).to(DefaultBaseAuthorizationManager.class);
bind(CodeCommentService.class).to(DefaultCodeCommentService.class);
bind(AccessTokenService.class).to(DefaultAccessTokenService.class);
bind(UserService.class).to(DefaultUserService.class);
bind(IssueWatchService.class).to(DefaultIssueWatchService.class);
bind(IssueChangeService.class).to(DefaultIssueChangeService.class);
bind(IssueVoteService.class).to(DefaultIssueVoteService.class);
bind(IssueWorkService.class).to(DefaultIssueWorkService.class);
bind(IterationService.class).to(DefaultIterationService.class);
bind(IssueCommentService.class).to(DefaultIssueCommentService.class);
bind(IssueQueryPersonalizationService.class).to(DefaultIssueQueryPersonalizationService.class);
bind(PullRequestQueryPersonalizationService.class).to(DefaultPullRequestQueryPersonalizationService.class);
bind(CodeCommentQueryPersonalizationService.class).to(DefaultCodeCommentQueryPersonalizationService.class);
bind(CommitQueryPersonalizationService.class).to(DefaultCommitQueryPersonalizationService.class);
bind(BuildQueryPersonalizationService.class).to(DefaultBuildQueryPersonalizationService.class);
bind(PackQueryPersonalizationService.class).to(DefaultPackQueryPersonalizationService.class);
bind(PullRequestAssignmentService.class).to(DefaultPullRequestAssignmentService.class);
bind(SshKeyService.class).to(DefaultSshKeyService.class);
bind(BuildMetricService.class).to(DefaultBuildMetricService.class);
bind(ReferenceChangeService.class).to(DefaultReferenceChangeService.class);
bind(GitLfsLockService.class).to(DefaultGitLfsLockService.class);
bind(IssueScheduleService.class).to(DefaultIssueScheduleService.class);
bind(LinkSpecService.class).to(DefaultLinkSpecService.class);
bind(IssueLinkService.class).to(DefaultIssueLinkService.class);
bind(IssueStateHistoryService.class).to(DefaultIssueStateHistoryService.class);
bind(LinkAuthorizationService.class).to(DefaultLinkAuthorizationService.class);
bind(EmailAddressService.class).to(DefaultEmailAddressService.class);
bind(GpgKeyService.class).to(DefaultGpgKeyService.class);
bind(IssueTextService.class).to(DefaultIssueTextService.class);
bind(PullRequestTextService.class).to(DefaultPullRequestTextService.class);
bind(CodeCommentTextService.class).to(DefaultCodeCommentTextService.class);
bind(PendingSuggestionApplyService.class).to(DefaultPendingSuggestionApplyService.class);
bind(IssueAuthorizationService.class).to(DefaultIssueAuthorizationService.class);
bind(DashboardService.class).to(DefaultDashboardService.class);
bind(DashboardUserShareService.class).to(DefaultDashboardUserShareService.class);
bind(DashboardGroupShareService.class).to(DefaultDashboardGroupShareService.class);
bind(DashboardVisitService.class).to(DefaultDashboardVisitService.class);
bind(LabelSpecService.class).to(DefaultLabelSpecService.class);
bind(ProjectLabelService.class).to(DefaultProjectLabelService.class);
bind(BuildLabelService.class).to(DefaultBuildLabelService.class);
bind(PackLabelService.class).to(DefaultPackLabelService.class);
bind(PullRequestLabelService.class).to(DefaultPullRequestLabelService.class);
bind(IssueTouchService.class).to(DefaultIssueTouchService.class);
bind(PullRequestTouchService.class).to(DefaultPullRequestTouchService.class);
bind(CodeCommentTouchService.class).to(DefaultCodeCommentTouchService.class);
bind(AlertService.class).to(DefaultAlertService.class);
bind(UpdateCheckService.class).to(DefaultUpdateCheckService.class);
bind(StopwatchService.class).to(DefaultStopwatchService.class);
bind(PackService.class).to(DefaultPackService.class);
bind(PackBlobService.class).to(DefaultPackBlobService.class);
bind(PackBlobReferenceService.class).to(DefaultPackBlobReferenceService.class);
bind(AccessTokenAuthorizationService.class).to(DefaultAccessTokenAuthorizationService.class);
bind(ReviewedDiffService.class).to(DefaultReviewedDiffService.class);
bind(OAuthTokenService.class).to(DefaultOAuthTokenService.class);
bind(IssueReactionService.class).to(DefaultIssueReactionService.class);
bind(IssueCommentReactionService.class).to(DefaultIssueCommentReactionService.class);
bind(PullRequestReactionService.class).to(DefaultPullRequestReactionService.class);
bind(PullRequestCommentReactionService.class).to(DefaultPullRequestCommentReactionService.class);
bind(IssueCommentRevisionService.class).to(DefaultIssueCommentRevisionService.class);
bind(PullRequestCommentRevisionService.class).to(DefaultPullRequestCommentRevisionService.class);
bind(IssueDescriptionRevisionService.class).to(DefaultIssueDescriptionRevisionService.class);
bind(PullRequestDescriptionRevisionService.class).to(DefaultPullRequestDescriptionRevisionService.class);
bind(SsoProviderService.class).to(DefaultSsoProviderService.class);
bind(SsoAccountService.class).to(DefaultSsoAccountService.class);
bind(BaseAuthorizationService.class).to(DefaultBaseAuthorizationService.class);
bind(GroupEntitlementService.class).to(DefaultGroupEntitlementService.class);
bind(UserEntitlementService.class).to(DefaultUserEntitlementService.class);
bind(ProjectEntitlementService.class).to(DefaultProjectEntitlementService.class);
bind(WebHookManager.class);
contribute(CodePullAuthorizationSource.class, DefaultJobManager.class);
contribute(CodePullAuthorizationSource.class, DefaultJobService.class);
bind(CodeIndexManager.class).to(DefaultCodeIndexManager.class);
bind(CodeSearchManager.class).to(DefaultCodeSearchManager.class);
bind(CodeIndexService.class).to(DefaultCodeIndexService.class);
bind(CodeSearchService.class).to(DefaultCodeSearchService.class);
Bootstrap.executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>()) {
@ -591,8 +638,7 @@ public class CoreModule extends AbstractPluginModule {
bind(OsInfo.class).toProvider(() -> ExecutorUtils.getOsInfo()).in(Singleton.class);
contributeFromPackage(LogInstruction.class, LogInstruction.class);
contributeFromPackage(LogInstruction.class, LogInstruction.class);
contribute(CodeProblemContribution.class, (build, blobPath, reportName) -> newArrayList());
@ -635,7 +681,7 @@ public class CoreModule extends AbstractPluginModule {
bind(GitLfsFilter.class);
bind(GitPreReceiveCallback.class);
bind(GitPostReceiveCallback.class);
bind(SignatureVerificationManager.class).to(DefaultSignatureVerificationManager.class);
bind(SignatureVerificationService.class).to(DefaultSignatureVerificationService.class);
contribute(CommandCreator.class, SshCommandCreator.class);
contributeFromPackage(SignatureVerifier.class, SignatureVerifier.class);
}
@ -647,21 +693,23 @@ public class CoreModule extends AbstractPluginModule {
contribute(FilterChainConfigurator.class, filterChainManager -> filterChainManager.createChain("/~api/**", "noSessionCreation, authcBasic, authcBearer"));
contribute(JerseyConfigurator.class, resourceConfig -> resourceConfig.packages(ProjectResource.class.getPackage().getName()));
contribute(JerseyConfigurator.class, resourceConfig -> resourceConfig.register(ClusterResource.class));
contribute(JerseyConfigurator.class, resourceConfig -> resourceConfig.register(McpHelperResource.class));
contribute(JerseyConfigurator.class, resourceConfig -> resourceConfig.register(BuildSpecSchemaResource.class));
}
private void configureWeb() {
bind(WicketServlet.class).to(DefaultWicketServlet.class);
bind(WicketFilter.class).to(DefaultWicketFilter.class);
bind(EditSupportRegistry.class).to(DefaultEditSupportRegistry.class);
bind(WebSocketManager.class).to(DefaultWebSocketManager.class);
bind(WebSocketService.class).to(DefaultWebSocketService.class);
bind(SessionDataStoreFactory.class).to(DefaultSessionDataStoreFactory.class);
contributeFromPackage(EditSupport.class, EditSupport.class);
bind(org.apache.wicket.protocol.http.WebApplication.class).to(WebApplication.class);
bind(Application.class).to(WebApplication.class);
bind(AvatarManager.class).to(DefaultAvatarManager.class);
bind(WebSocketManager.class).to(DefaultWebSocketManager.class);
bind(AvatarService.class).to(DefaultAvatarService.class);
bind(WebSocketService.class).to(DefaultWebSocketService.class);
contributeFromPackage(EditSupport.class, EditSupportLocator.class);
@ -681,24 +729,26 @@ public class CoreModule extends AbstractPluginModule {
contributeFromPackage(ExceptionHandler.class, ConstraintViolationExceptionHandler.class);
contributeFromPackage(ExceptionHandler.class, PageExpiredExceptionHandler.class);
contributeFromPackage(ExceptionHandler.class, WebApplicationExceptionHandler.class);
contribute(SessionListener.class, DefaultWebSocketService.class);
bind(UrlManager.class).to(DefaultUrlManager.class);
bind(UrlService.class).to(DefaultUrlService.class);
bind(CodeCommentEventBroadcaster.class);
bind(PullRequestEventBroadcaster.class);
bind(IssueEventBroadcaster.class);
bind(BuildEventBroadcaster.class);
bind(AlertEventBroadcaster.class);
bind(UploadManager.class).to(DefaultUploadManager.class);
bind(UploadService.class).to(DefaultUploadService.class);
bind(TaskButton.TaskFutureManager.class);
}
private void configureBuild() {
bind(ResourceAllocator.class).to(DefaultResourceAllocator.class);
bind(AgentManager.class).to(DefaultAgentManager.class);
bind(AgentTokenManager.class).to(DefaultAgentTokenManager.class);
bind(AgentAttributeManager.class).to(DefaultAgentAttributeManager.class);
bind(AgentLastUsedDateManager.class).to(DefaultAgentLastUsedDateManager.class);
bind(AgentService.class).to(DefaultAgentService.class);
bind(AgentTokenService.class).to(DefaultAgentTokenService.class);
bind(AgentAttributeService.class).to(DefaultAgentAttributeService.class);
bind(AgentLastUsedDateService.class).to(DefaultAgentLastUsedDateService.class);
contribute(ScriptContribution.class, new ScriptContribution() {
@ -736,22 +786,22 @@ public class CoreModule extends AbstractPluginModule {
}
private void configurePersistence() {
bind(DataManager.class).to(DefaultDataManager.class);
bind(DataService.class).to(DefaultDataService.class);
bind(Session.class).toProvider(SessionProvider.class);
bind(EntityManager.class).toProvider(SessionProvider.class);
bind(SessionFactory.class).toProvider(SessionFactoryProvider.class);
bind(EntityManagerFactory.class).toProvider(SessionFactoryProvider.class);
bind(SessionFactoryManager.class).to(DefaultSessionFactoryManager.class);
bind(SessionFactoryService.class).to(DefaultSessionFactoryService.class);
contribute(ObjectMapperConfigurator.class, HibernateObjectMapperConfigurator.class);
bind(Interceptor.class).to(HibernateInterceptor.class);
bind(PhysicalNamingStrategy.class).toInstance(new PrefixedNamingStrategy("o_"));
bind(SessionManager.class).to(DefaultSessionManager.class);
bind(TransactionManager.class).to(DefaultTransactionManager.class);
bind(IdManager.class).to(DefaultIdManager.class);
bind(SessionService.class).to(DefaultSessionService.class);
bind(TransactionService.class).to(DefaultTransactionService.class);
bind(IdService.class).to(DefaultIdService.class);
bind(Dao.class).to(DefaultDao.class);
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

View File

@ -10,6 +10,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
@ -21,7 +22,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.inject.Inject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
@ -43,20 +44,20 @@ import io.onedev.commons.loader.AppLoader;
import io.onedev.commons.loader.ManagedSerializedForm;
import io.onedev.commons.utils.FileUtils;
import io.onedev.commons.utils.TarUtils;
import io.onedev.server.cluster.ClusterManager;
import io.onedev.server.data.DataManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.cluster.ClusterService;
import io.onedev.server.data.DataService;
import io.onedev.server.service.SettingService;
import io.onedev.server.event.ListenerRegistry;
import io.onedev.server.event.system.SystemStarted;
import io.onedev.server.event.system.SystemStarting;
import io.onedev.server.event.system.SystemStopped;
import io.onedev.server.event.system.SystemStopping;
import io.onedev.server.exception.ServerNotReadyException;
import io.onedev.server.jetty.JettyManager;
import io.onedev.server.jetty.JettyService;
import io.onedev.server.model.support.administration.SystemSetting;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.SessionFactoryManager;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.IdService;
import io.onedev.server.persistence.SessionFactoryService;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.persistence.annotation.Sessional;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.taskschedule.TaskScheduler;
@ -65,59 +66,60 @@ import io.onedev.server.util.init.InitStage;
import io.onedev.server.util.init.ManualConfig;
public class OneDev extends AbstractPlugin implements Serializable, Runnable {
private static final Logger logger = LoggerFactory.getLogger(OneDev.class);
private final Provider<JettyManager> jettyLauncherProvider;
private final SessionManager sessionManager;
private final DataManager dataManager;
private final Provider<JettyService> jettyLauncherProvider;
private final SessionService sessionService;
private final DataService dataService;
private final Provider<ServerConfig> serverConfigProvider;
private final ListenerRegistry listenerRegistry;
private final TaskScheduler taskScheduler;
private final ExecutorService executorService;
private final ClusterManager clusterManager;
private final SettingManager settingManager;
private final IdManager idManager;
private final SessionFactoryManager sessionFactoryManager;
private final ClusterService clusterService;
private final SettingService settingService;
private final IdService idService;
private final SessionFactoryService sessionFactoryService;
private final Date bootDate = new Date();
private volatile InitStage initStage;
private Class<?> wrapperManagerClass;
private Class<?> wrapperManagerClass;
private volatile Thread thread;
// Some are injected via provider as instantiation might encounter problem during upgrade
// Some are injected via provider as instantiation might encounter problem
// during upgrade
@Inject
public OneDev(Provider<JettyManager> jettyLauncherProvider, TaskScheduler taskScheduler,
SessionManager sessionManager, Provider<ServerConfig> serverConfigProvider,
DataManager dataManager, ExecutorService executorService,
ListenerRegistry listenerRegistry, ClusterManager clusterManager,
IdManager idManager, SessionFactoryManager sessionFactoryManager,
SettingManager settingManager) {
public OneDev(Provider<JettyService> jettyLauncherProvider, TaskScheduler taskScheduler,
SessionService sessionService, Provider<ServerConfig> serverConfigProvider,
DataService dataService, ExecutorService executorService,
ListenerRegistry listenerRegistry, ClusterService clusterService,
IdService idService, SessionFactoryService sessionFactoryService,
SettingService settingService) {
this.jettyLauncherProvider = jettyLauncherProvider;
this.taskScheduler = taskScheduler;
this.sessionManager = sessionManager;
this.dataManager = dataManager;
this.sessionService = sessionService;
this.dataService = dataService;
this.serverConfigProvider = serverConfigProvider;
this.executorService = executorService;
this.listenerRegistry = listenerRegistry;
this.clusterManager = clusterManager;
this.idManager = idManager;
this.sessionFactoryManager = sessionFactoryManager;
this.settingManager = settingManager;
this.clusterService = clusterService;
this.idService = idService;
this.sessionFactoryService = sessionFactoryService;
this.settingService = settingService;
try {
wrapperManagerClass = Class.forName("org.tanukisoftware.wrapper.WrapperManager");
} catch (ClassNotFoundException e) {
@ -126,7 +128,7 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
initStage = new InitStage("Server is Starting...");
}
@Override
public void start() {
var maintenanceFile = getMaintenanceFile(Bootstrap.installDir);
@ -138,33 +140,34 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
throw new RuntimeException(e);
}
}
SecurityUtils.bindAsSystem();
System.setProperty("hsqldb.reconfig_logging", "false");
System.setProperty("hsqldb.method_class_names", "java.lang.Math");
clusterManager.start();
sessionFactoryManager.start();
var databasePopulated = clusterManager.getHazelcastInstance().getCPSubsystem().getAtomicLong("databasePopulated");
// Do not use database lock as schema update will commit transaction immediately
// in MySQL
clusterManager.initWithLead(databasePopulated, () -> {
try (var conn = dataManager.openConnection()) {
clusterService.start();
sessionFactoryService.start();
var databasePopulated = clusterService.getHazelcastInstance().getCPSubsystem()
.getAtomicLong("databasePopulated");
// Do not use database lock as schema update will commit transaction immediately
// in MySQL
clusterService.initWithLead(databasePopulated, () -> {
try (var conn = dataService.openConnection()) {
callWithTransaction(conn, () -> {
dataManager.populateDatabase(conn);
dataService.populateDatabase(conn);
return null;
});
} catch (SQLException e) {
throw new RuntimeException(e);
};
}
return 1L;
});
idManager.init();
sessionManager.run(() -> listenerRegistry.post(new SystemStarting()));
idService.init();
sessionService.run(() -> listenerRegistry.post(new SystemStarting()));
jettyLauncherProvider.get().start();
var manualConfigs = checkData();
@ -174,16 +177,16 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
else
logger.warn("Please set up the server at " + guessServerUrl());
initStage = new InitStage("Server Setup", manualConfigs);
var localServer = clusterManager.getLocalServerAddress();
var localServer = clusterService.getLocalServerAddress();
while (true) {
if (maintenanceFile.exists()) {
logger.info("Maintenance requested, trying to stop all servers...");
clusterManager.submitToAllServers(() -> {
if (!localServer.equals(clusterManager.getLocalServerAddress()))
clusterService.submitToAllServers(() -> {
if (!localServer.equals(clusterService.getLocalServerAddress()))
restart();
return null;
});
while (thread != null && clusterManager.getServerAddresses().size() != 1) {
while (thread != null && clusterService.getServerAddresses().size() != 1) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
@ -199,7 +202,7 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
}
if (thread == null)
return;
manualConfigs = checkData();
if (manualConfigs.isEmpty()) {
initStage = new InitStage("Please wait...");
@ -209,21 +212,21 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
}
}
}
var leadServer = clusterManager.getLeaderServerAddress();
if (!leadServer.equals(clusterManager.getLocalServerAddress())) {
var leadServer = clusterService.getLeaderServerAddress();
if (!leadServer.equals(clusterService.getLocalServerAddress())) {
logger.info("Syncing assets...");
Client client = ClientBuilder.newClient();
try {
String fromServerUrl = clusterManager.getServerUrl(leadServer);
String fromServerUrl = clusterService.getServerUrl(leadServer);
WebTarget target = client.target(fromServerUrl).path("/~api/cluster/assets");
Invocation.Builder builder = target.request();
builder.header(AUTHORIZATION,
BEARER + " " + clusterManager.getCredential());
BEARER + " " + clusterService.getCredential());
try (Response response = builder.get()) {
checkStatus(response);
try (InputStream is = response.readEntity(InputStream.class)) {
try (InputStream is = response.readEntity(InputStream.class)) {
TarUtils.untar(is, getAssetsDir(), false);
} catch (IOException e) {
throw new RuntimeException(e);
@ -233,44 +236,44 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
client.close();
}
}
// workaround for issue https://bugs.eclipse.org/bugs/show_bug.cgi?id=566170
FileStoreAttributes.setBackground(true);
taskScheduler.start();
}
@Sessional
@Override
public void postStart() {
if (thread == null)
if (thread == null)
return;
SecurityUtils.bindAsSystem();
initStage = null;
listenerRegistry.post(new SystemStarted());
clusterManager.postStart();
clusterService.postStart();
thread.start();
SystemSetting systemSetting = settingManager.getSystemSetting();
SystemSetting systemSetting = settingService.getSystemSetting();
logger.info("Server is ready at " + systemSetting.getServerUrl() + ".");
}
@Override
public void preStop() {
thread = null;
clusterManager.preStop();
clusterService.preStop();
SecurityUtils.bindAsSystem();
try {
sessionManager.run(() -> listenerRegistry.post(new SystemStopping()));
sessionService.run(() -> listenerRegistry.post(new SystemStopping()));
} catch (ServerNotReadyException ignore) {
}
}
private List<ManualConfig> checkData() {
HazelcastInstance hazelcastInstance = clusterManager.getHazelcastInstance();
HazelcastInstance hazelcastInstance = clusterService.getHazelcastInstance();
var lock = hazelcastInstance.getCPSubsystem().getLock("checkData");
lock.lock();
try {
return dataManager.checkData();
return dataService.checkData();
} finally {
lock.unlock();
}
@ -284,10 +287,10 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
taskScheduler.stop();
jettyLauncherProvider.get().stop();
sessionManager.run(() -> listenerRegistry.post(new SystemStopped()));
sessionService.run(() -> listenerRegistry.post(new SystemStopped()));
sessionFactoryManager.stop();
clusterManager.stop();
sessionFactoryService.stop();
clusterService.stop();
executorService.shutdown();
} catch (ServerNotReadyException ignore) {
}
@ -306,11 +309,11 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
return null;
}
}
public static String getK8sService() {
return System.getenv("k8s_service");
}
public String guessServerUrl() {
String serviceHost = System.getenv("ONEDEV_SERVICE_HOST");
if (serviceHost != null) {
@ -331,40 +334,46 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
return UrlUtils.toString(serverUrl);
}
}
private Url buildServerUrl(String host, String protocol, int port) {
Url serverUrl = new Url(StandardCharsets.UTF_8);
serverUrl.setHost(host);
serverUrl.setProtocol(protocol);
serverUrl.setPort(port);
private Url buildServerUrl(String host, String protocol, int port) {
Url serverUrl = new Url(StandardCharsets.UTF_8);
serverUrl.setHost(host);
serverUrl.setProtocol(protocol);
serverUrl.setPort(port);
return serverUrl;
}
/**
* This method can be called from different UI threads, so we clone initStage to
* This method can be called from different UI threads, so we clone initStage to
* make it thread-safe.
* <p>
*
* @return
* cloned initStage, or <tt>null</tt> if system initialization is completed
* cloned initStage, or <tt>null</tt> if system initialization is
* completed
*/
public @Nullable InitStage getInitStage() {
return initStage;
}
public boolean isReady() {
return initStage == null;
}
public static OneDev getInstance() {
return AppLoader.getInstance(OneDev.class);
}
public static <T> T getInstance(Class<T> type) {
return AppLoader.getInstance(type);
}
public static <T> T getInstance(Class<T> type, Class<? extends Annotation> annotationClass) {
return AppLoader.getInstance(type, annotationClass);
}
public static <T> Set<T> getExtensions(Class<T> extensionPoint) {
return AppLoader.getExtensions(extensionPoint);
}
@ -375,8 +384,8 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
public Object writeReplace() throws ObjectStreamException {
return new ManagedSerializedForm(OneDev.class);
}
}
public static boolean isServerRunning(File installDir) {
var serverConfig = new ServerConfig(installDir);
try (ServerSocket ignored = new ServerSocket(serverConfig.getClusterPort())) {
@ -386,7 +395,7 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
return true;
else
throw new RuntimeException(e);
}
}
}
public static File getIndexDir() {
@ -394,15 +403,15 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
FileUtils.createDir(indexDir);
return indexDir;
}
public static File getAssetsDir() {
return new File(Bootstrap.getSiteDir(), "assets");
}
public static File getMaintenanceFile(File installDir) {
return new File(installDir, "maintenance");
}
private void restart() {
if (wrapperManagerClass != null) {
try {
@ -418,17 +427,17 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
@Override
public void run() {
var localServer = clusterManager.getLocalServerAddress();
var localServer = clusterService.getLocalServerAddress();
var maintenanceFile = getMaintenanceFile(Bootstrap.installDir);
while (thread != null) {
if (maintenanceFile.exists()) {
logger.info("Maintenance requested, trying to stop all servers...");
clusterManager.submitToAllServers(() -> {
if (!localServer.equals(clusterManager.getLocalServerAddress()))
clusterService.submitToAllServers(() -> {
if (!localServer.equals(clusterService.getLocalServerAddress()))
restart();
return null;
});
while (thread != null && clusterManager.getServerAddresses().size() != 1) {
while (thread != null && clusterService.getServerAddresses().size() != 1) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
@ -436,7 +445,7 @@ public class OneDev extends AbstractPlugin implements Serializable, Runnable {
}
restart();
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {

View File

@ -1,13 +1,16 @@
package io.onedev.server;
import io.onedev.server.annotation.NoDBAccess;
import java.io.File;
public interface StorageManager {
public interface StorageService {
File initLfsDir(Long projectId);
File initArtifactsDir(Long projectId, Long buildNumber);
@NoDBAccess
File initPacksDir(Long projectId);
}

View File

@ -1,11 +1,13 @@
package io.onedev.server;
import io.onedev.server.annotation.NoDBAccess;
import org.apache.wicket.Component;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
public interface SubscriptionManager {
public interface SubscriptionService {
@NoDBAccess
boolean isSubscriptionActive();
@Nullable

View File

@ -8,7 +8,7 @@ import java.util.concurrent.ExecutorService;
import io.onedev.agent.*;
import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.AgentManager;
import io.onedev.server.service.AgentService;
import org.apache.commons.lang3.SerializationUtils;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
@ -24,9 +24,9 @@ import io.onedev.commons.utils.StringUtils;
import io.onedev.commons.utils.TaskLogger;
import io.onedev.server.exception.ServerNotReadyException;
import io.onedev.server.job.JobContext;
import io.onedev.server.job.JobManager;
import io.onedev.server.job.JobService;
import io.onedev.server.job.ResourceAllocator;
import io.onedev.server.job.log.LogManager;
import io.onedev.server.job.log.LogService;
import io.onedev.server.terminal.AgentShell;
@WebSocket
@ -42,7 +42,7 @@ public class ServerSocket {
public void onClose(int statusCode, String reason) {
try {
if (agentId != null)
getAgentManager().agentDisconnected(agentId);
getAgentService().agentDisconnected(agentId);
StringBuilder builder = new StringBuilder("Websocket closed (");
if (session != null && session.getRemoteAddress() != null)
@ -69,15 +69,15 @@ public class ServerSocket {
}
}
private AgentManager getAgentManager() {
return OneDev.getInstance(AgentManager.class);
private AgentService getAgentService() {
return OneDev.getInstance(AgentService.class);
}
@OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
try {
new Message(MessageTypes.UPDATE, getAgentManager().getAgentVersion()).sendBy(session);
new Message(MessageTypes.UPDATE, getAgentService().getAgentVersion()).sendBy(session);
} catch (Exception e) {
logger.error("Error sending websocket message", e);
try {
@ -98,7 +98,7 @@ public class ServerSocket {
// be assigned via Administrator
AgentData data = SerializationUtils.deserialize(message.getData());
try {
agentId = getAgentManager().agentConnected(data, session);
agentId = getAgentService().agentConnected(data, session);
} catch (Exception e) {
var explicitException = ExceptionUtils.find(e, ExplicitException.class);
if (explicitException != null) {
@ -132,7 +132,7 @@ public class ServerSocket {
if (sessionId.length() == 0)
sessionId = null;
String logMessage = StringUtils.substringAfter(remaining, ":");
TaskLogger logger = OneDev.getInstance(LogManager.class).getJobLogger(jobToken);
TaskLogger logger = OneDev.getInstance(LogService.class).getJobLogger(jobToken);
if (logger != null)
logger.log(logMessage, sessionId);
} catch (Exception e) {
@ -143,15 +143,15 @@ public class ServerSocket {
String dataString = new String(messageData, StandardCharsets.UTF_8);
String jobToken = StringUtils.substringBefore(dataString, ":");
String jobWorkspace = StringUtils.substringAfter(dataString, ":");
JobContext jobContext = getJobManager().getJobContext(jobToken, false);
JobContext jobContext = getJobService().getJobContext(jobToken, false);
if (jobContext != null)
getJobManager().reportJobWorkspace(jobContext, jobWorkspace);
getJobService().reportJobWorkspace(jobContext, jobWorkspace);
break;
case SHELL_OUTPUT:
dataString = new String(messageData, StandardCharsets.UTF_8);
String sessionId = StringUtils.substringBefore(dataString, ":");
String output = StringUtils.substringAfter(dataString, ":");
AgentShell shell = (AgentShell) getJobManager().getShell(sessionId);
AgentShell shell = (AgentShell) getJobService().getShell(sessionId);
if (shell != null)
shell.getTerminal().sendOutput(output);
break;
@ -159,13 +159,13 @@ public class ServerSocket {
dataString = new String(messageData, StandardCharsets.UTF_8);
sessionId = StringUtils.substringBefore(dataString, ":");
String error = StringUtils.substringAfter(dataString, ":");
shell = (AgentShell) getJobManager().getShell(sessionId);
shell = (AgentShell) getJobService().getShell(sessionId);
if (shell != null)
shell.getTerminal().sendError(error);
break;
case SHELL_CLOSED:
sessionId = new String(messageData, StandardCharsets.UTF_8);
shell = (AgentShell) getJobManager().getShell(sessionId);
shell = (AgentShell) getJobService().getShell(sessionId);
if (shell != null)
shell.getTerminal().close();
break;
@ -180,8 +180,8 @@ public class ServerSocket {
}
}
private JobManager getJobManager() {
return OneDev.getInstance(JobManager.class);
private JobService getJobService() {
return OneDev.getInstance(JobService.class);
}
private Serializable service(Serializable request) {

View File

@ -3,7 +3,7 @@ package io.onedev.server.agent;
import io.onedev.agent.Agent;
import io.onedev.server.OneDev;
import io.onedev.server.exception.ServerNotReadyException;
import io.onedev.server.entitymanager.AgentTokenManager;
import io.onedev.server.service.AgentTokenService;
import io.onedev.server.security.SecurityUtils;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
@ -22,11 +22,11 @@ public class ServerSocketServlet extends WebSocketServlet {
private static final long serialVersionUID = 1L;
private final AgentTokenManager tokenManager;
private final AgentTokenService tokenService;
@Inject
public ServerSocketServlet(AgentTokenManager tokenManager) {
this.tokenManager = tokenManager;
public ServerSocketServlet(AgentTokenService tokenService) {
this.tokenService = tokenService;
}
@Override
@ -43,7 +43,7 @@ public class ServerSocketServlet extends WebSocketServlet {
if (!OneDev.getInstance().isReady())
throw new ServerNotReadyException();
String tokenValue = SecurityUtils.getBearerToken(request);
if (tokenValue != null && tokenManager.find(tokenValue) != null)
if (tokenValue != null && tokenService.find(tokenValue) != null)
super.service(request, response);
else
response.sendError(SC_FORBIDDEN, "A valid agent token is expected");

View File

@ -0,0 +1,90 @@
package io.onedev.server.ai;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.onedev.server.OneDev;
import io.onedev.server.model.Issue;
import io.onedev.server.model.IssueComment;
import io.onedev.server.model.Project;
import io.onedev.server.web.UrlService;
public class IssueHelper {
private static ObjectMapper getObjectMapper() {
return OneDev.getInstance(ObjectMapper.class);
}
private static UrlService getUrlService() {
return OneDev.getInstance(UrlService.class);
}
public static Map<String, Object> getSummary(Project currentProject, Issue issue) {
var typeReference = new TypeReference<LinkedHashMap<String, Object>>() {};
var summary = getObjectMapper().convertValue(issue, typeReference);
summary.remove("id");
summary.remove("stateOrdinal");
summary.remove("uuid");
summary.remove("messageId");
summary.remove("pinDate");
summary.remove("boardPosition");
summary.remove("numberScopeId");
summary.put("reference", issue.getReference().toString(currentProject));
summary.remove("submitterId");
summary.put("submitter", issue.getSubmitter().getName());
summary.put("Project", issue.getProject().getPath());
summary.remove("lastActivity");
for (var it = summary.entrySet().iterator(); it.hasNext();) {
var entry = it.next();
if (entry.getKey().endsWith("Count"))
it.remove();
}
return summary;
}
public static List<Map<String, Object>> getComments(Issue issue) {
var comments = new ArrayList<Map<String, Object>>();
issue.getComments().stream().sorted(Comparator.comparing(IssueComment::getId)).forEach(comment -> {
var commentMap = new HashMap<String, Object>();
commentMap.put("user", comment.getUser().getName());
commentMap.put("date", comment.getDate());
commentMap.put("content", comment.getContent());
comments.add(commentMap);
});
return comments;
}
public static Map<String, Object> getDetail(Project currentProject, Issue issue) {
var detail = getSummary(currentProject, issue);
for (var entry : issue.getFieldInputs().entrySet()) {
detail.put(entry.getKey(), entry.getValue().getValues());
}
Map<String, Collection<String>> linkedIssues = new HashMap<>();
for (var link: issue.getTargetLinks()) {
linkedIssues.computeIfAbsent(link.getSpec().getName(), k -> new ArrayList<>())
.add(link.getTarget().getReference().toString(currentProject));
}
for (var link : issue.getSourceLinks()) {
if (link.getSpec().getOpposite() != null) {
linkedIssues.computeIfAbsent(link.getSpec().getOpposite().getName(), k -> new ArrayList<>())
.add(link.getSource().getReference().toString(currentProject));
} else {
linkedIssues.computeIfAbsent(link.getSpec().getName(), k -> new ArrayList<>())
.add(link.getSource().getReference().toString(currentProject));
}
}
detail.putAll(linkedIssues);
detail.put("link", getUrlService().urlFor(issue, true));
return detail;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
package io.onedev.server.ai;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.onedev.server.OneDev;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.PullRequestComment;
import io.onedev.server.model.PullRequestReview;
import io.onedev.server.web.UrlService;
public class PullRequestHelper {
private static ObjectMapper getObjectMapper() {
return OneDev.getInstance(ObjectMapper.class);
}
private static UrlService getUrlService() {
return OneDev.getInstance(UrlService.class);
}
public static Map<String, Object> getSummary(Project currentProject,
PullRequest pullRequest, boolean checkMergeConditionIfOpen) {
var typeReference = new TypeReference<LinkedHashMap<String, Object>>() {};
var summary = getObjectMapper().convertValue(pullRequest, typeReference);
summary.remove("id");
if (pullRequest.isOpen() && checkMergeConditionIfOpen) {
var errorMessage = pullRequest.checkMergeCondition();
if (errorMessage != null)
summary.put("status", PullRequest.Status.OPEN.name() + " (" + errorMessage + ")");
else
summary.put("status", PullRequest.Status.OPEN.name() + " (ready to merge)");
}
summary.remove("uuid");
summary.remove("buildCommitHash");
summary.remove("submitTimeGroups");
summary.remove("closeTimeGroups");
summary.remove("checkError");
summary.remove("numberScopeId");
summary.put("reference", pullRequest.getReference().toString(currentProject));
summary.remove("submitterId");
summary.put("submitter", pullRequest.getSubmitter().getName());
summary.put("targetProject", pullRequest.getTarget().getProject().getPath());
if (pullRequest.getSourceProject() != null)
summary.put("sourceProject", pullRequest.getSourceProject().getPath());
summary.remove("codeCommentsUpdateDate");
summary.remove("lastActivity");
for (var it = summary.entrySet().iterator(); it.hasNext();) {
var entry = it.next();
if (entry.getKey().endsWith("Count"))
it.remove();
}
return summary;
}
public static Map<String, Object> getDetail(Project currentProject, PullRequest pullRequest) {
var detail = getSummary(currentProject, pullRequest, true);
detail.put("headCommitHash", pullRequest.getLatestUpdate().getHeadCommitHash());
detail.put("assignees", pullRequest.getAssignees().stream().map(it->it.getName()).collect(Collectors.toList()));
var reviews = new ArrayList<Map<String, Object>>();
for (var review : pullRequest.getReviews()) {
if (review.getStatus() == PullRequestReview.Status.EXCLUDED)
continue;
var reviewMap = new HashMap<String, Object>();
reviewMap.put("reviewer", review.getUser().getName());
reviewMap.put("status", review.getStatus());
reviews.add(reviewMap);
}
detail.put("reviews", reviews);
var builds = new ArrayList<String>();
for (var build : pullRequest.getBuilds()) {
builds.add(build.getReference().toString(currentProject) + " (job: " + build.getJobName() + ", status: " + build.getStatus() + ")");
}
detail.put("builds", builds);
detail.put("labels", pullRequest.getLabels().stream().map(it->it.getSpec().getName()).collect(Collectors.toList()));
detail.put("link", getUrlService().urlFor(pullRequest, true));
return detail;
}
public static List<Map<String, Object>> getComments(PullRequest pullRequest) {
var comments = new ArrayList<Map<String, Object>>();
pullRequest.getComments().stream().sorted(Comparator.comparing(PullRequestComment::getId)).forEach(comment -> {
var commentMap = new HashMap<String, Object>();
commentMap.put("user", comment.getUser().getName());
commentMap.put("date", comment.getDate());
commentMap.put("content", comment.getContent());
comments.add(commentMap);
});
return comments;
}
}

View File

@ -0,0 +1,639 @@
package io.onedev.server.ai;
import static java.util.stream.Collectors.joining;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import io.onedev.server.OneDev;
import io.onedev.server.model.Agent;
import io.onedev.server.model.Build;
import io.onedev.server.model.Issue;
import io.onedev.server.model.LabelSpec;
import io.onedev.server.model.Pack;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.support.issue.field.spec.BooleanField;
import io.onedev.server.model.support.issue.field.spec.BuildChoiceField;
import io.onedev.server.model.support.issue.field.spec.CommitField;
import io.onedev.server.model.support.issue.field.spec.DateField;
import io.onedev.server.model.support.issue.field.spec.DateTimeField;
import io.onedev.server.model.support.issue.field.spec.FloatField;
import io.onedev.server.model.support.issue.field.spec.GroupChoiceField;
import io.onedev.server.model.support.issue.field.spec.IntegerField;
import io.onedev.server.model.support.issue.field.spec.IssueChoiceField;
import io.onedev.server.model.support.issue.field.spec.IterationChoiceField;
import io.onedev.server.model.support.issue.field.spec.PullRequestChoiceField;
import io.onedev.server.model.support.issue.field.spec.TextField;
import io.onedev.server.model.support.issue.field.spec.choicefield.ChoiceField;
import io.onedev.server.model.support.issue.field.spec.userchoicefield.UserChoiceField;
import io.onedev.server.model.support.pullrequest.MergeStrategy;
import io.onedev.server.pack.PackSupport;
import io.onedev.server.service.AgentAttributeService;
import io.onedev.server.service.AgentService;
import io.onedev.server.service.LabelSpecService;
import io.onedev.server.service.LinkSpecService;
import io.onedev.server.service.SettingService;
public class QueryDescriptions {
private static String REACTION_CRITERIAS = """
| '"Reaction: Thumbs Up Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Thumbs Up Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Thumbs Down Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Thumbs Down Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Smile Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Smile Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Tada Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Tada Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Confused Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Confused Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Heart Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Heart Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Rocket Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Rocket Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Reaction: Eyes Count"' 'is' ('not')? '"'Number'"'
| '"Reaction: Eyes Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
""".trim();
private static SettingService getSettingService() {
return OneDev.getInstance(SettingService.class);
}
private static LinkSpecService getLinkSpecService() {
return OneDev.getInstance(LinkSpecService.class);
}
public static String getIssueQueryDescription() {
var settingService = getSettingService();
var linkSpecService = getLinkSpecService();
var fieldCriterias = new ArrayList<String>();
var choiceFieldValueRules = new ArrayList<String>();
int choiceFieldValueRuleIndex = 0;
for (var field: settingService.getIssueSetting().getFieldSpecs()) {
if (field instanceof ChoiceField) {
var choiceField = (ChoiceField) field;
var fieldValueRuleName = "ChoiceFieldValue" + (choiceFieldValueRuleIndex++);
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'" + fieldValueRuleName + "'\"'");
choiceFieldValueRules.add(choiceField.getPossibleValues().stream().map(it->"'" + it.replace("'", "\\'") + "'").collect(joining("\n | ")));
} else if (field instanceof UserChoiceField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'LoginNameOfUser'\"'");
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? 'me'");
} else if (field instanceof GroupChoiceField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'GroupName'\"'");
} else if (field instanceof BooleanField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'('true'|'false')'\"'");
} else if (field instanceof DateField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('before'|'after') '\"'DateDescription'\"'");
} else if (field instanceof DateTimeField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('before'|'after') '\"'DateTimeDescription'\"'");
} else if (field instanceof IntegerField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'Integer'\"'");
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('greater'|'less') 'than' '\"'Integer'\"'");
} else if (field instanceof FloatField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('greater'|'less') 'than' '\"'Float'\"'");
} else if (field instanceof TextField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'Text'\"'");
fieldCriterias.add("'\"" + field.getName() + "\"' 'contains' '\"'Text'\"'");
} else if (field instanceof PullRequestChoiceField || field instanceof BuildChoiceField || field instanceof IssueChoiceField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'EntityReference'\"'");
} else if (field instanceof CommitField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'CommitReference'\"'");
} else if (field instanceof IterationChoiceField) {
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'IterationNameOrPattern'\"'");
}
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? 'empty'");
}
var linkCriterias = new ArrayList<String>();
for (var linkSpec: linkSpecService.query()) {
linkCriterias.add("'any' '\"" + linkSpec.getName() + "\"' 'matching' '('criteria')'");
linkCriterias.add("'all' '\"" + linkSpec.getName() + "\"' 'matching' '('criteria')'");
linkCriterias.add("'has any' '\"" + linkSpec.getName() + "\"'");
if (linkSpec.getOpposite() != null) {
linkCriterias.add("'any' '\"" + linkSpec.getOpposite().getName() + "\"' 'matching' '('criteria')'");
linkCriterias.add("'all' '\"" + linkSpec.getOpposite().getName() + "\"' 'matching' '('criteria')'");
linkCriterias.add("'has any' '\"" + linkSpec.getOpposite().getName() + "\"'");
}
}
var description = String.format("""
A structured query should conform to below ANTLR grammar:
issueQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: '"Number"' 'is' ('not')? '"'EntityReference'"'
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
| '"State"' 'is' ('not')? '"'StateName'"'
| '"State"' 'is' ('after'|'before') '"'StateName'"'
%s
%s
| 'submitted by' '"'LoginNameOfUser'"'
| 'submitted by me'
| 'watched by' '"'LoginNameOfUser'"'
| 'watched by me'
| 'ignored by' '"'LoginNameOfUser'"'
| 'ignored by me'
| 'commented by' '"'LoginNameOfUser'"'
| 'commented by me'
| 'mentioned' '"'LoginNameOfUser'"'
| 'mentioned me'
| 'fixed in commit' '"'CommitReference'"'
| 'fixed in current commit'
| 'fixed in build' '"'EntityReference'"'
| 'fixed in current build'
| 'fixed in pull request' '"'EntityReference'"'
| 'fixed in current pull request'
| 'fixed between' revision 'and' revision
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
| 'confidential'
| '"Spent Time"' 'is' ('greater'|'less') 'than' '"'TimePeriodDescription'"'
| '"Spent Time"' 'is' ('not')? '"'TimePeriodDescription'"'
| '"Estimated Time"' 'is' ('greater'|'less') 'than' '"'TimePeriodDescription'"'
| '"Estimated Time"' 'is' ('not')? '"'TimePeriodDescription'"'
| '"Progress"' 'is' ('greater'|'less') 'than' '"'Float'"'
| '"Iteration"' 'is' ('not')? '"'IterationNameOrPattern'"'
| '"Iteration"' 'is' ('not')? 'empty'
| '"Title"' 'contains' '"'Text'"'
| '"Description"' 'contains' '"'Text'"'
| '"Comment"' 'contains' '"'Text'"'
| '"Comment Count"' 'is' ('not')? '"'Number'"'
| '"Comment Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Vote Count"' 'is' ('not')? '"'Number'"'
| '"Vote Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
%s
| '"Project"' 'is current'
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
StateName
: %s
;
%s
revision
: 'commit' '"'CommitReference'"'
| 'build' '"'EntityReference'"'
| 'branch' '"'BranchReference'"'
| 'tag' '"'TagReference'"'
;
EntityReference
: '#'Number
| ProjectPath'#'Number
| ProjectKey'-'Number
;
CommitReference
: (ProjectPath':')?CommitHash
;
BranchReference
: (ProjectPath':')?BranchName
;
TagReference
: (ProjectPath':')?TagName
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible issues""",
fieldCriterias.stream().map(it->" | " + it + "\n").collect(joining("")).trim(),
linkCriterias.stream().map(it->" | " + it + "\n").collect(joining("")).trim(),
REACTION_CRITERIAS,
settingService.getIssueSetting().getStateSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
IntStream.range(0, choiceFieldValueRules.size()).mapToObj(i -> "ChoiceFieldValue" + i + "\n : " + choiceFieldValueRules.get(i) + "\n ;\n\n").collect(joining("")).trim(),
Issue.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
public static String getPullRequestQueryDescription() {
var description = String.format("""
A structured query should conform to below ANTLR grammar:
pullRequestQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: '"Number"' 'is' ('not')? '"'EntityReference'"'
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
| 'open'
| 'merged'
| 'discarded'
| '"Source Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
| '"Souce Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| '"Target Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
| '"Merge Strategy"' 'is' ('not')? '"'MergeStrategy'"'
| '"Label"' 'is' ('not')? '"'LabelName'"'
| 'ready to merge'
| 'has pending reviews'
| 'has unsuccessful builds'
| 'has unfinished builds'
| 'has merge conflicts'
| 'assigned to' '"'LoginNameOfUser'"'
| 'approved by' '"'LoginNameOfUser'"'
| 'to be reviewed by' '"'LoginNameOfUser'"'
| 'to be changed by' '"'LoginNameOfUser'"'
| 'to be merged by' '"'LoginNameOfUser'"'
| 'requested for changes by' '"'LoginNameOfUser'"'
| 'need action of' '"'LoginNameOfUser'"'
| 'assigned to me'
| 'approved by me'
| 'to be reviewed by me'
| 'to be changed by me'
| 'to be merged by me'
| 'requested for changes by me'
| 'someone requested for changes'
| 'mentioned' '"'LoginNameOfUser'"'
| 'mentioned me'
| 'need action of' '"'LoginNameOfUser'"'
| 'need my action'
| 'submitted by' '"'LoginNameOfUser'"'
| 'submitted by me'
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Close Date"' 'is' ('until'|'since') '"'DateDescription'"'
| 'includes issue' '"'EntityReference'"'
| 'includes commit' '"'CommitReference'"'
| '"Title"' 'contains' '"'Text'"'
| '"Description"' 'contains' '"'Text'"'
| '"Comment"' 'contains' '"'Text'"'
| '"Comment Count"' 'is' ('not')? '"'Number'"'
| '"Comment Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
%s
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| 'watched by' '"'LoginNameOfUser'"'
| 'watched by me'
| 'ignored by' '"'LoginNameOfUser'"'
| 'ignored by me'
| 'commented by' '"'LoginNameOfUser'"'
| 'commented by me'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
EntityReference
: '#'Number
| ProjectPath'#'Number
| ProjectKey'-'Number
;
CommitReference
: (ProjectPath':')?CommitHash
;
BranchReference
: (ProjectPath':')?BranchName
;
TagReference
: (ProjectPath':')?TagName
;
MergeStrategy
: %s
;
LabelName
: %s
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible pull requests""",
REACTION_CRITERIAS,
Arrays.stream(MergeStrategy.values()).map(it->"'" + it.name() + "'").collect(joining("\n | ")).trim(),
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
PullRequest.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
public static String getBuildQueryDescription() {
var description = String.format("""
A structured query should conform to below ANTLR grammar:
buildQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: '"Number"' 'is' ('not')? '"'EntityReference'"'
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
| 'sucessful'
| 'failed'
| 'cancelled'
| 'timed out'
| 'finished'
| 'running'
| 'waiting'
| 'pending'
| 'submitted by' '"'LoginNameOfUser'"'
| 'submitted by me'
| 'cancelled by' '"'LoginNameOfUser'"'
| 'cancelled by me'
| 'depends on' '"'EntityReference'"'
| 'dependencies of' '"'EntityReference'"'
| 'ran on' '"'AgentName'"'
| 'fixed issue' '"'EntityReference'"'
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| '"Job"' 'is' ('not')? '"'JobNameOrPattern'"'
| '"Version"' 'is' ('not')? '"'VersionNameOrPattern'"'
| '"Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
| '"Tag"' 'is' ('not')? '"'TagNameOrPattern'"'
| '"Param"' 'is' ('not')? '"'ParamName'"'
| '"Label"' 'is' ('not')? '"'LabelName'"'
| '"Pull Request"' 'is' ('not')? '"'EntityReference'"'
| '"Commit"' 'is' ('not')? '"'CommitReference'"'
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Pending Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Running Date"' 'is' ('until'|'since') '"'DateDescription'"'
| '"Finish Date"' 'is' ('until'|'since') '"'DateDescription'"'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
EntityReference
: '#'Number
| ProjectPath'#'Number
| ProjectKey'-'Number
;
CommitReference
: (ProjectPath':')?CommitHash
;
LabelName
: %s
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible builds""",
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
Build.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
private static List<LabelSpec> getLabelSpecs() {
return OneDev.getInstance(LabelSpecService.class).query();
}
public static String getPackQueryDescription() {
var packSupports = new ArrayList<>(OneDev.getExtensions(PackSupport.class));
var description = String.format("""
A structured query should conform to below ANTLR grammar:
packQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: 'published by me'
| 'published by user' '"'LoginNameOfUser'"'
| 'published by build' '"'EntityReference'"'
| 'published by project' '"'ProjectPathOrPattern'"'
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| '"Type"' 'is' ('not')? '"'PackType'"'
| '"Name"' 'is' ('not')? '"'PackName'"'
| '"Version"' 'is' ('not')? '"'PackVersion'"'
| '"Label"' 'is' ('not')? '"'LabelName'"'
| '"Publish Date"' 'is' ('until'|'since') '"'DateDescription'"'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
EntityReference
: '#'Number
| ProjectPath'#'Number
| ProjectKey'-'Number
;
PackType
: %s
;
LabelName
: %s
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible packages""",
packSupports.stream().map(it->"'" + it.getPackType() + "'").collect(joining("\n | ")).trim(),
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
Pack.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
public static String getCommitQueryDescription() {
var description = """
A structured query should conform to below ANTLR grammar:
commitQuery
: criteria+
;
criteria
: ('before('|'after(') DateDescription ')'
| 'committer(' CommitterNameAndEmail ')' // committer is specified user
| 'author(' AuthorNameAndEmail ')' // author is specified user
| 'message(' Text ')' // commit message contains specified text
| 'path(' FilePath ')' // commit touches specified file
| 'authored-by-me'
| 'committed-by-me'
| ('until')? revision // until specified revision
| 'since' revision // since specified revision
;
revision
: 'commit(' CommitHash ')'
| 'build(' '#'Number ')'
| 'branch(' BranchName ')'
| 'tag(' TagName ')'
| 'default-branch'
;
WS
: [ ]+ -> skip
;
""";
return description;
}
public static String getProjectQueryDescription() {
var description = String.format("""
A structured query should conform to below ANTLR grammar:
projectQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: 'owned by' '"'LoginNameOfUser'"'
| 'owned by me'
| 'owned by none'
| 'has outdated replicas'
| 'without enough replicas'
| 'missing storage'
| 'children of' '"'ProjectPathOrPattern'"'
| 'forks of' '"'ProjectPathOrPattern'"'
| 'roots'
| 'leafs'
| 'fork roots'
| '"Name"' 'is' ('not')? '"'ProjectNameOrPattern'"'
| '"Key"' 'is' ('not')? '"'ProjectKeyOrPattern'"'
| '"Path"' 'is' ('not')? '"'ProjectPathOrPattern'"'
| '"Label"' 'is' ('not')? '"'LabelName'"'
| '"Description"' 'contains' '"'Text'"'
| '"Id"' 'is' ('not')? '"'Number'"'
| '"Id"' 'is' ('greater'|'less') 'than' '"'Number'"'
| '"Service Desk Email Address"' 'is' ('not')? '"'EmailAddressOrPattern'"'
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
LabelName
: %s
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible projects""",
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
Project.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
public static String getAgentQueryDescription() {
var agentService = OneDev.getInstance(AgentService.class);
var attributeService = OneDev.getInstance(AgentAttributeService.class);
var description = String.format("""
A structured query should conform to below ANTLR grammar:
agentQuery
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
;
criteria
: 'online'
| 'offline'
| 'paused'
| 'has running builds'
| 'has attribute' '"'AttributeName'"'
| 'not used since' '"'DateDescription'"'
| 'ever used since' '"'DateDescription'"'
| 'ran build' '"'EntityReference'"'
| '"Name"' 'is' ('not')? '"'AgentNameOrPattern'"'
| '"Ip Address"' 'is' ('not')? '"'IPAddressOrPattern'"'
| '"Os"' 'is' ('not')? '"'(OsName|OsNamePattern)'"'
| '"Os Arch"' 'is' ('not')? '"'(OsArch|OsArchPattern)'"'
| '"Os Version"' 'is' ('not')? '"'OsVersionOrPattern'"'
| '"'AttributeName'"' 'is' ('not')? '"'AttributeValue'"'
| criteria 'and' criteria
| criteria 'or' criteria
| 'not('criteria')'
| '('criteria')'
;
OsName
: %s
;
OsArch
: %s
;
AttributeName
: %s
;
OrderField
: %s
;
WS
: [ ]+ -> skip
;
Please note:
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
2. Use an empty query to list all accessible agents""",
agentService.getOsNames().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
agentService.getOsArchs().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
attributeService.getAttributeNames().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
Agent.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
return description;
}
}

View File

@ -0,0 +1,18 @@
package io.onedev.server.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DependsOn {
String property();
String value() default "";
boolean inverse() default false;
}

View File

@ -0,0 +1,4 @@
package io.onedev.server.annotation;
public @interface NoDBAccess {
}

View File

@ -0,0 +1,12 @@
package io.onedev.server.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.google.inject.BindingAnnotation;
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface Shallow {
}

View File

@ -6,7 +6,7 @@ import java.util.List;
import io.onedev.server.util.artifact.FileInfo;
public interface AttachmentManager {
public interface AttachmentService {
File getAttachmentGroupDir(Long projectId, String attachmentGroup);

View File

@ -45,12 +45,12 @@ import io.onedev.commons.utils.FileUtils;
import io.onedev.commons.utils.StringUtils;
import io.onedev.commons.utils.TarUtils;
import io.onedev.k8shelper.KubernetesHelper;
import io.onedev.server.cluster.ClusterManager;
import io.onedev.server.cluster.ClusterService;
import io.onedev.server.cluster.ClusterRunnable;
import io.onedev.server.cluster.ClusterTask;
import io.onedev.server.entitymanager.IssueManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.service.IssueService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.SettingService;
import io.onedev.server.event.Listen;
import io.onedev.server.event.entity.EntityRemoved;
import io.onedev.server.event.project.build.BuildSubmitted;
@ -74,7 +74,7 @@ import io.onedev.server.event.system.SystemStopping;
import io.onedev.server.model.Issue;
import io.onedev.server.model.support.issue.changedata.IssueDescriptionChangeData;
import io.onedev.server.model.support.pullrequest.changedata.PullRequestDescriptionChangeData;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.TransactionService;
import io.onedev.server.persistence.annotation.Sessional;
import io.onedev.server.persistence.annotation.Transactional;
import io.onedev.server.persistence.dao.Dao;
@ -82,14 +82,14 @@ import io.onedev.server.taskschedule.SchedulableTask;
import io.onedev.server.taskschedule.TaskScheduler;
import io.onedev.server.util.IOUtils;
import io.onedev.server.util.artifact.FileInfo;
import io.onedev.server.util.concurrent.BatchWorkManager;
import io.onedev.server.util.concurrent.BatchWorkExecutionService;
import io.onedev.server.util.concurrent.BatchWorker;
import io.onedev.server.util.concurrent.Prioritized;
@Singleton
public class DefaultAttachmentManager implements AttachmentManager, SchedulableTask, Serializable {
public class DefaultAttachmentService implements AttachmentService, SchedulableTask, Serializable {
private static final Logger logger = LoggerFactory.getLogger(DefaultAttachmentManager.class);
private static final Logger logger = LoggerFactory.getLogger(DefaultAttachmentService.class);
private static final long TEMP_PRESERVE_PERIOD = 24*3600*1000L;
@ -98,47 +98,40 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
private static final String PERMANENT = "permanent";
private static final String TEMP = "temp";
private final Dao dao;
private final TransactionManager transactionManager;
private final TaskScheduler taskScheduler;
private final ProjectManager projectManager;
private final ClusterManager clusterManager;
private final SettingManager settingManager;
private final IssueManager issueManager;
private final BatchWorkManager batchWorkManager;
private String taskId;
@Inject
public DefaultAttachmentManager(Dao dao, TransactionManager transactionManager,
TaskScheduler taskScheduler, SettingManager settingManager,
ProjectManager projectManager, ClusterManager clusterManager,
IssueManager issueManager, BatchWorkManager batchWorkManager) {
this.dao = dao;
this.transactionManager = transactionManager;
this.taskScheduler = taskScheduler;
this.settingManager = settingManager;
this.projectManager = projectManager;
this.clusterManager = clusterManager;
this.issueManager = issueManager;
this.batchWorkManager = batchWorkManager;
}
private Dao dao;
@Inject
private TransactionService transactionService;
@Inject
private TaskScheduler taskScheduler;
@Inject
private ProjectService projectService;
@Inject
private ClusterService clusterService;
@Inject
private SettingService settingService;
@Inject
private IssueService issueService;
@Inject
private BatchWorkExecutionService batchWorkExecutionService;
private volatile String taskId;
public Object writeReplace() throws ObjectStreamException {
return new ManagedSerializedForm(AttachmentManager.class);
return new ManagedSerializedForm(AttachmentService.class);
}
@Override
public File getAttachmentGroupDir(Long projectId, String attachmentGroup) {
File baseDir = projectManager.getAttachmentDir(projectId);
File baseDir = projectService.getAttachmentDir(projectId);
File groupDir = getPermanentAttachmentGroupDir(baseDir, attachmentGroup);
if (groupDir.exists())
return groupDir;
@ -151,11 +144,11 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
public void on(IssuesMoved event) {
Long sourceProjectId = event.getSourceProject().getId();
Long targetProjectId = event.getProject().getId();
File targetBaseDir = projectManager.getAttachmentDir(targetProjectId);
File targetBaseDir = projectService.getAttachmentDir(targetProjectId);
String sourceActiveServer = projectManager.getActiveServer(sourceProjectId, true);
if (sourceActiveServer.equals(clusterManager.getLocalServerAddress())) {
File sourceBaseDir = projectManager.getAttachmentDir(sourceProjectId);
String sourceActiveServer = projectService.getActiveServer(sourceProjectId, true);
if (sourceActiveServer.equals(clusterService.getLocalServerAddress())) {
File sourceBaseDir = projectService.getAttachmentDir(sourceProjectId);
for (Long issueId: event.getIssueIds()) {
Issue issue = dao.load(Issue.class, issueId);
String attachmentGroup = issue.getAttachmentGroup();
@ -184,8 +177,8 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
} finally {
FileUtils.deleteDir(tempGroupDir);
}
projectManager.directoryModified(targetProjectId, sourceGroupDir.getParentFile());
projectManager.directoryModified(targetProjectId, targetGroupDir.getParentFile());
projectService.directoryModified(targetProjectId, sourceGroupDir.getParentFile());
projectService.directoryModified(targetProjectId, targetGroupDir.getParentFile());
}
} else {
Collection<String> attachmentGroups = new HashSet<>();
@ -211,22 +204,22 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
} finally {
FileUtils.deleteDir(tempGroupDir);
}
projectManager.directoryModified(targetProjectId, targetGroupDir.getParentFile());
projectService.directoryModified(targetProjectId, targetGroupDir.getParentFile());
}
projectManager.runOnActiveServer(sourceProjectId, new ClusterTask<Void>() {
projectService.runOnActiveServer(sourceProjectId, new ClusterTask<Void>() {
private static final long serialVersionUID = 1L;
@Override
public Void call() {
File sourceBaseDir = projectManager.getAttachmentDir(sourceProjectId);
File sourceBaseDir = projectService.getAttachmentDir(sourceProjectId);
for (var attachmentGroup: attachmentGroups) {
var sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, attachmentGroup);
write(getAttachmentLockName(sourceProjectId, attachmentGroup), () -> {
FileUtils.deleteDir(sourceGroupDir);
return null;
});
projectManager.directoryModified(sourceProjectId, sourceGroupDir.getParentFile());
projectService.directoryModified(sourceProjectId, sourceGroupDir.getParentFile());
}
return null;
}
@ -241,11 +234,11 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
Long sourceProjectId = event.getSourceProject().getId();
Long targetProjectId = event.getProject().getId();
File targetBaseDir = projectManager.getAttachmentDir(targetProjectId);
File targetBaseDir = projectService.getAttachmentDir(targetProjectId);
String sourceActiveServer = projectManager.getActiveServer(sourceProjectId, true);
if (sourceActiveServer.equals(clusterManager.getLocalServerAddress())) {
File sourceBaseDir = projectManager.getAttachmentDir(sourceProjectId);
String sourceActiveServer = projectService.getActiveServer(sourceProjectId, true);
if (sourceActiveServer.equals(clusterService.getLocalServerAddress())) {
File sourceBaseDir = projectService.getAttachmentDir(sourceProjectId);
for (var entry: event.getIssueIdMapping().entrySet()) {
Issue sourceIssue = dao.load(Issue.class, entry.getKey());
Issue targetIssue = dao.load(Issue.class, entry.getValue());
@ -273,7 +266,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
} finally {
FileUtils.deleteDir(tempGroupDir);
}
projectManager.directoryModified(targetProjectId, targetGroupDir.getParentFile());
projectService.directoryModified(targetProjectId, targetGroupDir.getParentFile());
}
} else {
for (var entry: event.getIssueIdMapping().entrySet()) {
@ -296,7 +289,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
} finally {
FileUtils.deleteDir(tempGroupDir);
}
projectManager.directoryModified(targetProjectId, targetGroupDir.getParentFile());
projectService.directoryModified(targetProjectId, targetGroupDir.getParentFile());
}
}
@ -306,13 +299,13 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
Long sourceProjectId, String sourceAttachmentGroup) {
Client client = ClientBuilder.newClient();
try {
String fromServerUrl = clusterManager.getServerUrl(sourceActiveServer);
String fromServerUrl = clusterService.getServerUrl(sourceActiveServer);
WebTarget target = client.target(fromServerUrl).path("/~api/cluster/attachments")
.queryParam("projectId", sourceProjectId)
.queryParam("attachmentGroup", sourceAttachmentGroup);
Invocation.Builder builder = target.request();
builder.header(AUTHORIZATION,
BEARER + " " + clusterManager.getCredential());
BEARER + " " + clusterService.getCredential());
try (Response response = builder.get()) {
KubernetesHelper.checkStatus(response);
@ -333,7 +326,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
}
private void permanentizeAttachmentGroup(Long projectId, String attachmentGroup) {
var baseAttachmentDir = projectManager.getAttachmentDir(projectId);
var baseAttachmentDir = projectService.getAttachmentDir(projectId);
write(getAttachmentLockName(projectId, attachmentGroup), () -> {
File permanentStorage = getPermanentAttachmentGroupDir(baseAttachmentDir, attachmentGroup);
if (!permanentStorage.exists()) {
@ -347,7 +340,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
} else {
FileUtils.createDir(permanentStorage);
}
projectManager.directoryModified(projectId, permanentStorage);
projectService.directoryModified(projectId, permanentStorage);
}
return null;
});
@ -366,13 +359,13 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public void execute() {
batchWorkManager.submit(new BatchWorker("attachment-manager-house-keeping") {
batchWorkExecutionService.submit(new BatchWorker("attachment-manager-house-keeping") {
@Override
public void doWorks(List<Prioritized> works) {
for (var projectId: projectManager.getActiveIds()) {
for (var projectId: projectService.getActiveIds()) {
try {
File tempAttachmentBase = new File(projectManager.getAttachmentDir(projectId), TEMP);
File tempAttachmentBase = new File(projectService.getAttachmentDir(projectId), TEMP);
if (tempAttachmentBase.exists()) {
for (File attachmentGroupDir: tempAttachmentBase.listFiles()) {
if (System.currentTimeMillis() - attachmentGroupDir.lastModified() > TEMP_PRESERVE_PERIOD) {
@ -398,7 +391,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Sessional
public void on(IssuesImported event) {
for (var issueId: event.getIssueIds()) {
var issue = issueManager.load(issueId);
var issue = issueService.load(issueId);
var attachmentGroup = issue.getAttachmentGroup();
permanentizeAttachmentGroup(issue.getProject().getId(), attachmentGroup);
}
@ -505,15 +498,15 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
if (event.getEntity() instanceof AttachmentStorageSupport) {
AttachmentStorageSupport storageSupport = (AttachmentStorageSupport) event.getEntity();
Long projectId = storageSupport.getAttachmentProject().getId();
String activeServer = projectManager.getActiveServer(projectId, false);
String activeServer = projectService.getActiveServer(projectId, false);
if (activeServer != null) {
transactionManager.runAfterCommit(new ClusterRunnable() {
transactionService.runAfterCommit(new ClusterRunnable() {
private static final long serialVersionUID = 1L;
@Override
public void run() {
clusterManager.runOnServer(activeServer, new ClusterTask<Void>() {
clusterService.runOnServer(activeServer, new ClusterTask<Void>() {
private static final long serialVersionUID = 1L;
@ -522,10 +515,10 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
var attachmentGroup = storageSupport.getAttachmentGroup();
return write(getAttachmentLockName(projectId, attachmentGroup), () -> {
File attachmentDir = getPermanentAttachmentGroupDir(
projectManager.getAttachmentDir(storageSupport.getAttachmentProject().getId()),
projectService.getAttachmentDir(storageSupport.getAttachmentProject().getId()),
attachmentGroup);
FileUtils.deleteDir(attachmentDir);
projectManager.directoryModified(projectId, attachmentDir.getParentFile());
projectService.directoryModified(projectId, attachmentDir.getParentFile());
return null;
});
}
@ -541,21 +534,21 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public String saveAttachment(Long projectId, String attachmentGroup, String preferredAttachmentName,
InputStream attachmentStream) {
String activeServer = projectManager.getActiveServer(projectId, true);
if (activeServer.equals(clusterManager.getLocalServerAddress())) {
String activeServer = projectService.getActiveServer(projectId, true);
if (activeServer.equals(clusterService.getLocalServerAddress())) {
return saveAttachmentLocal(projectId, attachmentGroup, preferredAttachmentName, attachmentStream);
} else {
Client client = ClientBuilder.newClient();
client.property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED");
try {
String serverUrl = clusterManager.getServerUrl(activeServer);
String serverUrl = clusterService.getServerUrl(activeServer);
WebTarget target = client.target(serverUrl)
.path("~api/cluster/attachment")
.queryParam("projectId", projectId)
.queryParam("attachmentGroup", attachmentGroup)
.queryParam("suggestedAttachmentName", preferredAttachmentName);
var builder = target.request();
builder.header(AUTHORIZATION, BEARER + " " + clusterManager.getCredential());
builder.header(AUTHORIZATION, BEARER + " " + clusterService.getCredential());
StreamingOutput os = output -> {
try {
@ -598,7 +591,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
index++;
}
long maxUploadFileSize = settingManager.getPerformanceSetting().getMaxUploadFileSize() * 1024L * 1024L;
long maxUploadFileSize = settingService.getPerformanceSetting().getMaxUploadFileSize() * 1024L * 1024L;
Exception ex = null;
File file = new File(attachmentDir, attachmentName);
@ -623,7 +616,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
throw ExceptionUtils.unchecked(ex);
} else {
if (!attachmentDir.getParentFile().getName().equals(TEMP))
projectManager.directoryModified(projectId, attachmentDir);
projectService.directoryModified(projectId, attachmentDir);
return file.getName();
}
});
@ -636,7 +629,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public FileInfo getAttachmentInfo(Long projectId, String attachmentGroup, String attachment) {
return projectManager.runOnActiveServer(projectId, () -> read(getAttachmentLockName(projectId, attachmentGroup), () -> {
return projectService.runOnActiveServer(projectId, () -> read(getAttachmentLockName(projectId, attachmentGroup), () -> {
File attachmentFile = new File(getAttachmentGroupDir(projectId, attachmentGroup), attachment);
if (!attachmentFile.exists())
throw new FileNotFoundException("Attachment not found: " + attachment);
@ -647,12 +640,12 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public void deleteAttachment(Long projectId, String attachmentGroup, String attachment) {
projectManager.runOnActiveServer(projectId, () -> write(getAttachmentLockName(projectId, attachmentGroup), () -> {
projectService.runOnActiveServer(projectId, () -> write(getAttachmentLockName(projectId, attachmentGroup), () -> {
var attachmentGroupDir = getAttachmentGroupDir(projectId, attachmentGroup);
File attachmentFile = new File(attachmentGroupDir, attachment);
if (attachmentFile.exists()) {
FileUtils.deleteFile(attachmentFile);
projectManager.directoryModified(projectId, attachmentGroupDir);
projectService.directoryModified(projectId, attachmentGroupDir);
}
return null;
}));
@ -660,7 +653,7 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public List<FileInfo> listAttachments(Long projectId, String attachmentGroup) {
return projectManager.runOnActiveServer(projectId, () -> read(getAttachmentLockName(projectId, attachmentGroup), () -> {
return projectService.runOnActiveServer(projectId, () -> read(getAttachmentLockName(projectId, attachmentGroup), () -> {
List<FileInfo> attachments = new ArrayList<>();
File attachmentGroupDir = getAttachmentGroupDir(projectId, attachmentGroup);
if (attachmentGroupDir.exists()) {
@ -676,12 +669,12 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
@Override
public void syncAttachments(Long projectId, String activeServer) {
var permanent = ATTACHMENT_DIR + "/" + PERMANENT;
projectManager.syncDirectory(projectId, permanent, prefix -> {
projectService.syncDirectory(projectId, permanent, prefix -> {
var prefixPath = permanent + "/" + prefix;
projectManager.syncDirectory(projectId, prefixPath, attachmentGroup -> {
projectService.syncDirectory(projectId, prefixPath, attachmentGroup -> {
var attachmentGroupPath = permanent + "/" + prefix + "/" + attachmentGroup;
var lockName = getAttachmentLockName(projectId, attachmentGroup);
projectManager.syncDirectory(projectId, attachmentGroupPath, lockName, activeServer);
projectService.syncDirectory(projectId, attachmentGroupPath, lockName, activeServer);
}, activeServer);
}, activeServer);
}

View File

@ -11,9 +11,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.ProjectService;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.web.resource.AttachmentResource;
import io.onedev.server.web.resource.AttachmentResourceReference;
@ -44,21 +44,21 @@ public class ProjectAttachmentSupport implements AttachmentSupport {
@Override
public List<String> getAttachments() {
return getAttachmentManager().listAttachments(projectId, attachmentGroup).stream()
return getAttachmentService().listAttachments(projectId, attachmentGroup).stream()
.map(it->it.getPath()).collect(Collectors.toList());
}
private ProjectManager getProjectManager() {
return OneDev.getInstance(ProjectManager.class);
private ProjectService getProjectService() {
return OneDev.getInstance(ProjectService.class);
}
private AttachmentManager getAttachmentManager() {
return OneDev.getInstance(AttachmentManager.class);
private AttachmentService getAttachmentService() {
return OneDev.getInstance(AttachmentService.class);
}
@Override
public void deleteAttachemnt(String attachment) {
getAttachmentManager().deleteAttachment(projectId, attachmentGroup, attachment);
getAttachmentService().deleteAttachment(projectId, attachmentGroup, attachment);
}
@Override
@ -67,18 +67,18 @@ public class ProjectAttachmentSupport implements AttachmentSupport {
}
protected Project getProject() {
SessionManager sessionManager = OneDev.getInstance(SessionManager.class);
sessionManager.openSession();
SessionService sessionService = OneDev.getInstance(SessionService.class);
sessionService.openSession();
try {
return getProjectManager().load(projectId);
return getProjectService().load(projectId);
} finally {
sessionManager.closeSession();
sessionService.closeSession();
}
}
@Override
public String saveAttachment(String suggestedAttachmentName, InputStream attachmentStream) {
return getAttachmentManager().saveAttachment(
return getAttachmentService().saveAttachment(
projectId, attachmentGroup, suggestedAttachmentName, attachmentStream);
}

View File

@ -1,10 +1,42 @@
package io.onedev.server.buildspec;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.Validator;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.wicket.Component;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import io.onedev.commons.codeassist.InputCompletion;
import io.onedev.commons.codeassist.InputStatus;
import io.onedev.commons.codeassist.InputSuggestion;
@ -27,27 +59,11 @@ import io.onedev.server.data.migration.XmlBuildSpecMigrator;
import io.onedev.server.job.JobAuthorizationContext;
import io.onedev.server.model.Project;
import io.onedev.server.model.support.build.JobProperty;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.util.ComponentContext;
import io.onedev.server.validation.Validatable;
import io.onedev.server.web.page.project.blob.ProjectBlobPage;
import io.onedev.server.web.util.SuggestionUtils;
import io.onedev.server.web.util.WicketUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.wicket.Component;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.nodes.*;
import javax.annotation.Nullable;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
@Editable
@ClassValidating
@ -105,6 +121,7 @@ public class BuildSpec implements Serializable, Validatable {
private transient Map<String, JobProperty> propertyMap;
@Editable
@Valid
public List<Job> getJobs() {
return jobs;
}
@ -115,6 +132,7 @@ public class BuildSpec implements Serializable, Validatable {
}
@Editable
@Valid
public List<StepTemplate> getStepTemplates() {
return stepTemplates;
}
@ -125,6 +143,7 @@ public class BuildSpec implements Serializable, Validatable {
}
@Editable
@Valid
public List<Service> getServices() {
return services;
}
@ -135,6 +154,7 @@ public class BuildSpec implements Serializable, Validatable {
}
@Editable
@Valid
public List<JobProperty> getProperties() {
return properties;
}
@ -145,6 +165,7 @@ public class BuildSpec implements Serializable, Validatable {
}
@Editable
@Valid
public List<Import> getImports() {
return imports;
}
@ -164,8 +185,7 @@ public class BuildSpec implements Serializable, Validatable {
Collection<String> newCommitChain = new HashSet<>(commitChain);
newCommitChain.add(importCommit.name());
BuildSpec importedBuildSpec = aImport.getBuildSpec();
JobAuthorizationContext.push(new JobAuthorizationContext(
aImport.getProject(), importCommit, SecurityUtils.getUser(), null));
JobAuthorizationContext.push(new JobAuthorizationContext(aImport.getProject(), importCommit, null));
try {
importedBuildSpecs.addAll(importedBuildSpec.getImportedBuildSpecs(newCommitChain));
} finally {
@ -271,27 +291,10 @@ public class BuildSpec implements Serializable, Validatable {
return isValid;
}
private Validator getValidator() {
return OneDev.getInstance(Validator.class);
}
@Override
public boolean isValid(ConstraintValidatorContext context) {
boolean isValid = true;
int index = 0;
for (Job job: jobs) {
for (ConstraintViolation<Job> violation: getValidator().validate(job)) {
context.buildConstraintViolationWithTemplate(violation.getMessage())
.addPropertyNode(PROP_JOBS)
.addBeanNode()
.inIterable().atIndex(index)
.addConstraintViolation();
isValid = false;
}
index++;
}
Set<String> jobNames = new HashSet<>();
for (Job job: jobs) {
if (!jobNames.add(job.getName())) {
@ -300,19 +303,6 @@ public class BuildSpec implements Serializable, Validatable {
isValid = false;
}
}
index = 0;
for (Service service: services) {
for (ConstraintViolation<Service> violation: getValidator().validate(service)) {
context.buildConstraintViolationWithTemplate(violation.getMessage())
.addPropertyNode(PROP_SERVICES)
.addBeanNode()
.inIterable().atIndex(index)
.addConstraintViolation();
isValid = false;
}
index++;
}
Set<String> serviceNames = new HashSet<>();
for (Service service: services) {
@ -322,19 +312,6 @@ public class BuildSpec implements Serializable, Validatable {
isValid = false;
}
}
index = 0;
for (StepTemplate stepTemplate: stepTemplates) {
for (ConstraintViolation<StepTemplate> violation: getValidator().validate(stepTemplate)) {
context.buildConstraintViolationWithTemplate(violation.getMessage())
.addPropertyNode(PROP_STEP_TEMPLATES)
.addBeanNode()
.inIterable().atIndex(index)
.addConstraintViolation();
isValid = false;
}
index++;
}
Set<String> stepTemplateNames = new HashSet<>();
for (StepTemplate template: stepTemplates) {
@ -344,19 +321,6 @@ public class BuildSpec implements Serializable, Validatable {
isValid = false;
}
}
index = 0;
for (JobProperty property: properties) {
for (ConstraintViolation<JobProperty> violation: getValidator().validate(property)) {
context.buildConstraintViolationWithTemplate(violation.getMessage())
.addPropertyNode(PROP_PROPERTIES)
.addBeanNode()
.inIterable().atIndex(index)
.addConstraintViolation();
isValid = false;
}
index++;
}
Set<String> propertyNames = new HashSet<>();
for (JobProperty property : properties) {
@ -366,20 +330,6 @@ public class BuildSpec implements Serializable, Validatable {
isValid = false;
}
}
index = 0;
for (Import aImport: getImports()) {
Validator validator = OneDev.getInstance(Validator.class);
for (ConstraintViolation<Import> violation: validator.validate(aImport)) {
context.buildConstraintViolationWithTemplate(violation.getMessage())
.addPropertyNode(PROP_IMPORTS)
.addBeanNode()
.inIterable().atIndex(index)
.addConstraintViolation();
isValid = false;
}
index++;
}
Set<String> importProjectAndRevisions = new HashSet<>();
for (Import aImport: imports) {
@ -2312,4 +2262,136 @@ public class BuildSpec implements Serializable, Validatable {
});
}
@SuppressWarnings("unused")
private void migrate41(VersionedYamlDoc doc, Stack<Integer> versions) {
for (NodeTuple specTuple: doc.getValue()) {
String specKey = ((ScalarNode)specTuple.getKeyNode()).getValue();
if (specKey.equals("jobs")) {
SequenceNode jobsNode = (SequenceNode) specTuple.getValueNode();
for (Node jobsNodeItem: jobsNode.getValue()) {
MappingNode jobNode = (MappingNode) jobsNodeItem;
for (var itJobTuple = jobNode.getValue().iterator(); itJobTuple.hasNext();) {
var jobTuple = itJobTuple.next();
var keyNode = (ScalarNode) jobTuple.getKeyNode();
if (keyNode.getValue().equals("retryCondition")) {
var valueNode = (ScalarNode) jobTuple.getValueNode();
if (valueNode.getValue().equals("never")) {
itJobTuple.remove();
}
}
}
}
}
}
}
@SuppressWarnings("unused")
private void migrate42(VersionedYamlDoc doc, Stack<Integer> versions) {
migrate42_processNode(doc);
}
private void migrate42_processNode(Node node) {
if (node instanceof MappingNode) {
MappingNode mappingNode = (MappingNode) node;
if (mappingNode.getTag() != null && !mappingNode.getTag().equals(Tag.MAP)
&& mappingNode.getTag().getValue().startsWith("!")) {
var tagValue = mappingNode.getTag().getValue().substring(1);
boolean hasTypeTuple = false;
for (NodeTuple tuple : mappingNode.getValue()) {
if (tuple.getKeyNode() instanceof ScalarNode) {
ScalarNode keyNode = (ScalarNode) tuple.getKeyNode();
if ("type".equals(keyNode.getValue())) {
hasTypeTuple = true;
break;
}
}
}
if (!hasTypeTuple) {
mappingNode.getValue().add(0, new NodeTuple(
new ScalarNode(Tag.STR, "type"),
new ScalarNode(Tag.STR, tagValue)));
}
mappingNode.setTag(Tag.MAP);
}
for (NodeTuple tuple : mappingNode.getValue()) {
migrate42_processNode(tuple.getKeyNode());
migrate42_processNode(tuple.getValueNode());
}
} else if (node instanceof SequenceNode) {
SequenceNode sequenceNode = (SequenceNode) node;
for (Node item : sequenceNode.getValue()) {
migrate42_processNode(item);
}
}
}
@SuppressWarnings("unused")
private void migrate43(VersionedYamlDoc doc, Stack<Integer> versions) {
for (NodeTuple specTuple: doc.getValue()) {
String specKey = ((ScalarNode)specTuple.getKeyNode()).getValue();
if (specKey.equals("jobs")) {
SequenceNode jobsNode = (SequenceNode) specTuple.getValueNode();
for (Node jobsNodeItem: jobsNode.getValue()) {
MappingNode jobNode = (MappingNode) jobsNodeItem;
boolean hasRetryCondition = false;
for (var itJobTuple = jobNode.getValue().iterator(); itJobTuple.hasNext();) {
var jobTuple = itJobTuple.next();
var keyNode = (ScalarNode) jobTuple.getKeyNode();
if (keyNode.getValue().equals("retryCondition")) {
var valueNode = (ScalarNode) jobTuple.getValueNode();
if (StringUtils.isBlank(valueNode.getValue())) {
itJobTuple.remove();
} else {
hasRetryCondition = true;
}
break;
} else if (keyNode.getValue().equals("triggers")) {
SequenceNode triggersNode = (SequenceNode) jobTuple.getValueNode();
for (Node triggerNode: triggersNode.getValue()) {
MappingNode triggerMappingNode = (MappingNode) triggerNode;
boolean hasUserMatch = false;
String triggerType = null;
for (var triggerTuple: triggerMappingNode.getValue()) {
String triggerTupleKey = ((ScalarNode)triggerTuple.getKeyNode()).getValue();
if (triggerTupleKey.equals("type")) {
triggerType = ((ScalarNode)triggerTuple.getValueNode()).getValue();
break;
}
}
if ("BranchUpdateTrigger".equals(triggerType)) {
for (var itTriggerTuple = triggerMappingNode.getValue().iterator(); itTriggerTuple.hasNext();) {
var triggerTuple = itTriggerTuple.next();
String triggerTupleKey = ((ScalarNode)triggerTuple.getKeyNode()).getValue();
if (triggerTupleKey.equals("userMatch")) {
var valueNode = (ScalarNode) triggerTuple.getValueNode();
if (StringUtils.isBlank(valueNode.getValue())) {
itTriggerTuple.remove();
} else {
hasUserMatch = true;
}
break;
}
}
if (!hasUserMatch) {
triggerMappingNode.getValue().add(new NodeTuple(
new ScalarNode(Tag.STR, "userMatch"),
new ScalarNode(Tag.STR, "anyone")));
}
}
}
}
}
if (!hasRetryCondition) {
jobNode.getValue().add(new NodeTuple(
new ScalarNode(Tag.STR, "retryCondition"),
new ScalarNode(Tag.STR, "never")));
}
}
}
}
}
}

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
public interface BuildSpecAware {

View File

@ -0,0 +1,487 @@
package io.onedev.server.buildspec;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import io.onedev.commons.loader.ImplementationRegistry;
import io.onedev.commons.utils.ClassUtils;
import io.onedev.commons.utils.ExplicitException;
import io.onedev.commons.utils.StringUtils;
import io.onedev.server.annotation.Code;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.ImplementationProvider;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.Multiline;
import io.onedev.server.annotation.Patterns;
import io.onedev.server.annotation.RetryCondition;
import io.onedev.server.annotation.UserMatch;
import io.onedev.server.buildspec.job.Job;
import io.onedev.server.data.migration.MigrationHelper;
import io.onedev.server.model.support.build.JobProperty;
import io.onedev.server.rest.annotation.Api;
import io.onedev.server.util.Pair;
import io.onedev.server.util.ReflectionUtils;
import io.onedev.server.util.patternset.PatternSet;
import io.onedev.server.web.editable.BeanDescriptor;
import io.onedev.server.web.editable.EditableUtils;
import io.onedev.server.web.editable.PropertyDescriptor;
@Api(internal = true)
@Path("/build-spec-schema.yml")
@Consumes(MediaType.APPLICATION_JSON)
@Produces("application/x-yaml")
@Singleton
public class BuildSpecSchemaResource {
private final ImplementationRegistry implementationRegistry;
private volatile String schema;
@Inject
public BuildSpecSchemaResource(ImplementationRegistry implementationRegistry) {
this.implementationRegistry = implementationRegistry;
}
private void processProperty(Map<String, Object> currentNode, Object bean, PropertyDescriptor property) {
var descriptionSections = new ArrayList<String>();
var descriptionSection = property.getDescription();
if (descriptionSection != null)
descriptionSections.add(descriptionSection);
var getter = property.getPropertyGetter();
var returnType = property.getPropertyClass();
if (returnType == String.class) {
if (getter.getAnnotation(Code.class) == null && getter.getAnnotation(Multiline.class) == null) {
descriptionSections.add("NOTE: If set, the value can only contain one line");
}
InputStream grammarStream = null;
try {
if (getter.getAnnotation(Patterns.class) != null) {
if (getter.getAnnotation(Interpolative.class) != null) {
grammarStream = PatternSet.class.getResourceAsStream("InterpolativePatternSet.g4");
} else {
grammarStream = PatternSet.class.getResourceAsStream("PatternSet.g4");
}
} else if (getter.getAnnotation(RetryCondition.class) != null) {
grammarStream = io.onedev.server.buildspec.job.retrycondition.RetryCondition.class.getResourceAsStream("RetryCondition.g4");
} else if (getter.getAnnotation(UserMatch.class) != null) {
grammarStream = io.onedev.server.util.usermatch.UserMatch.class.getResourceAsStream("UserMatch.g4");
}
if (grammarStream != null) {
try {
var grammar = IOUtils.toString(grammarStream, StandardCharsets.UTF_8);
descriptionSections.add("NOTE: If set, the value should conform with below ANTLR v4 grammar:\n\n" + grammar);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} finally {
IOUtils.closeQuietly(grammarStream);
}
}
if (descriptionSections.size() != 0)
currentNode.put("description", StringUtils.join(descriptionSections, "\n\n"));
Class<?> elementClass = null;
if (Collection.class.isAssignableFrom(returnType)) {
elementClass = ReflectionUtils.getCollectionElementClass(property.getPropertyGetter().getGenericReturnType());
if (elementClass == null)
throw new ExplicitException("Unknown collection element class (bean: " + property.getBeanClass() + ", property: " + property.getPropertyName() + ")");
processCollectionProperty(currentNode, elementClass);
} else {
processType(currentNode, returnType);
}
Object defaultValue;
var value = property.getPropertyValue(bean);
if (value instanceof Integer) {
var intValue = (Integer) value;
if (intValue == 0)
defaultValue = null;
else
defaultValue = intValue;
} else if (value instanceof Long) {
var longValue = (Long) value;
if (longValue == 0)
defaultValue = null;
else
defaultValue = longValue;
} else if (value instanceof Double) {
var doubleValue = (Double) value;
if (doubleValue == 0)
defaultValue = null;
else
defaultValue = doubleValue;
} else if (value instanceof Float) {
var floatValue = (Float) value;
if (floatValue == 0)
defaultValue = null;
else
defaultValue = floatValue;
} else if (value instanceof Boolean) {
var booleanValue = (Boolean) value;
if (!booleanValue)
defaultValue = null;
else
defaultValue = booleanValue;
} else if (value instanceof Enum) {
var enumValue = (Enum<?>) value;
defaultValue = enumValue.name();
} else if (value instanceof String || value instanceof Date) {
defaultValue = value;
} else {
defaultValue = null;
}
if (defaultValue != null) {
currentNode.put("default", defaultValue);
}
}
private Object newBean(Class<?> beanClass) {
try {
return beanClass.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private void processBean(Map<String, Object> currentNode, Class<?> beanClass,
Collection<Class<?>> implementations, Set<String> processedProperties) {
var propsNode = (Map<String, Object>) currentNode.get("properties");
if (propsNode == null) {
propsNode = new HashMap<>();
}
var requiredNode = (List<String>) currentNode.get("required");
if (requiredNode == null) {
requiredNode = new ArrayList<>();
}
var beanDescriptor = new BeanDescriptor(beanClass);
var propertyMap = new HashMap<String, PropertyDescriptor>();
for (var groupProperties: beanDescriptor.getProperties().values()) {
for (var property: groupProperties) {
propertyMap.put(property.getPropertyName(), property);
}
}
/*
* As long as one implementation overrides a property in base class, we will exclude the property from
* common property section, and add it individually to each implementation's property section to avoid
* defining same property both in common section and oneOf section
*/
var excludedProperties = new HashSet<String>();
Map<String, byte[]> valueBytesMap = new HashMap<>();
Object bean = null;
for (var implementation: implementations) {
bean = newBean(implementation);
for (var groupProperties: new BeanDescriptor(implementation).getProperties().values()) {
for (var property: groupProperties) {
if (!excludedProperties.contains(property.getPropertyName())
&& propertyMap.containsKey(property.getPropertyName())
&& property.getBeanClass() != beanClass) {
excludedProperties.add(property.getPropertyName());
}
if (!excludedProperties.contains(property.getPropertyName())) {
var value = property.getPropertyValue(bean);
var lastValueBytes = valueBytesMap.get(property.getPropertyName());
if (lastValueBytes == null) {
lastValueBytes = SerializationUtils.serialize((Serializable) value);
valueBytesMap.put(property.getPropertyName(), lastValueBytes);
} else if (!Arrays.equals(lastValueBytes, SerializationUtils.serialize((Serializable) value))) {
excludedProperties.add(property.getPropertyName());
}
}
}
}
}
if (bean == null) {
bean = newBean(beanClass);
}
var dependents = new ArrayList<Pair<PropertyDescriptor, DependsOn>>();
for (var groupProperties: beanDescriptor.getProperties().values()) {
for (var property: groupProperties) {
if (!excludedProperties.contains(property.getPropertyName()) && processedProperties.add(property.getPropertyName())) {
if (property.getPropertyName().equals("type"))
throw new ExplicitException("Property 'type' is reserved (class: " + beanClass.getName() + ")");
var dependsOn = property.getPropertyGetter().getAnnotation(DependsOn.class);
if (dependsOn != null) {
dependents.add(new Pair<>(property, dependsOn));
} else {
if (property.isPropertyRequired())
requiredNode.add(property.getPropertyName());
var propNode = new HashMap<String, Object>();
propsNode.put(property.getPropertyName(), propNode);
processProperty(propNode, bean, property);
}
}
}
}
if (!dependents.isEmpty()) {
var allOfNode = new ArrayList<Map<String, Object>>();
currentNode.put("allOf", allOfNode);
for (var dependent: dependents) {
var allOfItemNode = new HashMap<String, Object>();
allOfNode.add(allOfItemNode);
var dependsOn = dependent.getRight();
var dependencyProperty = propertyMap.get(dependsOn.property());
if (dependencyProperty == null)
throw new ExplicitException("Dependency property not found: " + dependsOn.property());
var ifNode = new HashMap<String, Object>();
allOfItemNode.put("if", ifNode);
var ifPropsNode = new HashMap<String, Object>();
ifNode.put("properties", ifPropsNode);
var dependencyPropertyNode = new HashMap<String, Object>();
ifPropsNode.put(dependencyProperty.getPropertyName(), dependencyPropertyNode);
var inverse = dependsOn.inverse();
var dependencyPropertyClass = dependencyProperty.getPropertyClass();
if (dependencyPropertyClass == boolean.class) {
if (dependsOn.value().length() != 0) {
dependencyPropertyNode.put("const", Boolean.parseBoolean(dependsOn.value()));
} else {
dependencyPropertyNode.put("const", true);
}
} else if (dependencyPropertyClass == int.class || dependencyPropertyClass == long.class
|| dependencyPropertyClass == double.class || dependencyPropertyClass == float.class) {
if (dependsOn.value().length() != 0) {
dependencyPropertyNode.put("const", Integer.parseInt(dependsOn.value()));
} else {
dependencyPropertyNode.put("const", 0);
inverse = !inverse;
}
} else {
if (dependsOn.value().length() != 0) {
if (dependencyPropertyClass == Boolean.class)
dependencyPropertyNode.put("const", Boolean.parseBoolean(dependsOn.value()));
else if (dependencyPropertyClass == Integer.class || dependencyPropertyClass == Long.class || dependencyPropertyClass == Double.class || dependencyPropertyClass == Float.class)
dependencyPropertyNode.put("const", Integer.parseInt(dependsOn.value()));
else
dependencyPropertyNode.put("const", dependsOn.value());
} else {
var typeList = new ArrayList<String>();
typeList.add("object");
typeList.add("string");
typeList.add("integer");
typeList.add("number");
typeList.add("boolean");
dependencyPropertyNode.put("type", typeList);
}
}
Map<String, Object> branchNode;
if (!inverse) {
branchNode = new HashMap<>();
allOfItemNode.put("then", branchNode);
} else {
branchNode = new HashMap<>();
allOfItemNode.put("else", branchNode);
}
var property = dependent.getLeft();
var branchPropsNode = new HashMap<String, Object>();
branchNode.put("properties", branchPropsNode);
var propNode = new HashMap<String, Object>();
branchPropsNode.put(property.getPropertyName(), propNode);
processProperty(propNode, bean, property);
if (property.isPropertyRequired()) {
var requiredList = new ArrayList<String>();
requiredList.add(property.getPropertyName());
branchNode.put("required", requiredList);
}
}
}
if (!propsNode.isEmpty())
currentNode.put("properties", propsNode);
if (!requiredNode.isEmpty())
currentNode.put("required", requiredNode);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void processType(Map<String, Object> currentNode, Class<?> type) {
if (type == String.class) {
currentNode.put("type", "string");
} else if (type == Boolean.class || type == boolean.class) {
currentNode.put("type", "boolean");
} else if (type == Integer.class || type == int.class || type == Long.class || type == long.class) {
currentNode.put("type", "integer");
} else if (type == Float.class || type == float.class || type == Double.class || type == double.class) {
currentNode.put("type", "number");
} else if (Enum.class.isAssignableFrom(type)) {
currentNode.put("type", "string");
var enumList = new ArrayList<String>();
var enumClass = (Class<Enum>) type;
for (var enumValue: EnumSet.allOf(enumClass)) {
enumList.add(((Enum) enumValue).name());
}
currentNode.put("enum", enumList);
} else if (type == Date.class) {
currentNode.put("type", "string");
currentNode.put("format", "date-time");
} else if (type.getAnnotation(Editable.class) != null) {
if (ClassUtils.isConcrete(type)) {
currentNode.put("type", "object");
processBean(currentNode, type, new ArrayList<>(), new HashSet<>());
currentNode.put("additionalProperties", false);
} else {
processPolymorphic(currentNode, type);
}
} else {
throw new ExplicitException("Unsupported type: " + type);
}
}
@SuppressWarnings("unchecked")
private void processPolymorphic(Map<String, Object> currentNode, Class<?> baseClass) {
Collection<Class<?>> implementations = new ArrayList<>();
var implementationProvider = baseClass.getAnnotation(ImplementationProvider.class);
if (implementationProvider != null)
implementations.addAll((Collection<? extends Class<? extends Serializable>>) ReflectionUtils.invokeStaticMethod(baseClass, implementationProvider.value()));
else
implementations.addAll(implementationRegistry.getImplementations(baseClass));
currentNode.put("type", "object");
var propsNode = new HashMap<String, Object>();
var typeNode = new HashMap<String, Object>();
typeNode.put("type", "string");
propsNode.put("type", typeNode);
currentNode.put("properties", propsNode);
var enumList = new ArrayList<String>();
typeNode.put("enum", enumList);
var requiredList = new ArrayList<String>();
requiredList.add("type");
currentNode.put("required", requiredList);
var processedProperties = new HashSet<String>();
processBean(currentNode, baseClass, implementations, processedProperties);
var oneOfList = new ArrayList<Map<String, Object>>();
currentNode.put("oneOf", oneOfList);
for (var implementation: implementations) {
enumList.add(implementation.getSimpleName());
var oneOfItemNode = new HashMap<String, Object>();
var oneOfItemPropsNode = new HashMap<String, Object>();
var typeConstNode = new HashMap<String, Object>();
typeConstNode.put("const", implementation.getSimpleName());
oneOfItemPropsNode.put("type", typeConstNode);
oneOfItemNode.put("properties", oneOfItemPropsNode);
var description = EditableUtils.getDescription(implementation);
if (description != null)
oneOfItemNode.put("description", description);
processBean(oneOfItemNode, implementation, new ArrayList<>(), new HashSet<>(processedProperties));
oneOfList.add(oneOfItemNode);
}
}
private void processCollectionProperty(Map<String, Object> currentNode, Class<?> collectionElementClass) {
currentNode.put("type", "array");
var itemsNode = new HashMap<String, Object>();
currentNode.put("items", itemsNode);
if (Collection.class.isAssignableFrom(collectionElementClass)) {
itemsNode.put("type", "array");
var nestedItemsNode = new HashMap<String, Object>();
nestedItemsNode.put("type", "string");
itemsNode.put("items", nestedItemsNode);
} else {
processType(itemsNode, collectionElementClass);
}
}
@GET
@SuppressWarnings("unchecked")
public String getBuildSpecSchema() {
if (schema == null) {
var rootNode = new HashMap<String, Object>();
rootNode.put("$schema", "https://json-schema.org/draft/2020-12/schema");
rootNode.put("title", "YAML schema of build spec file");
rootNode.put("type", "object");
var propsNode = new HashMap<String, Object>();
rootNode.put("properties", propsNode);
var versionNode = new HashMap<String, Object>();
propsNode.put("version", versionNode);
versionNode.put("type", "integer");
versionNode.put("const", Integer.parseInt(MigrationHelper.getVersion(BuildSpec.class)));
var jobsNode = new HashMap<String, Object>();
propsNode.put("jobs", jobsNode);
processCollectionProperty(jobsNode, Job.class);
var servicesNode = new HashMap<String, Object>();
propsNode.put("services", servicesNode);
processCollectionProperty(servicesNode, Service.class);
var propertiesNode = new HashMap<String, Object>();
propsNode.put("properties", propertiesNode);
processCollectionProperty(propertiesNode, JobProperty.class);
var importsNode = new HashMap<String, Object>();
propsNode.put("imports", importsNode);
processCollectionProperty(importsNode, Import.class);
var jobPropsNode = (Map<String, Object>) ((Map<String, Object>) jobsNode.get("items")).get("properties");
var stepTemplatesNode = new HashMap<String, Object>();
propsNode.put("stepTemplates", stepTemplatesNode);
stepTemplatesNode.put("type", "array");
var stepTemplateNode = new HashMap<String, Object>();
stepTemplatesNode.put("items", stepTemplateNode);
stepTemplateNode.put("type", "object");
var stepTemplatePropsNode = new HashMap<String, Object>();
stepTemplateNode.put("properties", stepTemplatePropsNode);
stepTemplatePropsNode.put("steps", jobPropsNode.get("steps"));
stepTemplatePropsNode.put("paramSpecs", jobPropsNode.get("paramSpecs"));
var requiredList = new ArrayList<String>();
requiredList.add("version");
rootNode.put("required", requiredList);
rootNode.put("additionalProperties", false);
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setPrettyFlow(true);
schema = new Yaml(options).dump(rootNode);
}
return schema;
}
}

View File

@ -10,7 +10,7 @@ import java.util.List;
import java.util.Stack;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
@ -30,7 +30,7 @@ import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.ClassValidating;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.ProjectService;
import io.onedev.server.job.JobAuthorizationContext;
import io.onedev.server.model.Project;
import io.onedev.server.security.SecurityUtils;
@ -64,7 +64,7 @@ public class Import implements Serializable, Validatable {
private transient BuildSpec buildSpec;
private static ThreadLocal<Stack<String>> importChain = ThreadLocal.withInitial(Stack::new);
private static ThreadLocal<Stack<String>> IMPORT_CHAIN = ThreadLocal.withInitial(Stack::new);
// change Named("projectPath") also if change name of this property
@Editable(order=100, name="Project", description="Specify project to import build spec from")
@ -80,13 +80,13 @@ public class Import implements Serializable, Validatable {
@SuppressWarnings("unused")
private static List<String> getProjectChoices() {
ProjectManager projectManager = OneDev.getInstance(ProjectManager.class);
ProjectService projectService = OneDev.getInstance(ProjectService.class);
Project project = ((ProjectPage)WicketUtils.getPage()).getProject();
Collection<Project> projects = SecurityUtils.getAuthorizedProjects(new AccessProject());
projects.remove(project);
ProjectCache cache = projectManager.cloneCache();
ProjectCache cache = projectService.cloneCache();
List<String> choices = projects.stream().map(it->cache.get(it.getId()).getPath()).collect(Collectors.toList());
Collections.sort(choices);
@ -98,7 +98,7 @@ public class Import implements Serializable, Validatable {
private static Project getInputProject() {
String projectPath = (String) EditContext.get().getInputValue("projectPath");
if (projectPath != null) {
Project project = OneDev.getInstance(ProjectManager.class).findByPath(projectPath);
Project project = OneDev.getInstance(ProjectService.class).findByPath(projectPath);
if (project != null && SecurityUtils.canReadCode(project))
return project;
}
@ -194,8 +194,8 @@ public class Import implements Serializable, Validatable {
public boolean isValid(ConstraintValidatorContext context) {
try {
var commit = getCommit();
if (importChain.get().contains(commit.name())) {
List<String> circular = new ArrayList<>(importChain.get());
if (IMPORT_CHAIN.get().contains(commit.name())) {
List<String> circular = new ArrayList<>(IMPORT_CHAIN.get());
circular.add(commit.name());
String errorMessage = MessageFormat.format(
_T("Circular build spec imports ({0})"), circular);
@ -203,12 +203,11 @@ public class Import implements Serializable, Validatable {
context.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation();
return false;
} else {
importChain.get().push(commit.name());
IMPORT_CHAIN.get().push(commit.name());
try {
Validator validator = OneDev.getInstance(Validator.class);
BuildSpec buildSpec = getBuildSpec();
JobAuthorizationContext.push(new JobAuthorizationContext(
getProject(), getCommit(), SecurityUtils.getUser(), null));
JobAuthorizationContext.push(new JobAuthorizationContext(getProject(), getCommit(), null));
try {
for (int i = 0; i < buildSpec.getImports().size(); i++) {
Import aImport = buildSpec.getImports().get(i);
@ -226,7 +225,7 @@ public class Import implements Serializable, Validatable {
JobAuthorizationContext.pop();
}
} finally {
importChain.get().pop();
IMPORT_CHAIN.get().pop();
}
}
} catch (Exception e) {
@ -243,7 +242,7 @@ public class Import implements Serializable, Validatable {
public Project getProject() {
if (project == null) {
project = OneDev.getInstance(ProjectManager.class).findByPath(projectPath);
project = OneDev.getInstance(ProjectService.class).findByPath(projectPath);
if (project == null)
throw new ExplicitException(MessageFormat.format( _T("Unable to find project to import build spec: {0}"), projectPath));
}

View File

@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import io.onedev.commons.codeassist.InputCompletion;
@ -87,6 +88,7 @@ public class Service implements NamedElement {
@Editable(order=300, name="Environment Variables", description="Optionally specify environment variables of "
+ "the service")
@Valid
public List<EnvVar> getEnvVars() {
return envVars;
}

View File

@ -17,7 +17,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Valid;
import javax.validation.constraints.Min;
@ -32,6 +32,7 @@ import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.ClassValidating;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.RetryCondition;
@ -45,14 +46,13 @@ import io.onedev.server.buildspec.job.trigger.JobTrigger;
import io.onedev.server.buildspec.param.ParamUtils;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.buildspec.step.Step;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.event.project.ProjectEvent;
import io.onedev.server.git.GitUtils;
import io.onedev.server.job.match.JobMatch;
import io.onedev.server.job.match.JobMatchContext;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.service.SettingService;
import io.onedev.server.util.ComponentContext;
import io.onedev.server.util.EditContext;
import io.onedev.server.util.criteria.Criteria;
@ -82,6 +82,8 @@ public class Job implements NamedElement, Validatable {
public static final String PROP_RETRY_CONDITION = "retryCondition";
public static final String PROP_POST_BUILD_ACTIONS = "postBuildActions";
public static final String PROP_JOB_EXECUTOR = "jobExecutor";
private String name;
@ -148,7 +150,7 @@ public class Job implements NamedElement, Validatable {
@SuppressWarnings("unused")
private static String getJobExecutorPlaceholder() {
if (OneDev.getInstance(SettingManager.class).getJobExecutors().isEmpty())
if (OneDev.getInstance(SettingService.class).getJobExecutors().isEmpty())
return _T("Auto-discovered executor");
else
return _T("First applicable executor");
@ -156,7 +158,7 @@ public class Job implements NamedElement, Validatable {
@SuppressWarnings("unused")
private static String getJobExecutorDescription() {
if (OneDev.getInstance(SettingManager.class).getJobExecutors().isEmpty())
if (OneDev.getInstance(SettingService.class).getJobExecutors().isEmpty())
return _T("Optionally specify executor for this job. Leave empty to use auto-discover executor");
else
return _T("Optionally specify executor for this job. Leave empty to use first applicable executor");
@ -171,8 +173,8 @@ public class Job implements NamedElement, Validatable {
String branch = page.getBlobIdent().revision;
if (branch == null)
branch = "main";
JobMatchContext context = new JobMatchContext(page.getProject(), branch, null, SecurityUtils.getAuthUser(), jobName);
for (JobExecutor executor: OneDev.getInstance(SettingManager.class).getJobExecutors()) {
JobMatchContext context = new JobMatchContext(page.getProject(), branch, null, jobName);
for (JobExecutor executor: OneDev.getInstance(SettingService.class).getJobExecutors()) {
if (executor.isEnabled()) {
if (executor.getJobMatch() == null) {
applicableJobExecutors.add(executor.getName());
@ -188,6 +190,7 @@ public class Job implements NamedElement, Validatable {
}
@Editable(order=200, description="Steps will be executed serially on same node, sharing the same <a href='https://docs.onedev.io/concepts#job-workspace'>job workspace</a>")
@Valid
public List<Step> getSteps() {
return steps;
}
@ -280,8 +283,8 @@ public class Job implements NamedElement, Validatable {
}
@Editable(order=9400, group="More Settings", description="Specify condition to retry build upon failure")
@NotEmpty
@RetryCondition
@NotEmpty
public String getRetryCondition() {
return retryCondition;
}
@ -292,6 +295,7 @@ public class Job implements NamedElement, Validatable {
@Editable(order=9410, group="More Settings", description="Maximum of retries before giving up")
@Min(value=1, message="This value should not be less than 1")
@DependsOn(property="retryCondition", value = "never", inverse = true)
public int getMaxRetries() {
return maxRetries;
}
@ -304,6 +308,7 @@ public class Job implements NamedElement, Validatable {
"Delay of subsequent retries will be calculated using an exponential back-off based on " +
"this value")
@Min(value=1, message="This value should not be less than 1")
@DependsOn(property="retryCondition", value = "never", inverse = true)
public int getRetryDelay() {
return retryDelay;
}
@ -346,6 +351,14 @@ public class Job implements NamedElement, Validatable {
public boolean isValid(ConstraintValidatorContext context) {
boolean isValid = true;
var jobExecutors = OneDev.getInstance(SettingService.class).getJobExecutors();
if (jobExecutor != null && !jobExecutor.contains("@")
&& jobExecutors.stream().noneMatch(it->it.getName().equals(jobExecutor))) {
isValid = false;
context.buildConstraintViolationWithTemplate("Job executor not found: " + jobExecutor)
.addPropertyNode(PROP_JOB_EXECUTOR).addConstraintViolation();
}
Set<String> dependencyJobNames = new HashSet<>();
for (JobDependency dependency: jobDependencies) {
if (!dependencyJobNames.add(dependency.getJobName())) {
@ -364,18 +377,16 @@ public class Job implements NamedElement, Validatable {
}
}
if (getRetryCondition() != null) {
try {
io.onedev.server.buildspec.job.retrycondition.RetryCondition.parse(this, getRetryCondition());
} catch (Exception e) {
String message = e.getMessage();
if (message == null)
message = "Malformed retry condition";
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(PROP_RETRY_CONDITION)
.addConstraintViolation();
isValid = false;
}
try {
io.onedev.server.buildspec.job.retrycondition.RetryCondition.parse(this, getRetryCondition());
} catch (Exception e) {
String message = e.getMessage();
if (message == null)
message = "Malformed retry condition";
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(PROP_RETRY_CONDITION)
.addConstraintViolation();
isValid = false;
}
if (isValid) {

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import io.onedev.server.buildspec.ParamSpecAware;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job;
import io.onedev.k8shelper.KubernetesHelper;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.service.SettingService;
import io.onedev.server.model.Build;
import io.onedev.server.util.UrlUtils;
@ -118,14 +118,14 @@ public enum JobVariable {
SERVER {
@Override
public String getValue(Build build) {
var serverUrl = OneDev.getInstance(SettingManager.class).getSystemSetting().getServerUrl();
var serverUrl = OneDev.getInstance(SettingService.class).getSystemSetting().getServerUrl();
return UrlUtils.getServer(serverUrl);
}
},
SERVER_HOST {
@Override
public String getValue(Build build) {
var serverUrl = OneDev.getInstance(SettingManager.class).getSystemSetting().getServerUrl();
var serverUrl = OneDev.getInstance(SettingService.class).getSystemSetting().getServerUrl();
try {
return new URL(serverUrl).getHost();
} catch (MalformedURLException e) {
@ -136,7 +136,7 @@ public enum JobVariable {
SERVER_URL {
@Override
public String getValue(Build build) {
return OneDev.getInstance(SettingManager.class).getSystemSetting().getServerUrl();
return OneDev.getInstance(SettingService.class).getSystemSetting().getServerUrl();
}
};

View File

@ -3,14 +3,14 @@ package io.onedev.server.buildspec.job;
import io.onedev.server.OneDev;
import io.onedev.server.buildspec.param.instance.ParamInstances;
import io.onedev.server.buildspec.param.instance.ParamMap;
import io.onedev.server.entitymanager.IssueManager;
import io.onedev.server.entitymanager.PullRequestManager;
import io.onedev.server.service.IssueService;
import io.onedev.server.service.PullRequestService;
import io.onedev.server.model.Issue;
import io.onedev.server.model.PullRequest;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import java.io.Serializable;
import java.util.List;
@ -48,7 +48,7 @@ public class TriggerMatch implements Serializable {
@Nullable
public PullRequest getRequest() {
if (requestId != null)
return OneDev.getInstance(PullRequestManager.class).load(requestId);
return OneDev.getInstance(PullRequestService.class).load(requestId);
else
return null;
}
@ -56,7 +56,7 @@ public class TriggerMatch implements Serializable {
@Nullable
public Issue getIssue() {
if (issueId != null)
return OneDev.getInstance(IssueManager.class).load(issueId);
return OneDev.getInstance(IssueService.class).load(issueId);
else
return null;
}

View File

@ -1,15 +1,34 @@
package io.onedev.server.buildspec.job.action;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotEmpty;
import org.apache.shiro.subject.Subject;
import edu.emory.mathcs.backport.java.util.Collections;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.commons.utils.ExplicitException;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.*;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.FieldNamesProvider;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.Multiline;
import io.onedev.server.annotation.OmitName;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.job.Job;
import io.onedev.server.entitymanager.IssueManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.service.IssueService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.SettingService;
import io.onedev.server.job.JobAuthorizationContext;
import io.onedev.server.model.Build;
import io.onedev.server.model.Issue;
@ -17,21 +36,12 @@ import io.onedev.server.model.Project;
import io.onedev.server.model.support.administration.GlobalIssueSetting;
import io.onedev.server.model.support.issue.field.FieldUtils;
import io.onedev.server.model.support.issue.field.instance.FieldInstance;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.TransactionService;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.security.permission.AccessProject;
import io.onedev.server.util.EditContext;
import io.onedev.server.util.facade.ProjectCache;
import io.onedev.server.web.page.project.ProjectPage;
import io.onedev.server.web.util.WicketUtils;
import org.apache.shiro.subject.Subject;
import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotEmpty;
import java.util.*;
import java.util.stream.Collectors;
@Editable(name="Create issue", order=300)
public class CreateIssueAction extends PostBuildAction {
@ -63,13 +73,13 @@ public class CreateIssueAction extends PostBuildAction {
@SuppressWarnings("unused")
private static List<String> getProjectChoices() {
ProjectManager projectManager = OneDev.getInstance(ProjectManager.class);
ProjectService projectService = OneDev.getInstance(ProjectService.class);
Project project = ((ProjectPage) WicketUtils.getPage()).getProject();
Collection<Project> projects = SecurityUtils.getAuthorizedProjects(new AccessProject());
projects.remove(project);
ProjectCache cache = projectManager.cloneCache();
ProjectCache cache = projectService.cloneCache();
List<String> choices = projects.stream().map(it->cache.get(it.getId()).getPath()).collect(Collectors.toList());
Collections.sort(choices);
@ -80,7 +90,7 @@ public class CreateIssueAction extends PostBuildAction {
@Editable(order=910, description="Specify a secret to be used as access token to create issue in " +
"above project if it is not publicly accessible")
@ChoiceProvider("getAccessTokenSecretChoices")
@ShowCondition("isProjectSpecified")
@DependsOn(property="projectPath")
@Nullable
public String getAccessTokenSecret() {
return accessTokenSecret;
@ -89,11 +99,6 @@ public class CreateIssueAction extends PostBuildAction {
public void setAccessTokenSecret(String accessTokenSecret) {
this.accessTokenSecret = accessTokenSecret;
}
@SuppressWarnings("unused")
private static boolean isProjectSpecified() {
return EditContext.get().getInputValue("projectPath") != null;
}
@SuppressWarnings("unused")
private static List<String> getAccessTokenSecretChoices() {
@ -150,15 +155,15 @@ public class CreateIssueAction extends PostBuildAction {
}
private static Collection<String> getFieldNames() {
return OneDev.getInstance(SettingManager.class).getIssueSetting().getFieldNames();
return OneDev.getInstance(SettingService.class).getIssueSetting().getFieldNames();
}
@Override
public void execute(Build build) {
OneDev.getInstance(TransactionManager.class).run(() -> {
OneDev.getInstance(TransactionService.class).run(() -> {
Project project;
if (getProjectPath() != null) {
project = OneDev.getInstance(ProjectManager.class).findByPath(getProjectPath());
project = OneDev.getInstance(ProjectService.class).findByPath(getProjectPath());
if (project == null)
throw new ExplicitException("Unable to find project: " + projectPath);
Subject subject = JobAuthorizationContext.get().getSubject(getAccessTokenSecret());
@ -172,8 +177,8 @@ public class CreateIssueAction extends PostBuildAction {
issue.setTitle(getIssueTitle());
issue.setSubmitter(SecurityUtils.getUser());
issue.setSubmitDate(new Date());
SettingManager settingManager = OneDev.getInstance(SettingManager.class);
GlobalIssueSetting issueSetting = settingManager.getIssueSetting();
SettingService settingService = OneDev.getInstance(SettingService.class);
GlobalIssueSetting issueSetting = settingService.getIssueSetting();
issue.setState(issueSetting.getInitialStateSpec().getName());
issue.setDescription(getIssueDescription());
@ -183,7 +188,7 @@ public class CreateIssueAction extends PostBuildAction {
.convertToObject(instance.getValueProvider().getValue());
issue.setFieldValue(instance.getName(), fieldValue);
}
OneDev.getInstance(IssueManager.class).open(issue);
OneDev.getInstance(IssueService.class).open(issue);
});
}
@ -197,7 +202,7 @@ public class CreateIssueAction extends PostBuildAction {
public void validateWith(BuildSpec buildSpec, Job job) {
super.validateWith(buildSpec, job);
GlobalIssueSetting issueSetting = OneDev.getInstance(SettingManager.class).getIssueSetting();
GlobalIssueSetting issueSetting = OneDev.getInstance(SettingService.class).getIssueSetting();
try {
FieldUtils.validateFields(issueSetting.getFieldSpecMap(getFieldNames()), issueFields);
} catch (ValidationException e) {

View File

@ -1,7 +1,23 @@
package io.onedev.server.buildspec.job.action;
import static io.onedev.server.buildspec.param.ParamUtils.resolveParams;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotEmpty;
import org.apache.wicket.Component;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.*;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.OmitName;
import io.onedev.server.annotation.ParamSpecProvider;
import io.onedev.server.annotation.ShowCondition;
import io.onedev.server.annotation.VariableOption;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.BuildSpecAware;
import io.onedev.server.buildspec.job.Job;
@ -9,21 +25,13 @@ import io.onedev.server.buildspec.param.ParamUtils;
import io.onedev.server.buildspec.param.instance.ParamInstances;
import io.onedev.server.buildspec.param.instance.ParamMap;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.job.JobManager;
import io.onedev.server.job.JobService;
import io.onedev.server.model.Build;
import io.onedev.server.service.UserService;
import io.onedev.server.util.ComponentContext;
import io.onedev.server.util.EditContext;
import io.onedev.server.web.editable.BeanEditor;
import io.onedev.server.web.util.WicketUtils;
import org.apache.wicket.Component;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;
import static io.onedev.server.buildspec.param.ParamUtils.resolveParams;
@Editable(name="Run job", order=100)
public class RunJobAction extends PostBuildAction {
@ -106,11 +114,11 @@ public class RunJobAction extends PostBuildAction {
@Override
public void execute(Build build) {
for (var paramMap: resolveParams(build, build.getParamCombination(), getParamMatrix(), getExcludeParamMaps())) {
JobManager jobManager = OneDev.getInstance(JobManager.class);
jobManager.submit(build.getProject(), build.getCommitId(),
getJobName(), paramMap, build.getRefName(),
build.getSubmitter(), build.getRequest(), build.getIssue(),
"Post build action of job '" + build.getJobName() + "'");
JobService jobService = OneDev.getInstance(JobService.class);
var userService = OneDev.getInstance(UserService.class);
jobService.submit(userService.getSystem(), build.getProject(), build.getCommitId(),
getJobName(), paramMap, build.getRefName(), build.getRequest(), build.getIssue(),
"Triggered via post build action of job '" + build.getJobName() + "'");
}
}

View File

@ -0,0 +1,308 @@
package io.onedev.server.buildspec.job.action;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.NotEmpty;
import javax.ws.rs.NotAcceptableException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.util.ThreadContext;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.ClassValidating;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.git.GitUtils;
import io.onedev.server.job.JobService;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.security.permission.AccessProject;
import io.onedev.server.service.AccessTokenService;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.util.EditContext;
import io.onedev.server.util.facade.ProjectCache;
import io.onedev.server.validation.Validatable;
import io.onedev.server.web.page.project.ProjectPage;
import io.onedev.server.web.util.SuggestionUtils;
import io.onedev.server.web.util.WicketUtils;
@Editable(name="Run job in another project", order=150)
@ClassValidating
public class RunProjectJobAction extends PostBuildAction implements Validatable {
private static final long serialVersionUID = 1L;
private String projectPath;
private String branch;
private String tag;
private String jobName;
private List<JobParam> jobParams = new ArrayList<>();
private String accessTokenSecret;
@Editable(order=100, name="Project", description="Specify project to run job in")
@ChoiceProvider("getProjectChoices")
@NotEmpty
public String getProjectPath() {
return projectPath;
}
public void setProjectPath(String projectPath) {
this.projectPath = projectPath;
}
@SuppressWarnings("unused")
private static List<String> getProjectChoices() {
List<String> choices = new ArrayList<>();
Project currentProject = ((ProjectPage)WicketUtils.getPage()).getProject();
ProjectCache cache = getProjectService().cloneCache();
for (Project project: SecurityUtils.getAuthorizedProjects(new AccessProject())) {
if (!project.equals(currentProject))
choices.add(cache.get(project.getId()).getPath());
}
Collections.sort(choices);
return choices;
}
@Editable(order=200, description="Specify branch to run the job against. Either branch or tag can be specified, but not both. Default branch will be used if both not specified")
@Interpolative(variableSuggester="suggestVariables", literalSuggester="suggestBranches")
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
@Editable(order=200, description="Specify tag to run the job against. Either branch or tag can be specified, but not both. Default branch will be used if both not specified")
@Interpolative(variableSuggester="suggestVariables", literalSuggester="suggestTags")
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
@SuppressWarnings("unused")
private static List<InputSuggestion> suggestVariables(String matchWith) {
return BuildSpec.suggestVariables(matchWith, false, false, false);
}
@SuppressWarnings("unused")
private static List<InputSuggestion> suggestBranches(String matchWith) {
Project project = getInputProject();
if (project != null)
return SuggestionUtils.suggestBranches(project, matchWith);
else
return new ArrayList<>();
}
@SuppressWarnings("unused")
private static List<InputSuggestion> suggestTags(String matchWith) {
Project project = getInputProject();
if (project != null)
return SuggestionUtils.suggestTags(project, matchWith);
else
return new ArrayList<>();
}
@Editable(order=300, name="Job")
@ChoiceProvider("getJobChoices")
@NotEmpty
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
@Nullable
private static Project getInputProject() {
String projectPath = (String) EditContext.get().getInputValue("projectPath");
if (projectPath != null) {
Project project = getProjectService().findByPath(projectPath);
if (project != null && SecurityUtils.canReadCode(project))
return project;
}
return null;
}
@SuppressWarnings("unused")
private static List<String> getJobChoices() {
Project project = getInputProject();
List<String> jobNames = new ArrayList<>();
if (project != null) {
jobNames.addAll(getBuildService().getAccessibleJobNames(SecurityUtils.getSubject(), project));
Collections.sort(jobNames);
}
return jobNames;
}
@Editable(order=400, name="Job Parameters")
public List<JobParam> getJobParams() {
return jobParams;
}
public void setJobParams(List<JobParam> jobParams) {
this.jobParams = jobParams;
}
@Editable(order=500, placeholder="Access Anonymously", description="Specify a secret to be used as "
+ "access token to trigger job in above project")
@ChoiceProvider("getAccessTokenSecretChoices")
@NotEmpty
public String getAccessTokenSecret() {
return accessTokenSecret;
}
public void setAccessTokenSecret(String accessTokenSecret) {
this.accessTokenSecret = accessTokenSecret;
}
@SuppressWarnings("unused")
private static List<String> getAccessTokenSecretChoices() {
return Project.get().getHierarchyJobSecrets()
.stream().map(it->it.getName()).collect(Collectors.toList());
}
@Override
public void execute(Build build) {
Project project = getProjectService().findByPath(projectPath);
if (project == null)
throw new NotAcceptableException("Project not found: " + projectPath);
String secretValue = build.getJobAuthorizationContext().getSecretValue(accessTokenSecret);
var accessToken = getAccessTokenService().findByValue(secretValue);
if (accessToken == null)
throw new NotAcceptableException("Invalid access token");
var subject = accessToken.asSubject();
if (!SecurityUtils.canRunJob(subject, project, jobName))
throw new UnauthorizedException();
var user = SecurityUtils.getUser(subject);
ThreadContext.bind(subject);
try {
String refName;
if (branch != null) {
refName = GitUtils.branch2ref(branch);
} else if (tag != null) {
refName = GitUtils.tag2ref(tag);
} else {
var defaultBranch = project.getDefaultBranch();
if (defaultBranch == null)
throw new NotAcceptableException("No default branch in project: " + project.getPath());
refName = GitUtils.branch2ref(defaultBranch);
}
var commit = project.getRevCommit(refName, false);
if (commit == null)
throw new NotAcceptableException("Ref not found (project: " + project.getPath() + ", ref: " + refName + ")");
Map<String, List<String>> jobParamMap = new HashMap<>();
for (var jobParam: jobParams) {
jobParamMap.computeIfAbsent(jobParam.getName(), k -> new ArrayList<>()).add(jobParam.getValue());
}
getJobService().submit(user, project, commit.copy(), jobName, jobParamMap, refName, null, null,
"Triggered via post build action of job '" + build.getJobName() + "' in project '" + build.getProject().getPath() + "'");
} finally {
ThreadContext.unbindSubject();
}
}
@Override
public boolean isValid(ConstraintValidatorContext context) {
if (branch != null && tag != null) {
var errorMessage = "Either branch or tag can be specified, but not both";
context.buildConstraintViolationWithTemplate(errorMessage)
.addPropertyNode("branch").addConstraintViolation();
context.buildConstraintViolationWithTemplate(errorMessage)
.addPropertyNode("tag").addConstraintViolation();
return false;
} else {
return true;
}
}
@Override
public String getDescription() {
return "Run job '" + jobName + "' in project '" + projectPath + "'";
}
private static ProjectService getProjectService() {
return OneDev.getInstance(ProjectService.class);
}
private static AccessTokenService getAccessTokenService() {
return OneDev.getInstance(AccessTokenService.class);
}
private static JobService getJobService() {
return OneDev.getInstance(JobService.class);
}
private static BuildService getBuildService() {
return OneDev.getInstance(BuildService.class);
}
@Editable
public static class JobParam implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String value;
@Editable(order=100)
@Interpolative(variableSuggester="suggestVariables")
@NotEmpty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Editable(order=200)
@Interpolative(variableSuggester="suggestVariables")
@NotEmpty
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@SuppressWarnings("unused")
private static List<InputSuggestion> suggestVariables(String matchWith) {
return BuildSpec.suggestVariables(matchWith, false, false, false);
}
}
}

View File

@ -14,7 +14,7 @@ import static io.onedev.server.model.Build.NAME_TAG;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.action.condition;
import static io.onedev.server.buildspec.job.action.condition.ActionCondition.getRuleName;
import static io.onedev.server.model.Build.NAME_BRANCH;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.action.condition;
import static io.onedev.server.buildspec.job.action.condition.ActionCondition.getRuleName;
import static io.onedev.server.model.Build.NAME_BRANCH;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,14 +2,14 @@ package io.onedev.server.buildspec.job.action.condition;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Predicate;
import io.onedev.server.OneDev;
import io.onedev.server.job.log.LogManager;
import io.onedev.server.job.log.LogService;
import io.onedev.server.model.Build;
import io.onedev.server.util.ProjectScope;
import io.onedev.server.util.criteria.Criteria;
@ -32,7 +32,7 @@ public class LogCriteria extends Criteria<Build> {
@Override
public boolean matches(Build build) {
Pattern pattern = Pattern.compile(value);
return OneDev.getInstance(LogManager.class).matches(build, pattern);
return OneDev.getInstance(LogService.class).matches(build, pattern);
}
@Override

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.action.condition;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.action.condition;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.action.condition;
import static io.onedev.server.buildspec.job.action.condition.ActionCondition.getRuleName;
import static io.onedev.server.model.Build.NAME_PROJECT;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.action.condition;
import static io.onedev.server.buildspec.job.action.condition.ActionCondition.getRuleName;
import static io.onedev.server.model.Build.NAME_TAG;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.action.condition;
import static io.onedev.server.buildspec.job.action.condition.ActionCondition.getRuleName;
import static io.onedev.server.model.Build.NAME_TAG;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.action.condition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -4,7 +4,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.BaseErrorListener;
@ -22,8 +22,8 @@ import io.onedev.commons.utils.ExplicitException;
import io.onedev.commons.utils.StringUtils;
import io.onedev.server.OneDev;
import io.onedev.server.buildspec.job.action.notificationreceiver.NotificationReceiverParser.CriteriaContext;
import io.onedev.server.entitymanager.GroupManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.service.GroupService;
import io.onedev.server.service.UserService;
import io.onedev.server.model.Build;
import io.onedev.server.model.EmailAddress;
import io.onedev.server.model.Group;
@ -60,14 +60,14 @@ public class NotificationReceiver {
for (CriteriaContext criteria: parser.receiver().criteria()) {
if (criteria.userCriteria() != null) {
String userName = getValue(criteria.userCriteria().Value());
User user = OneDev.getInstance(UserManager.class).findByName(userName);
User user = OneDev.getInstance(UserService.class).findByName(userName);
if (user != null)
addEmailAddress(emailAddresses, user);
else
throw new ExplicitException("Unable to find user '" + userName + "'");
} else if (criteria.groupCriteria() != null) {
String groupName = getValue(criteria.groupCriteria().Value());
Group group = OneDev.getInstance(GroupManager.class).find(groupName);
Group group = OneDev.getInstance(GroupService.class).find(groupName);
if (group != null) {
emailAddresses.addAll(group.getMembers().stream()
.map(it->it.getPrimaryEmailAddress())

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.job.gitcredential;
import io.onedev.k8shelper.CloneInfo;
import io.onedev.k8shelper.DefaultCloneInfo;
import io.onedev.server.OneDev;
import io.onedev.server.web.UrlManager;
import io.onedev.server.web.UrlService;
import io.onedev.server.model.Build;
import io.onedev.server.annotation.Editable;
@ -14,7 +14,7 @@ public class DefaultCredential implements GitCredential {
@Override
public CloneInfo newCloneInfo(Build build, String jobToken) {
return new DefaultCloneInfo(OneDev.getInstance(UrlManager.class).cloneUrlFor(build.getProject(), false), jobToken);
return new DefaultCloneInfo(OneDev.getInstance(UrlService.class).cloneUrlFor(build.getProject(), false), jobToken);
}
}

View File

@ -10,7 +10,7 @@ import javax.validation.constraints.NotEmpty;
import io.onedev.k8shelper.CloneInfo;
import io.onedev.k8shelper.HttpCloneInfo;
import io.onedev.server.OneDev;
import io.onedev.server.web.UrlManager;
import io.onedev.server.web.UrlService;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.validation.Validatable;
@ -45,7 +45,7 @@ public class HttpCredential implements GitCredential, Validatable {
@Override
public CloneInfo newCloneInfo(Build build, String jobToken) {
return new HttpCloneInfo(OneDev.getInstance(UrlManager.class).cloneUrlFor(build.getProject(), false),
return new HttpCloneInfo(OneDev.getInstance(UrlService.class).cloneUrlFor(build.getProject(), false),
build.getJobAuthorizationContext().getSecretValue(accessTokenSecret));
}

View File

@ -14,8 +14,8 @@ import javax.validation.constraints.NotEmpty;
import io.onedev.k8shelper.CloneInfo;
import io.onedev.k8shelper.SshCloneInfo;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.web.UrlManager;
import io.onedev.server.service.SettingService;
import io.onedev.server.web.UrlService;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.model.support.administration.SshSetting;
@ -52,10 +52,10 @@ public class SshCredential implements GitCredential, Validatable {
@Override
public CloneInfo newCloneInfo(Build build, String jobToken) {
String cloneUrl = OneDev.getInstance(UrlManager.class).cloneUrlFor(build.getProject(), true);
SettingManager settingManager = OneDev.getInstance(SettingManager.class);
SystemSetting systemSetting = settingManager.getSystemSetting();
SshSetting sshSetting = settingManager.getSshSetting();
String cloneUrl = OneDev.getInstance(UrlService.class).cloneUrlFor(build.getProject(), true);
SettingService settingService = OneDev.getInstance(SettingService.class);
SystemSetting systemSetting = settingService.getSystemSetting();
SshSetting sshSetting = settingService.getSshSetting();
StringBuilder knownHosts = new StringBuilder(systemSetting.getSshServerName()).append(" ");
try {
PublicKeyEntry.appendPublicKeyEntry(knownHosts,

View File

@ -5,7 +5,7 @@ import com.google.common.collect.Lists;
import io.onedev.server.job.log.StyleBuilder;
import io.onedev.server.web.asset.emoji.Emojis;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.projectdependency;
import java.io.Serializable;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;

View File

@ -7,13 +7,13 @@ import javax.validation.constraints.NotEmpty;
import edu.emory.mathcs.backport.java.util.Collections;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.util.EditContext;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.OmitName;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.service.BuildService;
import io.onedev.server.util.EditContext;
@Editable(order=100, name="Last Finished of Specified Job")
public class LastFinishedBuild implements BuildProvider {
@ -25,7 +25,6 @@ public class LastFinishedBuild implements BuildProvider {
private String refName;
@Editable(order=100)
@OmitName
@ChoiceProvider("getJobChoices")
@NotEmpty
public String getJobName() {
@ -51,7 +50,7 @@ public class LastFinishedBuild implements BuildProvider {
Project project = ProjectDependency.getInputProject(EditContext.get(1));
List<String> jobNames = new ArrayList<>();
if (project != null) {
jobNames.addAll(OneDev.getInstance(BuildManager.class).getAccessibleJobNames(project));
jobNames.addAll(OneDev.getInstance(BuildService.class).getAccessibleJobNames(SecurityUtils.getSubject(), project));
Collections.sort(jobNames);
}
return jobNames;
@ -59,7 +58,7 @@ public class LastFinishedBuild implements BuildProvider {
@Override
public Build getBuild(Project project) {
return OneDev.getInstance(BuildManager.class).findLastFinished(project, jobName, refName);
return OneDev.getInstance(BuildService.class).findLastFinished(project, jobName, refName);
}
@Override

View File

@ -8,7 +8,7 @@ import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.Patterns;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.ProjectService;
import io.onedev.server.model.Project;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.security.permission.AccessProject;
@ -17,7 +17,7 @@ import io.onedev.server.util.facade.ProjectCache;
import io.onedev.server.web.page.project.ProjectPage;
import io.onedev.server.web.util.WicketUtils;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ -57,8 +57,8 @@ public class ProjectDependency implements Serializable {
List<String> choices = new ArrayList<>();
Project currentProject = ((ProjectPage)WicketUtils.getPage()).getProject();
ProjectManager projectManager = OneDev.getInstance(ProjectManager.class);
ProjectCache cache = projectManager.cloneCache();
ProjectService projectService = OneDev.getInstance(ProjectService.class);
ProjectCache cache = projectService.cloneCache();
for (Project project: SecurityUtils.getAuthorizedProjects(new AccessProject())) {
if (!project.equals(currentProject))
choices.add(cache.get(project.getId()).getPath());
@ -83,7 +83,7 @@ public class ProjectDependency implements Serializable {
static Project getInputProject(EditContext editContext) {
String projectPath = (String) editContext.getInputValue("projectPath");
if (projectPath != null) {
Project project = OneDev.getInstance(ProjectManager.class).findByPath(projectPath);
Project project = OneDev.getInstance(ProjectService.class).findByPath(projectPath);
if (project != null && SecurityUtils.canReadCode(project))
return project;
}

View File

@ -8,7 +8,7 @@ import javax.validation.constraints.NotEmpty;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.server.OneDev;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.util.EditContext;
@ -59,7 +59,7 @@ public class SpecifiedBuild implements BuildProvider {
else
buildNumber = Long.parseLong(this.buildNumber);
return OneDev.getInstance(BuildManager.class).find(project, buildNumber);
return OneDev.getInstance(BuildService.class).find(project, buildNumber);
}
@Override

View File

@ -2,14 +2,14 @@ package io.onedev.server.buildspec.job.retrycondition;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Predicate;
import io.onedev.server.OneDev;
import io.onedev.server.job.log.LogManager;
import io.onedev.server.job.log.LogService;
import io.onedev.server.model.Build;
import io.onedev.server.util.ProjectScope;
import io.onedev.server.util.criteria.Criteria;
@ -33,7 +33,7 @@ public class LogCriteria extends Criteria<RetryContext> {
public boolean matches(RetryContext context) {
Pattern pattern = Pattern.compile(value);
return context.getErrorMessage() != null && pattern.matcher(context.getErrorMessage()).find()
|| OneDev.getInstance(LogManager.class).matches(context.getBuild(), pattern);
|| OneDev.getInstance(LogService.class).matches(context.getBuild(), pattern);
}
@Override

View File

@ -1,6 +1,6 @@
package io.onedev.server.buildspec.job.retrycondition;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.retrycondition;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.retrycondition;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -10,7 +10,7 @@ import static io.onedev.server.model.Build.NAME_LOG;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.job.retrycondition;
import io.onedev.server.model.Build;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
public class RetryContext {

View File

@ -1,25 +1,30 @@
package io.onedev.server.buildspec.job.trigger;
import java.util.Collection;
import java.util.List;
import javax.validation.constraints.NotEmpty;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.commons.utils.match.Matcher;
import io.onedev.commons.utils.match.PathMatcher;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Patterns;
import io.onedev.server.annotation.UserMatch;
import io.onedev.server.buildspec.job.Job;
import io.onedev.server.buildspec.job.TriggerMatch;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.ProjectService;
import io.onedev.server.event.project.ProjectEvent;
import io.onedev.server.event.project.RefUpdated;
import io.onedev.server.git.GitUtils;
import io.onedev.server.model.Project;
import io.onedev.commons.utils.match.Matcher;
import io.onedev.commons.utils.match.PathMatcher;
import io.onedev.server.util.patternset.PatternSet;
import io.onedev.server.util.usermatch.Anyone;
import io.onedev.server.web.util.SuggestionUtils;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import java.util.Collection;
import java.util.List;
@Editable(order=100, name="Branch update", description=""
+ "Job will run when code is committed. <b class='text-info'>NOTE:</b> This trigger will ignore commits "
@ -33,7 +38,9 @@ public class BranchUpdateTrigger extends JobTrigger {
private String branches;
private String paths;
private String userMatch = new Anyone().toString();
@Editable(name="Branches", order=100, placeholder="Any branch", description="Optionally specify space-separated branches "
+ "to check. Use '**' or '*' or '?' for <a href='https://docs.onedev.io/appendix/path-wildcard' target='_blank'>path wildcard match</a>. "
+ "Prefix with '-' to exclude. Leave empty to match all branches")
@ -63,6 +70,17 @@ public class BranchUpdateTrigger extends JobTrigger {
this.paths = paths;
}
@Editable(order=300, name="Applicable Users", description="Optionally specify applicable users who pushed the change")
@UserMatch
@NotEmpty
public String getUserMatch() {
return userMatch;
}
public void setUserMatch(String userMatch) {
this.userMatch = userMatch;
}
@SuppressWarnings("unused")
private static List<InputSuggestion> getPathSuggestions(String matchWith) {
return SuggestionUtils.suggestBlobs(Project.get(), matchWith);
@ -75,7 +93,7 @@ public class BranchUpdateTrigger extends JobTrigger {
} else if (refUpdated.getNewCommitId().equals(ObjectId.zeroId())) {
return false;
} else {
Repository repository = OneDev.getInstance(ProjectManager.class)
Repository repository = OneDev.getInstance(ProjectService.class)
.getRepository(refUpdated.getProject().getId());
Collection<String> changedFiles = GitUtils.getChangedFiles(
repository,
@ -92,6 +110,14 @@ public class BranchUpdateTrigger extends JobTrigger {
return true;
}
}
private boolean pushedBy(RefUpdated refUpdated) {
if (refUpdated.getUser() != null) {
return io.onedev.server.util.usermatch.UserMatch.parse(getUserMatch()).matches(refUpdated.getUser());
} else {
return true;
}
}
@Override
protected TriggerMatch triggerMatches(ProjectEvent event, Job job) {
@ -102,7 +128,7 @@ public class BranchUpdateTrigger extends JobTrigger {
if (updatedBranch != null
&& !SKIP_COMMIT.apply(event.getProject().getRevCommit(refUpdated.getNewCommitId(), true))
&& (branches == null || PatternSet.parse(branches).matches(matcher, updatedBranch))
&& touchedFile(refUpdated)) {
&& touchedFile(refUpdated) && pushedBy(refUpdated)) {
return new TriggerMatch(refUpdated.getRefName(), null, null,
getParamMatrix(), getExcludeParamMaps(), "Branch '" + updatedBranch + "' is updated");
}
@ -113,14 +139,26 @@ public class BranchUpdateTrigger extends JobTrigger {
@Override
public String getTriggerDescription() {
String description;
if (getBranches() != null && getPaths() != null)
description = String.format("When update branches '%s' and touch files '%s'", getBranches(), getPaths());
else if (getBranches() != null)
description = String.format("When update branches '%s'", getBranches());
else if (getPaths() != null)
description = String.format("When touch files '%s'", getPaths());
else
description = "When update branches";
if (getUserMatch() == null || getUserMatch().equals(new Anyone().toString())) {
if (getBranches() != null && getPaths() != null)
description = String.format("When update branches '%s' and touch files '%s'", getBranches(), getPaths());
else if (getBranches() != null)
description = String.format("When update branches '%s'", getBranches());
else if (getPaths() != null)
description = String.format("When touch files '%s'", getPaths());
else
description = "When update branches";
} else {
if (getBranches() != null && getPaths() != null)
description = String.format("When update branches '%s' and touch files '%s' and pushed by '%s'", getBranches(), getPaths(), getUserMatch());
else if (getBranches() != null)
description = String.format("When update branches '%s' and pushed by '%s'", getBranches(), getUserMatch());
else if (getPaths() != null)
description = String.format("When touch files '%s' and pushed by '%s'", getPaths(), getUserMatch());
else
description = "When pushed by '" + getUserMatch() + "'";
}
return description;
}

View File

@ -5,7 +5,7 @@ import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.job.Job;
import io.onedev.server.buildspec.job.TriggerMatch;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.service.SettingService;
import io.onedev.server.event.project.ProjectEvent;
import io.onedev.server.event.project.issue.IssueChanged;
import io.onedev.server.event.project.issue.IssueOpened;
@ -16,7 +16,7 @@ import io.onedev.server.model.support.issue.changedata.IssueStateChangeData;
import io.onedev.server.search.entity.issue.IssueQuery;
import io.onedev.server.search.entity.issue.IssueQueryParseOption;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@ -42,7 +42,7 @@ public class IssueInStateTrigger extends JobTrigger {
@SuppressWarnings("unused")
private static List<String> getStateChoices() {
return OneDev.getInstance(SettingManager.class).getIssueSetting()
return OneDev.getInstance(SettingService.class).getIssueSetting()
.getStateSpecs().stream().map(StateSpec::getName).collect(toList());
}

View File

@ -20,7 +20,7 @@ import io.onedev.server.web.util.WicketUtils;
import org.apache.wicket.Component;
import org.eclipse.jgit.revwalk.RevCommit;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.Valid;
import java.io.Serializable;
import java.util.ArrayList;

View File

@ -5,7 +5,7 @@ import io.onedev.server.OneDev;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Patterns;
import io.onedev.server.buildspec.job.TriggerMatch;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.ProjectService;
import io.onedev.server.git.GitUtils;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
@ -15,7 +15,7 @@ import io.onedev.server.util.patternset.PatternSet;
import io.onedev.server.web.util.SuggestionUtils;
import org.eclipse.jgit.lib.Repository;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.List;
@ -65,7 +65,7 @@ public abstract class PullRequestTrigger extends JobTrigger {
private boolean touchedFile(PullRequest request) {
if (getPaths() != null) {
Repository repository = OneDev.getInstance(ProjectManager.class)
Repository repository = OneDev.getInstance(ProjectService.class)
.getRepository(request.getTargetProject().getId());
Collection<String> changedFiles = GitUtils.getChangedFiles(repository,
request.getBaseCommit(), request.getLatestUpdate().getHeadCommit());

View File

@ -14,7 +14,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.util.Input;
import io.onedev.server.buildspecmodel.inputspec.Input;
public class ParamCombination implements Serializable {

View File

@ -17,7 +17,7 @@ import org.apache.commons.lang.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.ValidationException;
import java.io.Serializable;
import java.util.*;

View File

@ -6,7 +6,8 @@ import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.param.ParamCombination;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.model.Build;
import io.onedev.server.util.Input;
import io.onedev.server.buildspecmodel.inputspec.Input;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

View File

@ -4,17 +4,18 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.validation.constraints.NotEmpty;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import io.onedev.commons.utils.ExplicitException;
import io.onedev.server.buildspec.param.ParamCombination;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.model.Build;
import io.onedev.server.util.Input;
import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.param.ParamCombination;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.buildspecmodel.inputspec.Input;
import io.onedev.server.model.Build;
@Editable(name="Use value of specified parameter/secret")
public class PassthroughValues implements ValuesProvider {

View File

@ -4,7 +4,7 @@ import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.param.ParamCombination;
import io.onedev.server.model.Build;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import java.io.Serializable;
import java.util.List;

View File

@ -3,7 +3,7 @@ package io.onedev.server.buildspec.param.instance;
import java.io.Serializable;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import io.onedev.server.buildspec.param.ParamCombination;
import io.onedev.server.model.Build;

View File

@ -2,7 +2,7 @@ package io.onedev.server.buildspec.param.spec;
import java.util.List;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.Valid;
import io.onedev.server.annotation.Multiline;

View File

@ -7,15 +7,14 @@ import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.buildspec.param.spec.choiceparam.defaultmultivalueprovider.DefaultMultiValueProvider;
import io.onedev.server.buildspec.param.spec.choiceparam.defaultvalueprovider.DefaultValueProvider;
import io.onedev.server.buildspecmodel.inputspec.choiceinput.ChoiceInput;
import io.onedev.server.buildspecmodel.inputspec.choiceinput.choiceprovider.ChoiceProvider;
import io.onedev.server.buildspecmodel.inputspec.choiceinput.choiceprovider.SpecifiedChoices;
import io.onedev.server.util.EditContext;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.ShowCondition;
@Editable(order=145, name= ParamSpec.ENUMERATION)
public class ChoiceParam extends ParamSpec {
@ -39,8 +38,8 @@ public class ChoiceParam extends ParamSpec {
this.choiceProvider = choiceProvider;
}
@ShowCondition("isDefaultValueProviderVisible")
@Editable(order=1100, name="Default Value", placeholder="No default value")
@DependsOn(property="allowMultiple", value="false")
@Valid
public DefaultValueProvider getDefaultValueProvider() {
return defaultValueProvider;
@ -50,13 +49,8 @@ public class ChoiceParam extends ParamSpec {
this.defaultValueProvider = defaultValueProvider;
}
@SuppressWarnings("unused")
private static boolean isDefaultValueProviderVisible() {
return EditContext.get().getInputValue("allowMultiple").equals(false);
}
@ShowCondition("isDefaultMultiValueProviderVisible")
@Editable(order=1100, name="Default Value", placeholder="No default value")
@DependsOn(property="allowMultiple")
@Valid
public DefaultMultiValueProvider getDefaultMultiValueProvider() {
return defaultMultiValueProvider;
@ -66,11 +60,6 @@ public class ChoiceParam extends ParamSpec {
this.defaultMultiValueProvider = defaultMultiValueProvider;
}
@SuppressWarnings("unused")
private static boolean isDefaultMultiValueProviderVisible() {
return EditContext.get().getInputValue("allowMultiple").equals(true);
}
@Override
public List<String> getPossibleValues() {
return ChoiceInput.getPossibleValues(choiceProvider);

View File

@ -6,15 +6,14 @@ import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.buildspec.param.spec.ParamSpec;
import io.onedev.server.buildspec.param.spec.userchoiceparam.defaultmultivalueprovider.DefaultMultiValueProvider;
import io.onedev.server.buildspec.param.spec.userchoiceparam.defaultvalueprovider.DefaultValueProvider;
import io.onedev.server.buildspecmodel.inputspec.userchoiceinput.UserChoiceInput;
import io.onedev.server.buildspecmodel.inputspec.userchoiceinput.choiceprovider.AllUsers;
import io.onedev.server.buildspecmodel.inputspec.userchoiceinput.choiceprovider.ChoiceProvider;
import io.onedev.server.util.EditContext;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.ShowCondition;
@Editable(order=150, name= ParamSpec.USER)
public class UserChoiceParam extends ParamSpec {
@ -39,7 +38,7 @@ public class UserChoiceParam extends ParamSpec {
}
@Editable(order=1100, name="Default Value", placeholder="No default value")
@ShowCondition("isDefaultValueProviderVisible")
@DependsOn(property="allowMultiple", value="false")
@Valid
public DefaultValueProvider getDefaultValueProvider() {
return defaultValueProvider;
@ -49,13 +48,8 @@ public class UserChoiceParam extends ParamSpec {
this.defaultValueProvider = defaultValueProvider;
}
@SuppressWarnings("unused")
private static boolean isDefaultValueProviderVisible() {
return EditContext.get().getInputValue("allowMultiple").equals(false);
}
@ShowCondition("isDefaultMultiValueProviderVisible")
@Editable(order=1100, name="Default Value", placeholder="No default value")
@DependsOn(property="allowMultiple")
@Valid
public DefaultMultiValueProvider getDefaultMultiValueProvider() {
return defaultMultiValueProvider;
@ -65,11 +59,6 @@ public class UserChoiceParam extends ParamSpec {
this.defaultMultiValueProvider = defaultMultiValueProvider;
}
@SuppressWarnings("unused")
private static boolean isDefaultMultiValueProviderVisible() {
return EditContext.get().getInputValue("allowMultiple").equals(true);
}
@Override
public List<String> getPossibleValues() {
return UserChoiceInput.getPossibleValues();

View File

@ -7,6 +7,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ -82,6 +83,7 @@ public class BuildImageStep extends Step {
@Editable(order=1000, group="More Settings", description="Optionally specify registry logins to override " +
"those defined in job executor. For built-in registry, use <code>@server_url@</code> for registry url, " +
"<code>@job_token@</code> for user name, and access token secret for password secret")
@Valid
public List<RegistryLogin> getRegistryLogins() {
return registryLogins;
}

View File

@ -119,6 +119,10 @@ public class BuildImageWithKanikoStep extends CommandStep {
this.moreOptions = moreOptions;
}
static List<InputSuggestion> suggestVariables(String matchWith) {
return BuildSpec.suggestVariables(matchWith, true, true, false);
}
@Override
public Interpreter getInterpreter() {
return new DefaultInterpreter() {

View File

@ -2,6 +2,7 @@ package io.onedev.server.buildspec.step;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import io.onedev.commons.codeassist.InputSuggestion;
@ -35,6 +36,7 @@ public class CheckoutStep extends Step {
@Editable(order=100, description="By default code is cloned via an auto-generated credential, " +
"which only has read permission over current project. In case the job needs to <a href='https://docs.onedev.io/tutorials/cicd/commit-and-push' target='_blank'>push code to server</a>, " +
"you should supply custom credential with appropriate permissions here")
@Valid
@NotNull
public GitCredential getCloneCredential() {
return cloneCredential;

View File

@ -8,11 +8,11 @@ import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.IterationManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.IterationService;
import io.onedev.server.model.Iteration;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.TransactionService;
import javax.validation.constraints.NotEmpty;
import java.io.File;
@ -63,16 +63,16 @@ public class CloseIterationStep extends ServerSideStep {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger logger) {
return OneDev.getInstance(TransactionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
return OneDev.getInstance(TransactionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
Project project = build.getProject();
String iterationName = getIterationName();
IterationManager iterationManager = OneDev.getInstance(IterationManager.class);
Iteration iteration = iterationManager.findInHierarchy(project, iterationName);
IterationService iterationService = OneDev.getInstance(IterationService.class);
Iteration iteration = iterationService.findInHierarchy(project, iterationName);
if (iteration != null) {
if (build.canCloseIteration(getAccessTokenSecret())) {
iteration.setClosed(true);
iterationManager.createOrUpdate(iteration);
iterationService.createOrUpdate(iteration);
} else {
logger.error("This build is not authorized to close iteration '" + iterationName + "'");
return new ServerStepResult(false);

View File

@ -6,15 +6,16 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.k8shelper.StepFacade;
import io.onedev.server.annotation.DependsOn;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.RegEx;
import io.onedev.server.annotation.ShowCondition;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.job.EnvVar;
import io.onedev.server.buildspec.param.ParamCombination;
@ -23,7 +24,6 @@ import io.onedev.server.buildspec.step.commandinterpreter.Interpreter;
import io.onedev.server.model.Build;
import io.onedev.server.model.support.administration.jobexecutor.DockerAware;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
import io.onedev.server.util.EditContext;
@Editable(order=100, name="Execute Commands")
public class CommandStep extends Step {
@ -57,14 +57,9 @@ public class CommandStep extends Step {
public void setRunInContainer(boolean runInContainer) {
this.runInContainer = runInContainer;
}
@SuppressWarnings("unused")
private static boolean isRunInContainerEnabled() {
return (boolean) EditContext.get().getInputValue("runInContainer");
}
@Editable(order=100, name="container:image", description="Specify container image to execute commands inside")
@ShowCondition("isRunInContainerEnabled")
@DependsOn(property="runInContainer")
@Interpolative(variableSuggester="suggestVariables")
@NotEmpty
public String getImage() {
@ -76,10 +71,17 @@ public class CommandStep extends Step {
}
static List<InputSuggestion> suggestVariables(String matchWith) {
/*
* We do not support dynamic variable here as:
* 1. @file:@ notion will not work for workspace files generated in current step
* 2. It is very easy to use file content in shell script via shell utilities
* in regardless the file is generated in current or previous steps
*/
return BuildSpec.suggestVariables(matchWith, false, false, false);
}
@Editable(order=110)
@Valid
@NotNull
public Interpreter getInterpreter() {
return interpreter;
@ -92,7 +94,7 @@ public class CommandStep extends Step {
@Editable(order=8000, name="Run As", group = "More Settings", placeholder = "root", description = "Optionally specify uid:gid to run container as. " +
"<b class='text-warning'>Note:</b> This setting should be left empty if container runtime is rootless or " +
"using user namespace remapping")
@ShowCondition("isRunInContainerEnabled")
@DependsOn(property="runInContainer")
@RegEx(pattern="\\d+:\\d+", message = "Should be specified in form of <uid>:<gid>")
public String getRunAs() {
return runAs;
@ -105,7 +107,8 @@ public class CommandStep extends Step {
@Editable(order=8500, group="More Settings", description="Optionally specify registry logins to override " +
"those defined in job executor. For built-in registry, use <code>@server_url@</code> for registry url, " +
"<code>@job_token@</code> for user name, and access token secret for password secret")
@ShowCondition("isRunInContainerEnabled")
@DependsOn(property="runInContainer")
@Valid
public List<RegistryLogin> getRegistryLogins() {
return registryLogins;
}
@ -116,6 +119,7 @@ public class CommandStep extends Step {
@Editable(order=9900, name="Environment Variables", group="More Settings", description="Optionally specify environment "
+ "variables for this step")
@Valid
public List<EnvVar> getEnvVars() {
return envVars;
}
@ -125,7 +129,7 @@ public class CommandStep extends Step {
}
@Editable(order=10000, name="Enable TTY Mode", group = "More Settings", description=USE_TTY_HELP)
@ShowCondition("isRunInContainerEnabled")
@DependsOn(property="runInContainer")
public boolean isUseTTY() {
return useTTY;
}

View File

@ -1,20 +1,22 @@
package io.onedev.server.buildspec.step;
import static io.onedev.agent.DockerExecutorUtils.buildDockerConfig;
import static io.onedev.k8shelper.RegistryLoginFacade.merge;
import java.util.List;
import java.util.Map;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.k8shelper.CommandFacade;
import io.onedev.k8shelper.RegistryLoginFacade;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.Multiline;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.step.commandinterpreter.DefaultInterpreter;
import io.onedev.server.buildspec.step.commandinterpreter.Interpreter;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
import io.onedev.server.model.support.administration.jobexecutor.DockerAware;
import java.util.List;
import java.util.Map;
import static io.onedev.agent.DockerExecutorUtils.buildDockerConfig;
import static io.onedev.k8shelper.RegistryLoginFacade.merge;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
public abstract class CraneStep extends CommandStep {
@ -70,6 +72,10 @@ public abstract class CraneStep extends CommandStep {
super.setRegistryLogins(registryLogins);
}
static List<InputSuggestion> suggestVariables(String matchWith) {
return BuildSpec.suggestVariables(matchWith, true, true, false);
}
@Override
public Interpreter getInterpreter() {
return new DefaultInterpreter() {

View File

@ -10,12 +10,12 @@ import io.onedev.server.annotation.ChoiceProvider;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.git.GitUtils;
import io.onedev.server.git.service.GitService;
import io.onedev.server.git.service.RefFacade;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.web.util.SuggestionUtils;
import org.eclipse.jgit.lib.Repository;
@ -93,8 +93,8 @@ public class CreateBranchStep extends ServerSideStep {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger logger) {
return OneDev.getInstance(SessionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
return OneDev.getInstance(SessionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
Project project = build.getProject();
String branchName = getBranchName();

View File

@ -7,14 +7,14 @@ import io.onedev.k8shelper.ServerStepResult;
import io.onedev.server.OneDev;
import io.onedev.server.annotation.*;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.UserService;
import io.onedev.server.git.GitUtils;
import io.onedev.server.git.service.GitService;
import io.onedev.server.git.service.RefFacade;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.SessionService;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
@ -81,9 +81,9 @@ public class CreateTagStep extends ServerSideStep {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger logger) {
return OneDev.getInstance(SessionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
PersonIdent taggerIdent = OneDev.getInstance(UserManager.class).getSystem().asPerson();
return OneDev.getInstance(SessionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
PersonIdent taggerIdent = OneDev.getInstance(UserService.class).getSystem().asPerson();
Project project = build.getProject();
String tagName = getTagName();
@ -95,7 +95,7 @@ public class CreateTagStep extends ServerSideStep {
if (build.canCreateTag(getAccessTokenSecret(), tagName)) {
RefFacade tagRef = project.getTagRef(tagName);
if (tagRef != null)
OneDev.getInstance(ProjectManager.class).deleteTag(project, tagName);
OneDev.getInstance(ProjectService.class).deleteTag(project, tagName);
OneDev.getInstance(GitService.class).createTag(project, tagName, build.getCommitHash(),
taggerIdent, getTagMessage(), false);
} else {

View File

@ -1,15 +1,18 @@
package io.onedev.server.buildspec.step;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.buildspec.step.commandinterpreter.Interpreter;
import io.onedev.server.buildspec.step.commandinterpreter.ShellInterpreter;
import static io.onedev.server.buildspec.step.StepGroup.UTILITIES;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;
import static io.onedev.server.buildspec.step.StepGroup.UTILITIES;
import javax.validation.constraints.NotEmpty;
import io.onedev.commons.codeassist.InputSuggestion;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.buildspec.step.commandinterpreter.Interpreter;
import io.onedev.server.buildspec.step.commandinterpreter.ShellInterpreter;
@Editable(order=1110, group = UTILITIES, name="Generate File Checksum", description = "" +
"This step can only be executed by a docker aware executor")
@ -45,6 +48,10 @@ public class GenerateChecksumStep extends CommandStep {
this.targetFile = targetFile;
}
static List<InputSuggestion> suggestVariables(String matchWith) {
return BuildSpec.suggestVariables(matchWith, true, true, false);
}
@Override
public boolean isRunInContainer() {
return true;

View File

@ -6,15 +6,15 @@ import io.onedev.commons.utils.LockUtils;
import io.onedev.commons.utils.TaskLogger;
import io.onedev.k8shelper.ServerStepResult;
import io.onedev.server.OneDev;
import io.onedev.server.StorageManager;
import io.onedev.server.StorageService;
import io.onedev.server.annotation.Editable;
import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.Patterns;
import io.onedev.server.annotation.SubPath;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.util.patternset.PatternSet;
import javax.validation.constraints.NotEmpty;
@ -23,7 +23,8 @@ import java.util.List;
import static io.onedev.server.buildspec.step.StepGroup.PUBLISH;
@Editable(order=1050, group= PUBLISH, name="Artifacts")
@Editable(order=1050, group= PUBLISH, name="Artifacts", description="This step copies files from job workspace " +
"to build artifacts directory, so that they can be accessed after job is completed")
public class PublishArtifactStep extends ServerSideStep {
private static final long serialVersionUID = 1L;
@ -46,8 +47,7 @@ public class PublishArtifactStep extends ServerSideStep {
this.sourcePath = sourcePath;
}
@Editable(order=100, description="Specify files under above directory to be published. "
+ "Use * or ? for pattern match")
@Editable(order=100, description="Specify files under above directory to be published")
@Interpolative(variableSuggester="suggestVariables")
@Patterns(path=true)
@NotEmpty
@ -71,13 +71,13 @@ public class PublishArtifactStep extends ServerSideStep {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger jobLogger) {
return OneDev.getInstance(SessionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
return OneDev.getInstance(SessionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
return LockUtils.write(build.getArtifactsLockName(), () -> {
var projectId = build.getProject().getId();
var artifactsDir = OneDev.getInstance(StorageManager.class).initArtifactsDir(projectId, build.getNumber());
var artifactsDir = OneDev.getInstance(StorageService.class).initArtifactsDir(projectId, build.getNumber());
FileUtils.copyDirectory(inputDir, artifactsDir);
OneDev.getInstance(ProjectManager.class).directoryModified(projectId, artifactsDir);
OneDev.getInstance(ProjectService.class).directoryModified(projectId, artifactsDir);
return new ServerStepResult(true);
});
});

View File

@ -20,15 +20,13 @@ import io.onedev.server.annotation.Patterns;
import io.onedev.server.annotation.ProjectChoice;
import io.onedev.server.annotation.SubPath;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.SettingService;
import io.onedev.server.job.JobContext;
import io.onedev.server.job.JobManager;
import io.onedev.server.model.Build;
import io.onedev.server.job.JobService;
import io.onedev.server.model.Project;
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.util.patternset.PatternSet;
@Editable(order=1060, name="Site", group = PUBLISH, description="This step publishes specified files to be served as project web site. "
@ -94,13 +92,13 @@ public class PublishSiteStep extends ServerSideStep {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger logger) {
return OneDev.getInstance(SessionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
JobContext jobContext = OneDev.getInstance(JobManager.class).getJobContext(build.getId());
return OneDev.getInstance(SessionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
JobContext jobContext = OneDev.getInstance(JobService.class).getJobContext(build.getId());
if (jobContext.getJobExecutor().isSitePublishEnabled()) {
Project project;
if (projectPath != null) {
project = OneDev.getInstance(ProjectManager.class).findByPath(projectPath);
project = OneDev.getInstance(ProjectService.class).findByPath(projectPath);
if (project == null) {
logger.error("Unable to find project: " + projectPath);
return new ServerStepResult(false);
@ -110,13 +108,13 @@ public class PublishSiteStep extends ServerSideStep {
}
var projectId = project.getId();
LockUtils.write(project.getSiteLockName(), () -> {
File projectSiteDir = OneDev.getInstance(ProjectManager.class).getSiteDir(projectId);
File projectSiteDir = OneDev.getInstance(ProjectService.class).getSiteDir(projectId);
FileUtils.cleanDir(projectSiteDir);
FileUtils.copyDirectory(inputDir, projectSiteDir);
OneDev.getInstance(ProjectManager.class).directoryModified(projectId, projectSiteDir);
OneDev.getInstance(ProjectService.class).directoryModified(projectId, projectSiteDir);
return null;
});
String serverUrl = OneDev.getInstance(SettingManager.class).getSystemSetting().getServerUrl();
String serverUrl = OneDev.getInstance(SettingService.class).getSystemSetting().getServerUrl();
logger.log("Site published as "
+ StringUtils.stripEnd(serverUrl, "/") + "/" + project.getPath() + "/~site");
} else {
@ -127,9 +125,4 @@ public class PublishSiteStep extends ServerSideStep {
});
}
@Override
public boolean isApplicable(Build build, JobExecutor executor) {
return executor.isSitePublishEnabled();
}
}

View File

@ -6,6 +6,7 @@ import static io.onedev.server.web.translation.Translation._T;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@ -13,7 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;
import org.jspecify.annotations.Nullable;
import javax.validation.constraints.NotEmpty;
import org.eclipse.jgit.lib.AnyObjectId;
@ -44,8 +45,9 @@ import io.onedev.server.annotation.Interpolative;
import io.onedev.server.annotation.ProjectChoice;
import io.onedev.server.buildspec.BuildSpec;
import io.onedev.server.cluster.ClusterTask;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.service.BuildService;
import io.onedev.server.service.ProjectService;
import io.onedev.server.service.UserService;
import io.onedev.server.event.ListenerRegistry;
import io.onedev.server.event.project.RefUpdated;
import io.onedev.server.git.CommandUtils;
@ -54,7 +56,8 @@ import io.onedev.server.git.command.LfsFetchAllCommand;
import io.onedev.server.git.command.LfsFetchCommand;
import io.onedev.server.git.service.RefFacade;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.SessionManager;
import io.onedev.server.model.User;
import io.onedev.server.persistence.SessionService;
import io.onedev.server.security.SecurityUtils;
@Editable(order=1070, name="Pull from Remote", group=StepGroup.REPOSITORY_SYNC, description=""
@ -136,12 +139,12 @@ public class PullRepository extends SyncRepository {
@Override
public ServerStepResult run(Long buildId, File inputDir, TaskLogger logger) {
return OneDev.getInstance(SessionManager.class).call(() -> {
var build = OneDev.getInstance(BuildManager.class).load(buildId);
return OneDev.getInstance(SessionService.class).call(() -> {
var build = OneDev.getInstance(BuildService.class).load(buildId);
Project project = build.getProject();
Project targetProject;
if (getTargetProject() != null) {
targetProject = getProjectManager().findByPath(getTargetProject());
targetProject = getProjectService().findByPath(getTargetProject());
if (targetProject == null)
throw new ExplicitException("Target project not found: " + getTargetProject());
} else {
@ -158,21 +161,34 @@ public class PullRepository extends SyncRepository {
if (!authorized)
throw new ExplicitException("This build is not authorized to sync to project: " + targetProject.getPath());
Long userId;
if (getAccessTokenSecret() != null) {
userId = build.getAccessToken(getAccessTokenSecret()).getOwner().getId();
} else {
userId = User.SYSTEM_ID;
}
String remoteUrl = getRemoteUrlWithCredential(build);
Long targetProjectId = targetProject.getId();
var task = new PullTask(targetProjectId, remoteUrl, getCertificate(), getRefs(), isForce(), isWithLfs(), getProxy(), build.getSecretMasker());
getProjectManager().runOnActiveServer(targetProjectId, task);
var task = new PullTask(targetProjectId, userId, remoteUrl, getCertificate(), getRefs(), isForce(), isWithLfs(), getProxy(), build.getSecretMasker());
getProjectService().runOnActiveServer(targetProjectId, task);
return new ServerStepResult(true);
});
}
private static ProjectManager getProjectManager() {
return OneDev.getInstance(ProjectManager.class);
private static ProjectService getProjectService() {
return OneDev.getInstance(ProjectService.class);
}
private static UserService getUserService() {
return OneDev.getInstance(UserService.class);
}
private static class PullTask implements ClusterTask<Void> {
private final Long projectId;
private final Long userId;
private final String remoteUrl;
@ -188,10 +204,11 @@ public class PullRepository extends SyncRepository {
private final SecretMasker secretMasker;
PullTask(Long projectId, String remoteUrl, @Nullable String certificate,
PullTask(Long projectId, Long userId, String remoteUrl, @Nullable String certificate,
String refs, boolean force, boolean withLfs, @Nullable String proxy,
SecretMasker secretMasker) {
this.projectId = projectId;
this.userId = userId;
this.remoteUrl = remoteUrl;
this.certificate = certificate;
this.refs = refs;
@ -225,7 +242,7 @@ public class PullRepository extends SyncRepository {
var certificateFile = writeCertificate(certificate);
SecretMasker.push(secretMasker);
try {
Repository repository = getProjectManager().getRepository(projectId);
Repository repository = getProjectService().getRepository(projectId);
String defaultBranch = GitUtils.getDefaultBranch(repository);
Map<String, ObjectId> oldCommitIds = getRefCommits(repository);
@ -344,7 +361,7 @@ public class PullRepository extends SyncRepository {
git.clearArgs();
configureProxy(git, proxy);
configureCertificate(git, certificateFile);
var sinceCommitIds = getProjectManager().readLfsSinceCommits(projectId);
var sinceCommitIds = getProjectService().readLfsSinceCommits(projectId);
if (sinceCommitIds.isEmpty()) {
new LfsFetchAllCommand(git.workingDir(), remoteUrl) {
@ -362,27 +379,28 @@ public class PullRepository extends SyncRepository {
}
}.run();
}
getProjectManager().writeLfsSinceCommits(projectId, newCommitIds.values());
getProjectService().writeLfsSinceCommits(projectId, newCommitIds.values());
}
OneDev.getInstance(SessionManager.class).runAsync(() -> {
OneDev.getInstance(SessionService.class).runAsync(() -> {
try {
// Access db connection in a separate thread to avoid possible deadlock, as
// the parent thread is blocking another thread holding database connections
var project = getProjectManager().load(projectId);
var project = getProjectService().load(projectId);
var user = getUserService().load(userId);
MapDifference<String, ObjectId> difference = difference(oldCommitIds, newCommitIds);
ListenerRegistry registry = OneDev.getInstance(ListenerRegistry.class);
for (Map.Entry<String, ObjectId> entry : difference.entriesOnlyOnLeft().entrySet()) {
if (RefUpdated.isValidRef(entry.getKey()))
registry.post(new RefUpdated(project, entry.getKey(), entry.getValue(), ObjectId.zeroId()));
registry.post(new RefUpdated(user, project, entry.getKey(), entry.getValue(), ObjectId.zeroId()));
}
for (Map.Entry<String, ObjectId> entry : difference.entriesOnlyOnRight().entrySet()) {
if (RefUpdated.isValidRef(entry.getKey()))
registry.post(new RefUpdated(project, entry.getKey(), ObjectId.zeroId(), entry.getValue()));
registry.post(new RefUpdated(user, project, entry.getKey(), ObjectId.zeroId(), entry.getValue()));
}
for (Map.Entry<String, ValueDifference<ObjectId>> entry : difference.entriesDiffering().entrySet()) {
if (RefUpdated.isValidRef(entry.getKey())) {
registry.post(new RefUpdated(project, entry.getKey(),
registry.post(new RefUpdated(user, project, entry.getKey(),
entry.getValue().leftValue(), entry.getValue().rightValue()));
}
}

Some files were not shown because too many files have changed in this diff Show More