Skip to content

Commit e2c76e6

Browse files
authored
Merge pull request #412 from conductor-oss/server-lite
[Beta] Conductor-Lite: Server for running conductor server locally
2 parents 12f06de + 8d1984d commit e2c76e6

File tree

10 files changed

+373
-1
lines changed

10 files changed

+373
-1
lines changed

.github/workflows/publish.yml

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ on:
44
types:
55
- released
66
- prereleased
7-
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Version to publish (e.g., v1.0.0)'
11+
required: true
812
permissions:
913
contents: read
1014

@@ -38,3 +42,39 @@ jobs:
3842
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.SIGNING_KEY_ID }}
3943
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
4044
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
45+
publish-server:
46+
runs-on: ubuntu-latest
47+
name: Gradle Build and Publish
48+
steps:
49+
- uses: actions/checkout@v3
50+
- name: Set up Zulu JDK 17
51+
uses: actions/setup-java@v3
52+
with:
53+
distribution: 'zulu'
54+
java-version: '17'
55+
- name: Cache Gradle packages
56+
uses: actions/cache@v3
57+
with:
58+
path: |
59+
~/.gradle/caches
60+
~/.gradle/wrapper
61+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
62+
restore-keys: |
63+
${{ runner.os }}-gradle-
64+
- name: Create Standalone server
65+
run: |
66+
export VERSION="${{github.ref_name}}"
67+
export PUBLISH_VERSION=`echo ${VERSION:1}`
68+
echo Publishing version $PUBLISH_VERSION
69+
cd server-lite
70+
./build_ui.sh
71+
../gradlew clean build -x test -Pversion=$PUBLISH_VERSION
72+
- name: Upload JAR to GitHub Release
73+
uses: actions/upload-release-asset@v1
74+
with:
75+
upload_url: ${{ steps.create_release.outputs.upload_url }}
76+
asset_path: build/libs/*lite.jar
77+
asset_name: conductor-server-lite.jar
78+
asset_content_type: application/java-archive
79+
env:
80+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/publish_build.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
export PUBLISH_VERSION=`echo ${VERSION:1}`
4141
echo Publishing version $PUBLISH_VERSION
4242
cd server-lite
43+
./build_ui.sh
4344
../gradlew clean build -x test -Pversion=$PUBLISH_VERSION
4445
- name: Upload JAR to GitHub Release
4546
uses: softprops/action-gh-release@v2

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@ build/
3838

3939

4040
.qodo
41+
conductorosstest.db
42+
conductorosstest.db
43+
/server-lite/src/main/resources/static
44+
yarn.lock

server-lite/build.gradle

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2023 Conductor authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
* <p>
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* <p>
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
plugins {
15+
id 'org.springframework.boot'
16+
}
17+
18+
dependencies {
19+
implementation project(':conductor-core')
20+
implementation project(':conductor-rest')
21+
implementation project(':conductor-grpc-server')
22+
23+
//Event Systems
24+
implementation project(':conductor-amqp')
25+
implementation project(':conductor-nats')
26+
implementation project(':conductor-nats-streaming')
27+
implementation project(':conductor-awssqs-event-queue')
28+
implementation project(':conductor-kafka-event-queue')
29+
30+
//External Payload Storage
31+
implementation project(':conductor-azureblob-storage')
32+
implementation project(':conductor-postgres-external-storage')
33+
implementation project(':conductor-awss3-storage')
34+
35+
36+
//Persistence
37+
implementation project(':conductor-sqlite-persistence')
38+
39+
40+
41+
42+
//System Tasks
43+
implementation project(':conductor-http-task')
44+
implementation project(':conductor-json-jq-task')
45+
implementation project(':conductor-kafka')
46+
47+
//Metrics
48+
implementation project(':conductor-metrics')
49+
50+
//Event Listener
51+
implementation project(':conductor-workflow-event-listener')
52+
53+
54+
implementation 'org.springframework.boot:spring-boot-starter'
55+
implementation 'org.springframework.boot:spring-boot-starter-validation'
56+
implementation 'org.springframework.boot:spring-boot-starter-web'
57+
implementation 'org.springframework.retry:spring-retry'
58+
59+
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
60+
implementation 'org.apache.logging.log4j:log4j-web'
61+
implementation "redis.clients:jedis:${revJedis}"
62+
implementation "org.postgresql:postgresql:${revPostgres}"
63+
implementation "org.apache.lucene:lucene-core:${revApacheLucene}"
64+
65+
implementation 'org.springframework.boot:spring-boot-starter-actuator'
66+
implementation ("io.orkes.queues:orkes-conductor-queues:${revOrkesQueues}") {
67+
exclude group: 'com.netflix.conductor', module: 'conductor-core'
68+
}
69+
70+
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${revSpringDoc}"
71+
72+
73+
runtimeOnly "org.glassfish.jaxb:jaxb-runtime:${revJAXB}"
74+
75+
testImplementation project(':conductor-rest')
76+
testImplementation project(':conductor-common')
77+
testImplementation "io.grpc:grpc-testing:${revGrpc}"
78+
testImplementation "com.google.protobuf:protobuf-java:${revProtoBuf}"
79+
testImplementation "io.grpc:grpc-protobuf:${revGrpc}"
80+
testImplementation "io.grpc:grpc-stub:${revGrpc}"
81+
}
82+
83+
jar {
84+
enabled = true
85+
}
86+
87+
bootJar {
88+
mainClass = 'org.conductoross.conductor.Conductor'
89+
archiveClassifier = 'standalone'
90+
}
91+
92+
publishing {
93+
publications {
94+
mavenJava(MavenPublication) {
95+
artifact bootJar
96+
}
97+
}
98+
}
99+
100+
// https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#integrating-with-actuator.build-info
101+
// This will configure a BuildInfo task named bootBuildInfo
102+
springBoot {
103+
buildInfo()
104+
}
105+
106+
compileJava.dependsOn bootBuildInfo

server-lite/build_ui.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cd ../ui
2+
pwd
3+
yarn install
4+
yarn build
5+
echo "Done building UI, copying the UI files to server"
6+
cd ..
7+
pwd
8+
rm -rf server-lite/src/main/resources/static
9+
mv ui/build/ server-lite/src/main/resources/static
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2021 Conductor Authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
* <p>
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* <p>
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.conductoross.conductor;
14+
15+
import java.io.IOException;
16+
import java.util.Properties;
17+
18+
import org.apache.commons.lang3.StringUtils;
19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
import org.springframework.boot.SpringApplication;
22+
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
24+
import org.springframework.context.annotation.ComponentScan;
25+
import org.springframework.context.annotation.FilterType;
26+
import org.springframework.core.io.FileSystemResource;
27+
28+
import com.netflix.conductor.rest.config.RestConfiguration;
29+
30+
// Prevents from the datasource beans to be loaded, AS they are needed only for specific databases.
31+
// In case that SQL database is selected this class will be imported back in the appropriate
32+
// database persistence module.
33+
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
34+
@ComponentScan(
35+
basePackages = {"com.netflix.conductor", "io.orkes.conductor", "org.conductoross"},
36+
excludeFilters =
37+
@ComponentScan.Filter(
38+
type = FilterType.ASSIGNABLE_TYPE,
39+
classes = {RestConfiguration.class}))
40+
public class Conductor {
41+
42+
private static final Logger log = LoggerFactory.getLogger(Conductor.class);
43+
44+
public static void main(String[] args) throws IOException {
45+
loadExternalConfig();
46+
47+
SpringApplication.run(Conductor.class, args);
48+
}
49+
50+
/**
51+
* Reads properties from the location specified in <code>CONDUCTOR_CONFIG_FILE</code> and sets
52+
* them as system properties so they override the default properties.
53+
*
54+
* <p>Spring Boot property hierarchy is documented here,
55+
* https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
56+
*
57+
* @throws IOException if file can't be read.
58+
*/
59+
private static void loadExternalConfig() throws IOException {
60+
String configFile = System.getProperty("CONDUCTOR_CONFIG_FILE2");
61+
if (StringUtils.isBlank(configFile)) {
62+
configFile = System.getenv("CONDUCTOR_CONFIG_FILE2");
63+
}
64+
if (StringUtils.isNotBlank(configFile)) {
65+
log.info("Loading {}", configFile);
66+
FileSystemResource resource = new FileSystemResource(configFile);
67+
if (resource.exists()) {
68+
Properties properties = new Properties();
69+
properties.load(resource.getInputStream());
70+
properties.forEach(
71+
(key, value) -> System.setProperty((String) key, (String) value));
72+
log.info("Loaded {} properties from {}", properties.size(), configFile);
73+
} else {
74+
log.warn("Ignoring {} since it does not exist", configFile);
75+
}
76+
}
77+
}
78+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2025 Conductor Authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
* <p>
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* <p>
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.conductoross.conductor;
14+
15+
import org.springframework.context.annotation.Configuration;
16+
import org.springframework.core.Ordered;
17+
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
18+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
19+
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
20+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
21+
22+
import lombok.extern.slf4j.Slf4j;
23+
24+
import static org.springframework.http.MediaType.APPLICATION_JSON;
25+
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
26+
import static org.springframework.http.MediaType.TEXT_PLAIN;
27+
28+
@Configuration
29+
@Slf4j
30+
public class RestConfiguration implements WebMvcConfigurer {
31+
32+
private final SpaInterceptor spaInterceptor;
33+
34+
public RestConfiguration(SpaInterceptor spaInterceptor) {
35+
this.spaInterceptor = spaInterceptor;
36+
log.info("spaInterceptor: {}", spaInterceptor);
37+
}
38+
39+
@Override
40+
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
41+
configurer
42+
.favorParameter(false)
43+
.favorPathExtension(false)
44+
.ignoreAcceptHeader(true)
45+
.defaultContentType(APPLICATION_JSON, TEXT_PLAIN, APPLICATION_OCTET_STREAM);
46+
}
47+
48+
@Override
49+
public void addInterceptors(InterceptorRegistry registry) {
50+
if (spaInterceptor != null) {
51+
registry.addInterceptor(spaInterceptor)
52+
.excludePathPatterns("/api/**")
53+
.excludePathPatterns("/actuator/**")
54+
.excludePathPatterns("/health/**")
55+
.excludePathPatterns("/v3/api-docs")
56+
.excludePathPatterns("/v3/api-docs/**")
57+
.excludePathPatterns("/swagger-ui/**")
58+
.order(Ordered.HIGHEST_PRECEDENCE);
59+
}
60+
}
61+
62+
@Override
63+
public void addResourceHandlers(ResourceHandlerRegistry registry) {
64+
if (spaInterceptor != null) {
65+
log.info("Serving static resources");
66+
registry.addResourceHandler("/static/ui/**")
67+
.addResourceLocations("classpath:/static/ui/");
68+
}
69+
}
70+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2025 Conductor Authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
* <p>
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* <p>
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.conductoross.conductor;
14+
15+
import org.springframework.stereotype.Component;
16+
import org.springframework.web.servlet.HandlerInterceptor;
17+
18+
import jakarta.servlet.http.HttpServletRequest;
19+
import jakarta.servlet.http.HttpServletResponse;
20+
import lombok.extern.slf4j.Slf4j;
21+
22+
@Component
23+
@Slf4j
24+
public class SpaInterceptor implements HandlerInterceptor {
25+
26+
public SpaInterceptor() {
27+
log.info("Serving UI on /");
28+
}
29+
30+
@Override
31+
public boolean preHandle(
32+
HttpServletRequest request, HttpServletResponse response, Object handler)
33+
throws Exception {
34+
String path = request.getRequestURI();
35+
log.debug("Service SPA page {}", path);
36+
37+
// Skip API, health checks, actuator, and static resources
38+
if (path.startsWith("/api/")
39+
|| path.equals("/health")
40+
|| path.equals("/api-docs")
41+
|| path.equals("/error")
42+
|| path.contains(".")) {
43+
return true;
44+
}
45+
46+
// Forward to index.html
47+
request.getRequestDispatcher("/index.html").forward(request, response);
48+
return false;
49+
}
50+
}

0 commit comments

Comments
 (0)