diff --git a/.run/Application - 1.run.xml b/.run/Application - 1.run.xml
index 24482ec71..fa31f637e 100644
--- a/.run/Application - 1.run.xml
+++ b/.run/Application - 1.run.xml
@@ -14,6 +14,7 @@
+
diff --git a/core/src/main/java/com/flowci/core/agent/domain/ShellIn.java b/core/src/main/java/com/flowci/core/agent/domain/ShellIn.java
index 9cbf5eb89..d0eb89fa3 100644
--- a/core/src/main/java/com/flowci/core/agent/domain/ShellIn.java
+++ b/core/src/main/java/com/flowci/core/agent/domain/ShellIn.java
@@ -1,10 +1,8 @@
package com.flowci.core.agent.domain;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flowci.domain.DockerOption;
import com.flowci.domain.StringVars;
import com.flowci.domain.Vars;
-import com.flowci.util.StringHelper;
import com.google.common.base.Strings;
import lombok.Getter;
import lombok.Setter;
@@ -37,14 +35,13 @@ public enum ShellType {
private List dockers;
- @JsonIgnore
- private String condition;
-
private List bash;
private List pwsh;
- private int timeout = 1800;
+ private int timeout = 1800; // from StepNode.timeout
+
+ private int retry; // from StepNode.retry
private Vars inputs;
@@ -75,9 +72,4 @@ public void addEnvFilters(Set exports) {
public void addInputs(StringVars vars) {
inputs.putAll(vars);
}
-
- @JsonIgnore
- public boolean hasCondition() {
- return StringHelper.hasValue(condition);
- }
}
diff --git a/core/src/main/java/com/flowci/core/agent/manager/AgentEventManager.java b/core/src/main/java/com/flowci/core/agent/manager/AgentEventManager.java
index b28975ca8..2b137f1ee 100644
--- a/core/src/main/java/com/flowci/core/agent/manager/AgentEventManager.java
+++ b/core/src/main/java/com/flowci/core/agent/manager/AgentEventManager.java
@@ -54,20 +54,19 @@ public void writeMessage(String token, ResponseMessage msg) {
try {
byte[] bytes = objectMapper.writeValueAsBytes(msg);
- session.sendMessage(new BinaryMessage(bytes));
+ writeMessage(session, bytes);
} catch (IOException e) {
log.warn("Unable to write response message for agent {}: {}", token, e.getMessage());
}
}
- public void writeMessage(String token, byte[] bytes) throws IOException {
+ public void writeMessage(String token, byte[] bytes) {
WebSocketSession session = agentSessionStore.get(token);
if (session == null) {
log.warn("Agent {} not connected", token);
return;
}
-
- session.sendMessage(new BinaryMessage(bytes));
+ writeMessage(session, bytes);
}
@Override
@@ -123,12 +122,22 @@ public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
private void writeMessage(WebSocketSession session, ResponseMessage msg) {
try {
byte[] bytes = objectMapper.writeValueAsBytes(msg);
- session.sendMessage(new BinaryMessage(bytes));
+ writeMessage(session, bytes);
} catch (IOException e) {
log.warn(e);
}
}
+ private void writeMessage(WebSocketSession session, byte[] body) {
+ synchronized (session) {
+ try {
+ session.sendMessage(new BinaryMessage(body));
+ } catch (IOException e) {
+ log.warn(e);
+ }
+ }
+ }
+
private void onConnected(WebSocketSession session, String token, byte[] body) {
try {
AgentInit init = objectMapper.readValue(body, AgentInit.class);
diff --git a/core/src/main/java/com/flowci/core/common/config/AppConfig.java b/core/src/main/java/com/flowci/core/common/config/AppConfig.java
index bfc7a42a7..db90ca718 100644
--- a/core/src/main/java/com/flowci/core/common/config/AppConfig.java
+++ b/core/src/main/java/com/flowci/core/common/config/AppConfig.java
@@ -34,6 +34,7 @@
import org.springframework.core.ResolvableType;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.PostConstruct;
import java.io.IOException;
@@ -80,8 +81,10 @@ public ObjectMapper objectMapper() {
}
@Bean("appTaskExecutor")
- public TaskExecutor getAppTaskExecutor() {
- return ThreadHelper.createTaskExecutor(100, 100, 50, "app-task-");
+ public ThreadPoolTaskExecutor getAppTaskExecutor() {
+ int corePoolSize = appProperties.getCorePoolSize();
+ int maxPoolSize = appProperties.getMaxPoolSize();
+ return ThreadHelper.createTaskExecutor(maxPoolSize, corePoolSize, 10, "app-task-");
}
@Bean(name = "applicationEventMulticaster")
diff --git a/core/src/main/java/com/flowci/core/common/config/AppProperties.java b/core/src/main/java/com/flowci/core/common/config/AppProperties.java
index 90605a627..49a2b3da5 100644
--- a/core/src/main/java/com/flowci/core/common/config/AppProperties.java
+++ b/core/src/main/java/com/flowci/core/common/config/AppProperties.java
@@ -57,6 +57,10 @@ public class AppProperties {
private boolean socketContainer;
+ private int corePoolSize;
+
+ private int maxPoolSize;
+
@Bean("zkProperties")
@ConfigurationProperties(prefix = "app.zookeeper")
public Zookeeper zk() {
diff --git a/core/src/main/java/com/flowci/core/common/manager/ConditionManager.java b/core/src/main/java/com/flowci/core/common/manager/ConditionManager.java
new file mode 100644
index 000000000..d48830003
--- /dev/null
+++ b/core/src/main/java/com/flowci/core/common/manager/ConditionManager.java
@@ -0,0 +1,16 @@
+package com.flowci.core.common.manager;
+
+import com.flowci.domain.Vars;
+import groovy.util.ScriptException;
+
+import javax.annotation.Nullable;
+
+public interface ConditionManager {
+
+ /**
+ * Verify the input condition is groovy script with boolean return or not
+ */
+ void verify(@Nullable String condition) throws ScriptException;
+
+ boolean run(@Nullable String groovyScript, @Nullable Vars envs) throws ScriptException;
+}
diff --git a/core/src/main/java/com/flowci/core/job/manager/ConditionManagerImpl.java b/core/src/main/java/com/flowci/core/common/manager/ConditionManagerImpl.java
similarity index 64%
rename from core/src/main/java/com/flowci/core/job/manager/ConditionManagerImpl.java
rename to core/src/main/java/com/flowci/core/common/manager/ConditionManagerImpl.java
index 08921eb3f..65fafd2a0 100644
--- a/core/src/main/java/com/flowci/core/job/manager/ConditionManagerImpl.java
+++ b/core/src/main/java/com/flowci/core/common/manager/ConditionManagerImpl.java
@@ -1,7 +1,7 @@
-package com.flowci.core.job.manager;
+package com.flowci.core.common.manager;
-import com.flowci.core.agent.domain.CmdIn;
-import com.flowci.core.agent.domain.ShellIn;
+import com.flowci.domain.Vars;
+import com.google.common.base.Strings;
import groovy.lang.Binding;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.GroovyShell;
@@ -11,7 +11,11 @@
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
-import java.util.concurrent.*;
+import javax.annotation.Nullable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
@Log4j2
@Component
@@ -23,23 +27,29 @@ public class ConditionManagerImpl implements ConditionManager {
private ThreadPoolTaskExecutor jobConditionExecutor;
@Override
- public boolean run(CmdIn in) throws ScriptException {
- if (!(in instanceof ShellIn)) {
- return true;
+ public void verify(@Nullable String condition) throws ScriptException {
+ try {
+ new GroovyShell().parse(condition);
+ } catch (Exception e) {
+ throw new ScriptException("Invalid groovy condition: " + e.getMessage());
}
+ }
- ShellIn shell = (ShellIn) in;
- if (!shell.hasCondition()) {
+ @Override
+ public boolean run(String groovyScript, Vars envs) throws ScriptException {
+ if (Strings.isNullOrEmpty(groovyScript)) {
return true;
}
Future submit = jobConditionExecutor.submit(() -> {
Binding binding = new Binding();
- shell.getInputs().forEach(binding::setVariable);
+ if (envs != null) {
+ envs.forEach(binding::setVariable);
+ }
try {
GroovyShell groovy = new GroovyShell(binding);
- Object value = groovy.evaluate(shell.getCondition());
+ Object value = groovy.evaluate(groovyScript);
if (value instanceof Boolean) {
return (Boolean) value;
}
diff --git a/core/src/main/java/com/flowci/core/common/service/SettingServiceImpl.java b/core/src/main/java/com/flowci/core/common/service/SettingServiceImpl.java
index a6fdbc493..1e365abb0 100644
--- a/core/src/main/java/com/flowci/core/common/service/SettingServiceImpl.java
+++ b/core/src/main/java/com/flowci/core/common/service/SettingServiceImpl.java
@@ -35,18 +35,21 @@ public class SettingServiceImpl implements SettingService {
public void setDefaultValue() {
taskManager.run("init-default-settings", () -> {
Optional optional = settingsDao.findById(Settings.DefaultId);
- if (!optional.isPresent()) {
- String address = serverProperties.getAddress().toString().replace("/", "");
- String serverUrl = environment.getProperty(
- Variables.App.ServerUrl,
- String.format("http://%s:%s", address, serverProperties.getPort())
- );
+ String address = serverProperties.getAddress().toString().replace("/", "");
+ String serverUrl = environment.getProperty(
+ Variables.App.ServerUrl,
+ String.format("http://%s:%s", address, serverProperties.getPort())
+ );
- Settings s = new Settings();
- s.setServerUrl(serverUrl);
- settingsDao.save(s);
+ Settings s = new Settings();
+
+ if (optional.isPresent()) {
+ s = optional.get();
}
+
+ s.setServerUrl(serverUrl);
+ settingsDao.save(s);
});
}
diff --git a/core/src/main/java/com/flowci/core/flow/service/FlowServiceImpl.java b/core/src/main/java/com/flowci/core/flow/service/FlowServiceImpl.java
index b21acb969..4a3d28cb9 100644
--- a/core/src/main/java/com/flowci/core/flow/service/FlowServiceImpl.java
+++ b/core/src/main/java/com/flowci/core/flow/service/FlowServiceImpl.java
@@ -17,20 +17,29 @@
package com.flowci.core.flow.service;
import com.flowci.core.common.domain.Variables;
+import com.flowci.core.common.manager.ConditionManager;
import com.flowci.core.common.manager.SessionManager;
import com.flowci.core.common.manager.SpringEventManager;
import com.flowci.core.flow.dao.FlowDao;
import com.flowci.core.flow.dao.FlowUserDao;
+import com.flowci.core.flow.dao.YmlDao;
import com.flowci.core.flow.domain.ConfirmOption;
import com.flowci.core.flow.domain.Flow;
import com.flowci.core.flow.domain.Flow.Status;
+import com.flowci.core.flow.domain.WebhookStatus;
+import com.flowci.core.flow.domain.Yml;
import com.flowci.core.flow.event.FlowCreatedEvent;
import com.flowci.core.flow.event.FlowDeletedEvent;
import com.flowci.core.flow.event.FlowInitEvent;
+import com.flowci.core.job.domain.Job;
+import com.flowci.core.job.event.CreateNewJobEvent;
import com.flowci.core.secret.domain.Secret;
import com.flowci.core.secret.event.CreateAuthEvent;
import com.flowci.core.secret.event.CreateRsaEvent;
import com.flowci.core.secret.event.GetSecretEvent;
+import com.flowci.core.trigger.domain.GitPingTrigger;
+import com.flowci.core.trigger.domain.GitTrigger;
+import com.flowci.core.trigger.event.GitHookEvent;
import com.flowci.core.user.event.UserDeletedEvent;
import com.flowci.domain.*;
import com.flowci.exception.ArgumentException;
@@ -38,8 +47,11 @@
import com.flowci.exception.NotFoundException;
import com.flowci.exception.StatusException;
import com.flowci.store.FileManager;
+import com.flowci.tree.FlowNode;
+import com.flowci.tree.YmlParser;
import com.flowci.util.ObjectsHelper;
import com.google.common.collect.Sets;
+import groovy.util.ScriptException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
@@ -60,6 +72,9 @@ public class FlowServiceImpl implements FlowService {
@Autowired
private FlowDao flowDao;
+ @Autowired
+ private YmlDao ymlDao;
+
@Autowired
private FlowUserDao flowUserDao;
@@ -72,6 +87,9 @@ public class FlowServiceImpl implements FlowService {
@Autowired
private FileManager fileManager;
+ @Autowired
+ private ConditionManager conditionManager;
+
@Autowired
private YmlService ymlService;
@@ -293,10 +311,64 @@ public void deleteUserFromFlow(UserDeletedEvent event) {
// TODO:
}
+ @EventListener
+ public void onGitHookEvent(GitHookEvent event) {
+ GitTrigger trigger = event.getTrigger();
+ Flow flow = flowDao.findByName(event.getFlow());
+
+ if (event.isPingEvent()) {
+ GitPingTrigger ping = (GitPingTrigger) trigger;
+
+ WebhookStatus ws = new WebhookStatus();
+ ws.setAdded(true);
+ ws.setCreatedAt(ping.getCreatedAt());
+ ws.setEvents(ping.getEvents());
+
+ flow.setWebhookStatus(ws);
+ flowDao.save(flow);
+ return;
+ }
+
+ if (trigger.isSkip()) {
+ log.info("Ignore git trigger {} since skip message", trigger);
+ return;
+ }
+
+ Optional optional = ymlDao.findById(flow.getId());
+ if (!optional.isPresent()) {
+ log.warn("No available yml for flow {}", flow.getName());
+ return;
+ }
+
+ Yml yml = optional.get();
+ FlowNode root = YmlParser.load(flow.getName(), yml.getRaw());
+ if (!canStartJob(root, trigger)) {
+ log.debug("Cannot start job, condition not match: {}", root.getCondition());
+ return;
+ }
+
+ StringVars gitInput = trigger.toVariableMap();
+ Job.Trigger jobTrigger = trigger.toJobTrigger();
+
+ eventManager.publish(new CreateNewJobEvent(this, flow, yml.getRaw(), jobTrigger, gitInput));
+ }
+
+
// ====================================================================
// %% Utils
// ====================================================================
+ private boolean canStartJob(FlowNode root, GitTrigger trigger) {
+ try {
+ String groovy = root.getCondition();
+ Vars envs = trigger.toVariableMap();
+ return conditionManager.run(groovy, envs);
+ } catch (ScriptException e) {
+ log.warn("Illegal groovy script at condition section", e);
+ return false;
+ }
+ }
+
private void setupDefaultVars(Flow flow) {
Vars localVars = flow.getLocally();
localVars.put(Variables.Flow.Name, VarValue.of(flow.getName(), VarType.STRING, false));
diff --git a/core/src/main/java/com/flowci/core/flow/service/FlowSettingServiceImpl.java b/core/src/main/java/com/flowci/core/flow/service/FlowSettingServiceImpl.java
index d0b364bfe..92c2347e4 100644
--- a/core/src/main/java/com/flowci/core/flow/service/FlowSettingServiceImpl.java
+++ b/core/src/main/java/com/flowci/core/flow/service/FlowSettingServiceImpl.java
@@ -16,35 +16,20 @@
package com.flowci.core.flow.service;
-import com.flowci.core.common.manager.SpringEventManager;
import com.flowci.core.common.manager.VarManager;
import com.flowci.core.flow.dao.FlowDao;
-import com.flowci.core.flow.dao.YmlDao;
import com.flowci.core.flow.domain.Flow;
import com.flowci.core.flow.domain.Settings;
import com.flowci.core.flow.domain.WebhookStatus;
-import com.flowci.core.flow.domain.Yml;
-import com.flowci.core.job.domain.Job;
-import com.flowci.core.job.event.CreateNewJobEvent;
-import com.flowci.core.trigger.domain.GitPingTrigger;
-import com.flowci.core.trigger.domain.GitPushTrigger;
-import com.flowci.core.trigger.domain.GitTrigger;
-import com.flowci.core.trigger.event.GitHookEvent;
-import com.flowci.domain.StringVars;
import com.flowci.domain.VarValue;
import com.flowci.exception.ArgumentException;
-import com.flowci.tree.FlowNode;
-import com.flowci.tree.TriggerFilter;
-import com.flowci.tree.YmlParser;
import com.flowci.util.StringHelper;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
/**
* @author yang
@@ -57,12 +42,6 @@ public class FlowSettingServiceImpl implements FlowSettingService {
@Autowired
private FlowDao flowDao;
- @Autowired
- private YmlDao ymlDao;
-
- @Autowired
- private SpringEventManager eventManager;
-
@Autowired
private VarManager varManager;
@@ -126,62 +105,4 @@ public void remove(Flow flow, List vars) {
flowDao.save(flow);
}
-
- @EventListener
- public void onGitHookEvent(GitHookEvent event) {
- GitTrigger trigger = event.getTrigger();
- Flow flow = flowDao.findByName(event.getFlow());
-
- if (event.isPingEvent()) {
- GitPingTrigger ping = (GitPingTrigger) trigger;
-
- WebhookStatus ws = new WebhookStatus();
- ws.setAdded(true);
- ws.setCreatedAt(ping.getCreatedAt());
- ws.setEvents(ping.getEvents());
-
- flow.setWebhookStatus(ws);
- flowDao.save(flow);
- return;
- }
-
- if (trigger.isSkip()) {
- log.info("Ignore git trigger {} since skip message", trigger);
- return;
- }
-
- Optional optional = ymlDao.findById(flow.getId());
- if (!optional.isPresent()) {
- log.warn("No available yml for flow {}", flow.getName());
- return;
- }
-
- Yml yml = optional.get();
- FlowNode root = YmlParser.load(flow.getName(), yml.getRaw());
- if (!canStartJob(root, trigger)) {
- log.debug("Cannot start job since filter not matched on flow {}", flow.getName());
- return;
- }
-
- StringVars gitInput = trigger.toVariableMap();
- Job.Trigger jobTrigger = trigger.toJobTrigger();
-
- eventManager.publish(new CreateNewJobEvent(this, flow, yml.getRaw(), jobTrigger, gitInput));
- }
-
- private boolean canStartJob(FlowNode root, GitTrigger trigger) {
- TriggerFilter condition = root.getTrigger();
-
- if (trigger.getEvent() == GitTrigger.GitEvent.PUSH) {
- GitPushTrigger pushTrigger = (GitPushTrigger) trigger;
- return condition.isMatchBranch(pushTrigger.getRef());
- }
-
- if (trigger.getEvent() == GitTrigger.GitEvent.TAG) {
- GitPushTrigger tagTrigger = (GitPushTrigger) trigger;
- return condition.isMatchTag(tagTrigger.getRef());
- }
-
- return true;
- }
}
diff --git a/core/src/main/java/com/flowci/core/flow/service/YmlServiceImpl.java b/core/src/main/java/com/flowci/core/flow/service/YmlServiceImpl.java
index 52cfb20d0..598570648 100644
--- a/core/src/main/java/com/flowci/core/flow/service/YmlServiceImpl.java
+++ b/core/src/main/java/com/flowci/core/flow/service/YmlServiceImpl.java
@@ -16,6 +16,7 @@
package com.flowci.core.flow.service;
+import com.flowci.core.common.manager.ConditionManager;
import com.flowci.core.common.manager.SpringEventManager;
import com.flowci.core.flow.dao.FlowDao;
import com.flowci.core.flow.dao.YmlDao;
@@ -27,6 +28,7 @@
import com.flowci.exception.ArgumentException;
import com.flowci.exception.NotFoundException;
import com.flowci.tree.FlowNode;
+import com.flowci.tree.Node;
import com.flowci.tree.StepNode;
import com.flowci.tree.YmlParser;
import com.flowci.util.StringHelper;
@@ -53,6 +55,9 @@ public class YmlServiceImpl implements YmlService {
@Autowired
private SpringEventManager eventManager;
+ @Autowired
+ private ConditionManager conditionManager;
+
//====================================================================
// %% Public function
//====================================================================
@@ -91,6 +96,11 @@ public Yml saveYml(Flow flow, String yml) {
throw hasErr.get();
}
+ hasErr = verifyCondition(root);
+ if (hasErr.isPresent()) {
+ throw hasErr.get();
+ }
+
Yml ymlObj = new Yml(flow.getId(), yml);
ymlDao.save(ymlObj);
@@ -113,6 +123,25 @@ public void delete(Flow flow) {
}
}
+ private Optional verifyCondition(Node node) {
+ try {
+ if (node.hasCondition()) {
+ conditionManager.verify(node.getCondition());
+ }
+
+ for (Node child : node.getChildren()) {
+ Optional exception = verifyCondition(child);
+ if (exception.isPresent()) {
+ return exception;
+ }
+ }
+
+ return Optional.empty();
+ } catch (Throwable e) {
+ return Optional.of(new RuntimeException(e.getMessage()));
+ }
+ }
+
/**
* Try to fetch plugins
*
diff --git a/core/src/main/java/com/flowci/core/job/manager/CmdManager.java b/core/src/main/java/com/flowci/core/job/manager/CmdManager.java
index 19436e08f..669c0567f 100644
--- a/core/src/main/java/com/flowci/core/job/manager/CmdManager.java
+++ b/core/src/main/java/com/flowci/core/job/manager/CmdManager.java
@@ -16,18 +16,18 @@
package com.flowci.core.job.manager;
-import com.flowci.core.agent.domain.CmdIn;
+import com.flowci.core.agent.domain.ShellIn;
+import com.flowci.core.agent.domain.ShellKill;
import com.flowci.core.job.domain.Job;
import com.flowci.core.job.domain.Step;
import com.flowci.tree.NodeTree;
-import com.flowci.tree.StepNode;
/**
* @author yang
*/
public interface CmdManager {
- CmdIn createShellCmd(Job job, Step step, NodeTree tree);
+ ShellIn createShellCmd(Job job, Step step, NodeTree tree);
- CmdIn createKillCmd();
+ ShellKill createKillCmd();
}
diff --git a/core/src/main/java/com/flowci/core/job/manager/CmdManagerImpl.java b/core/src/main/java/com/flowci/core/job/manager/CmdManagerImpl.java
index cfd0bfc75..87a96a66e 100644
--- a/core/src/main/java/com/flowci/core/job/manager/CmdManagerImpl.java
+++ b/core/src/main/java/com/flowci/core/job/manager/CmdManagerImpl.java
@@ -16,7 +16,6 @@
package com.flowci.core.job.manager;
-import com.flowci.core.agent.domain.CmdIn;
import com.flowci.core.agent.domain.ShellIn;
import com.flowci.core.agent.domain.ShellKill;
import com.flowci.core.common.domain.Variables;
@@ -50,21 +49,21 @@ public class CmdManagerImpl implements CmdManager {
private SpringEventManager eventManager;
@Override
- public CmdIn createShellCmd(Job job, Step step, NodeTree tree) {
+ public ShellIn createShellCmd(Job job, Step step, NodeTree tree) {
StepNode node = tree.get(NodePath.create(step.getNodePath()));
ShellIn in = new ShellIn()
.setId(step.getId())
.setFlowId(job.getFlowId())
.setJobId(job.getId())
- .setCondition(node.getCondition())
.setAllowFailure(node.isAllowFailure())
.setDockers(findDockerOptions(node))
.setBash(linkScript(node, ShellIn.ShellType.Bash))
.setPwsh(linkScript(node, ShellIn.ShellType.PowerShell))
.setEnvFilters(linkFilters(node))
.setInputs(linkInputs(node).merge(job.getContext(), false))
- .setTimeout(job.getTimeout());
+ .setTimeout(linkTimeout(node, job.getTimeout()))
+ .setRetry(linkRetry(node, 0));
if (node.hasPlugin()) {
setPlugin(node.getPlugin(), in);
@@ -90,7 +89,7 @@ public CmdIn createShellCmd(Job job, Step step, NodeTree tree) {
}
@Override
- public CmdIn createKillCmd() {
+ public ShellKill createKillCmd() {
return new ShellKill();
}
@@ -112,6 +111,36 @@ private StringVars linkInputs(Node current) {
return output;
}
+ private Integer linkRetry(StepNode current, Integer defaultRetry) {
+ if (current.hasRetry()) {
+ return current.getRetry();
+ }
+
+ if (current.hasParent()) {
+ Node parent = current.getParent();
+ if (parent instanceof StepNode) {
+ return linkRetry((StepNode) parent, defaultRetry);
+ }
+ }
+
+ return defaultRetry;
+ }
+
+ private Integer linkTimeout(StepNode current, Integer defaultTimeout) {
+ if (current.hasTimeout()) {
+ return current.getTimeout();
+ }
+
+ if (current.hasParent()) {
+ Node parent = current.getParent();
+ if (parent instanceof StepNode) {
+ return linkTimeout((StepNode) parent, defaultTimeout);
+ }
+ }
+
+ return defaultTimeout;
+ }
+
private Set linkFilters(StepNode current) {
Set output = new LinkedHashSet<>();
diff --git a/core/src/main/java/com/flowci/core/job/manager/ConditionManager.java b/core/src/main/java/com/flowci/core/job/manager/ConditionManager.java
deleted file mode 100644
index 0abeca9fb..000000000
--- a/core/src/main/java/com/flowci/core/job/manager/ConditionManager.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.flowci.core.job.manager;
-
-import com.flowci.core.agent.domain.CmdIn;
-import groovy.util.ScriptException;
-
-public interface ConditionManager {
-
- boolean run(CmdIn in) throws ScriptException;
-}
diff --git a/core/src/main/java/com/flowci/core/job/manager/JobActionManagerImpl.java b/core/src/main/java/com/flowci/core/job/manager/JobActionManagerImpl.java
index 41f3b02c6..cfbd770c7 100644
--- a/core/src/main/java/com/flowci/core/job/manager/JobActionManagerImpl.java
+++ b/core/src/main/java/com/flowci/core/job/manager/JobActionManagerImpl.java
@@ -1,9 +1,12 @@
package com.flowci.core.job.manager;
+import com.flowci.core.agent.domain.Agent;
import com.flowci.core.agent.domain.CmdIn;
+import com.flowci.core.agent.domain.ShellIn;
import com.flowci.core.agent.service.AgentService;
import com.flowci.core.common.domain.Variables;
import com.flowci.core.common.git.GitClient;
+import com.flowci.core.common.manager.ConditionManager;
import com.flowci.core.common.manager.SpringEventManager;
import com.flowci.core.common.rabbit.RabbitOperations;
import com.flowci.core.job.dao.JobDao;
@@ -17,7 +20,6 @@
import com.flowci.core.job.util.StatusHelper;
import com.flowci.core.secret.domain.Secret;
import com.flowci.core.secret.service.SecretService;
-import com.flowci.core.agent.domain.Agent;
import com.flowci.domain.SimpleSecret;
import com.flowci.domain.Vars;
import com.flowci.exception.CIException;
@@ -672,8 +674,8 @@ private boolean execute(Job job, StepNode node) throws ScriptException {
setJobStatusAndSave(job, Job.Status.RUNNING, null);
Step step = stepService.get(job.getId(), node.getPathAsString());
- CmdIn cmd = cmdManager.createShellCmd(job, step, tree);
- boolean canExecute = conditionManager.run(cmd);
+ ShellIn cmd = cmdManager.createShellCmd(job, step, tree);
+ boolean canExecute = conditionManager.run(node.getCondition(), cmd.getInputs());
if (!canExecute) {
setStepToSkipped(node, step);
diff --git a/core/src/main/java/com/flowci/core/plugin/domain/PluginParser.java b/core/src/main/java/com/flowci/core/plugin/domain/PluginParser.java
index d873b0447..245d5c65e 100644
--- a/core/src/main/java/com/flowci/core/plugin/domain/PluginParser.java
+++ b/core/src/main/java/com/flowci/core/plugin/domain/PluginParser.java
@@ -27,7 +27,6 @@
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.yaml.snakeyaml.Yaml;
-import sun.jvm.hotspot.oops.ObjectHeap;
import java.io.InputStream;
import java.util.LinkedList;
diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties
index af031b993..09c8cb40c 100644
--- a/core/src/main/resources/application.properties
+++ b/core/src/main/resources/application.properties
@@ -2,7 +2,7 @@ logging.level.org.apache.zookeeper=ERROR
logging.level.org.apache.curator.framework=ERROR
logging.level.com.flowci.core=${FLOWCI_LOG_LEVEL:INFO}
-info.app.version=0.20.40
+info.app.version=0.20.45
info.app.name=flow.ci
server.port=${FLOWCI_SERVER_PORT:8080}
diff --git a/core/src/main/resources/flow.properties b/core/src/main/resources/flow.properties
index fa68dda05..867b7e68e 100644
--- a/core/src/main/resources/flow.properties
+++ b/core/src/main/resources/flow.properties
@@ -5,6 +5,8 @@ app.secret=${FLOWCI_SECRET:qazWSXedcRFV12#$}
app.auto-local-agent-host=${FLOWCI_AUTO_AGENT:true}
app.default-smtp-config=true
app.socket-container=true
+app.core-pool-size=100
+app.max-pool-size=200
app.auth.enabled=true
app.auth.expire-seconds=7200
diff --git a/core/src/test/java/com/flowci/core/test/SpringScenario.java b/core/src/test/java/com/flowci/core/test/SpringScenario.java
index 67088b2cd..082aa6543 100644
--- a/core/src/test/java/com/flowci/core/test/SpringScenario.java
+++ b/core/src/test/java/com/flowci/core/test/SpringScenario.java
@@ -17,8 +17,10 @@
package com.flowci.core.test;
import com.flowci.core.common.domain.Mongoable;
+import com.flowci.core.common.domain.Settings;
import com.flowci.core.common.manager.SessionManager;
import com.flowci.core.common.rabbit.RabbitOperations;
+import com.flowci.core.common.service.SettingService;
import com.flowci.core.flow.dao.FlowDao;
import com.flowci.core.flow.domain.Flow;
import com.flowci.core.test.SpringScenario.Config;
@@ -30,11 +32,14 @@
import lombok.extern.log4j.Log4j2;
import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
@@ -91,6 +96,9 @@ public interface VarsMixinIgnore {
}
}
+ @MockBean
+ protected SettingService settingService;
+
@Autowired
protected SessionManager sessionManager;
@@ -111,11 +119,12 @@ public interface VarsMixinIgnore {
private final List> listenersForTest = new LinkedList<>();
- @After
- public void cleanListeners() {
- for (ApplicationListener> listener : listenersForTest) {
- applicationEventMulticaster.removeApplicationListener(listener);
- }
+ @Before
+ public void mockSettingsService() {
+ Settings s = new Settings();
+ s.setServerUrl("http://127.0.0.1:8080");
+
+ Mockito.when(settingService.get()).thenReturn(s);
}
@After
@@ -123,6 +132,13 @@ public void dbCleanUp() {
mongoTemplate.getDb().drop();
}
+ @After
+ public void cleanListeners() {
+ for (ApplicationListener> listener : listenersForTest) {
+ applicationEventMulticaster.removeApplicationListener(listener);
+ }
+ }
+
@After
public void queueCleanUp() {
for (Flow flow : flowDao.findAll()) {
diff --git a/core/src/test/java/com/flowci/core/test/flow/FlowServiceTest.java b/core/src/test/java/com/flowci/core/test/flow/FlowServiceTest.java
index ab29d3971..f8cca5c19 100644
--- a/core/src/test/java/com/flowci/core/test/flow/FlowServiceTest.java
+++ b/core/src/test/java/com/flowci/core/test/flow/FlowServiceTest.java
@@ -18,6 +18,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.flowci.core.common.domain.GitSource;
import com.flowci.core.common.domain.Variables;
import com.flowci.core.common.domain.http.ResponseMessage;
import com.flowci.core.flow.domain.ConfirmOption;
@@ -26,9 +27,14 @@
import com.flowci.core.flow.event.GitTestEvent;
import com.flowci.core.flow.service.FlowService;
import com.flowci.core.flow.service.GitService;
+import com.flowci.core.job.event.CreateNewJobEvent;
import com.flowci.core.secret.domain.AuthSecret;
import com.flowci.core.secret.service.SecretService;
import com.flowci.core.test.SpringScenario;
+import com.flowci.core.trigger.domain.GitPushTrigger;
+import com.flowci.core.trigger.domain.GitTrigger;
+import com.flowci.core.trigger.domain.GitUser;
+import com.flowci.core.trigger.event.GitHookEvent;
import com.flowci.domain.SimpleAuthPair;
import com.flowci.domain.SimpleKeyPair;
import com.flowci.domain.VarValue;
@@ -156,6 +162,31 @@ public void should_throw_exception_if_flow_name_is_invalid_when_create() {
flowService.create(name);
}
+ @Test
+ public void should_start_job_with_condition() throws IOException, InterruptedException {
+ Flow flow = flowService.create("githook");
+
+ String yaml = StringHelper.toString(load("flow-with-condition.yml"));
+ flowService.confirm(flow.getName(), new ConfirmOption().setYaml(yaml));
+
+ GitPushTrigger trigger = new GitPushTrigger();
+ trigger.setEvent(GitTrigger.GitEvent.PUSH);
+ trigger.setSource(GitSource.GITEE);
+ trigger.setAuthor(new GitUser().setEmail("xx").setId("xx").setName("xx").setUsername("xx"));
+ trigger.setRef("master");
+
+ CountDownLatch c = new CountDownLatch(1);
+ addEventListener((ApplicationListener) e -> {
+ Assert.assertEquals(flow, e.getFlow());
+ c.countDown();
+ });
+
+ GitHookEvent e = new GitHookEvent(this, flow.getName(), trigger);
+ multicastEvent(e);
+
+ Assert.assertTrue(c.await(5, TimeUnit.SECONDS));
+ }
+
@Ignore
@Test
public void should_list_remote_branches_via_ssh_rsa() throws IOException, InterruptedException {
diff --git a/core/src/test/java/com/flowci/core/test/job/CmdManagerTest.java b/core/src/test/java/com/flowci/core/test/job/CmdManagerTest.java
index 86ba5210e..b3a5cbc52 100644
--- a/core/src/test/java/com/flowci/core/test/job/CmdManagerTest.java
+++ b/core/src/test/java/com/flowci/core/test/job/CmdManagerTest.java
@@ -85,7 +85,7 @@ public void should_apply_flow_level_docker_option() throws IOException {
Step step = stepService.get(job.getId(), node.getPathAsString());
// then: first step docker should be applied from step level
- ShellIn in = (ShellIn) cmdManager.createShellCmd(job, step, tree);
+ ShellIn in = cmdManager.createShellCmd(job, step, tree);
Assert.assertNotNull(in.getDockers());
Assert.assertEquals(1, in.getDockers().size());
Assert.assertEquals("step:0.1", in.getDockers().get(0).getImage());
@@ -95,7 +95,7 @@ public void should_apply_flow_level_docker_option() throws IOException {
step = stepService.get(job.getId(), node.getPathAsString());
// then: first step docker should be applied from step level
- in = (ShellIn) cmdManager.createShellCmd(job, step, tree);
+ in = cmdManager.createShellCmd(job, step, tree);
Assert.assertNotNull(in.getDockers());
Assert.assertEquals(1, in.getDockers().size());
Assert.assertEquals("helloworld:0.1", in.getDockers().get(0).getImage());
@@ -122,7 +122,7 @@ public void should_create_cmd_in_with_default_plugin_value() throws IOException
StepNode node = tree.get(NodePath.create(flow.getName(), "plugin-test"));
Step step = stepService.get(job.getId(), node.getPathAsString());
- ShellIn cmdIn = (ShellIn) cmdManager.createShellCmd(job, step, tree);
+ ShellIn cmdIn = cmdManager.createShellCmd(job, step, tree);
Assert.assertNotNull(cmdIn);
// then:
@@ -161,7 +161,9 @@ public void should_handle_step_in_step() throws IOException {
StepNode step2_2 = tree.get(NodePath.create(flow.getName(), "step2", "step-2-2"));
// then: verify step 2 - 1 cmd
- ShellIn cmdStep2_1 = (ShellIn) cmdManager.createShellCmd(job, stepService.get(job.getId(), step2_1.getPathAsString()), tree);
+ ShellIn cmdStep2_1 = cmdManager.createShellCmd(job, stepService.get(job.getId(), step2_1.getPathAsString()), tree);
+ Assert.assertEquals(500, cmdStep2_1.getTimeout());
+ Assert.assertEquals(2, cmdStep2_1.getRetry());
// input should be overwrite
Assert.assertEquals("overwrite-parent", cmdStep2_1.getInputs().get("STEP_2"));
@@ -176,8 +178,10 @@ public void should_handle_step_in_step() throws IOException {
Assert.assertEquals("mysql", cmdStep2_1.getDockers().get(1).getImage());
// then: verify step 2 - 2 cmd
- ShellIn cmdStep2_2 = (ShellIn) cmdManager.createShellCmd(job, stepService.get(job.getId(), step2_2.getPathAsString()), tree);
+ ShellIn cmdStep2_2 = cmdManager.createShellCmd(job, stepService.get(job.getId(), step2_2.getPathAsString()), tree);
Assert.assertEquals("parent", cmdStep2_2.getInputs().get("STEP_2"));
+ Assert.assertEquals(1000, cmdStep2_2.getTimeout());
+ Assert.assertEquals(5, cmdStep2_2.getRetry());
// scripts should be linked
Assert.assertEquals("echo 2", cmdStep2_2.getBash().get(0));
diff --git a/core/src/test/java/com/flowci/core/test/job/ConditionManagerTest.java b/core/src/test/java/com/flowci/core/test/job/ConditionManagerTest.java
index 5fb6b329b..fe46fc69c 100644
--- a/core/src/test/java/com/flowci/core/test/job/ConditionManagerTest.java
+++ b/core/src/test/java/com/flowci/core/test/job/ConditionManagerTest.java
@@ -1,8 +1,8 @@
package com.flowci.core.test.job;
-import com.flowci.core.agent.domain.ShellIn;
-import com.flowci.core.job.manager.ConditionManager;
+import com.flowci.core.common.manager.ConditionManager;
import com.flowci.core.test.SpringScenario;
+import com.flowci.domain.StringVars;
import groovy.util.ScriptException;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,42 +13,48 @@ public class ConditionManagerTest extends SpringScenario {
@Autowired
private ConditionManager conditionManager;
+ @Test
+ public void should_verify_condition() throws ScriptException {
+ String expression = "if(a==1 return 1;";
+ conditionManager.verify(expression);
+ conditionManager.verify("return true");
+ }
+
@Test
public void should_run_groovy_condition() throws ScriptException {
- ShellIn in = new ShellIn();
- in.getInputs().put("foo", "helloword");
- in.setCondition(
- "println \"$foo\"; "
- + "return true;"
- );
+ StringVars vars = new StringVars();
+ vars.put("foo", "helloword");
- Assert.assertTrue(conditionManager.run(in));
+ String groovy = "println \"$foo\"; "
+ + "return true;";
+
+ Assert.assertTrue(conditionManager.run(groovy, vars));
}
@Test(expected = ScriptException.class)
public void should_throw_exception_if_wrong_return_type() throws ScriptException {
- ShellIn in = new ShellIn();
- in.getInputs().put("foo", "helloword");
- in.setCondition(
- "println \"$foo\"; "
- + "hello = \"1234\";"
- + "return hello;"
- );
+ StringVars vars = new StringVars();
+ vars.put("foo", "helloword");
- conditionManager.run(in);
+ String groovy = "println \"$foo\"; "
+ + "hello = \"1234\";"
+ + "return hello;";
+
+ conditionManager.run(groovy, vars);
}
@Test(expected = ScriptException.class)
public void should_throw_exception_if_timeout() throws ScriptException {
- ShellIn in = new ShellIn();
- in.getInputs().put("foo", "helloword");
- in.setCondition(
+ StringVars vars = new StringVars();
+ vars.put("foo", "helloword");
+
+ String groovy = (
"sleep(6000); "
+ "println \"$foo\"; "
+ "hello = \"1234\";"
+ "return true;"
);
- conditionManager.run(in);
+ conditionManager.run(groovy, vars);
}
}
diff --git a/core/src/test/resources/flow-with-condition.yml b/core/src/test/resources/flow-with-condition.yml
index 787cc35ea..73deade91 100644
--- a/core/src/test/resources/flow-with-condition.yml
+++ b/core/src/test/resources/flow-with-condition.yml
@@ -2,9 +2,8 @@ envs:
FLOW_WORKSPACE: "echo hello"
FLOW_VERSION: "echo version"
-trigger:
- branch:
- - develop
+condition: |
+ return FLOWCI_GIT_BRANCH == "master" && FLOWCI_GIT_SOURCE == "GITEE"
steps:
- envs:
diff --git a/core/src/test/resources/flow.properties b/core/src/test/resources/flow.properties
index 31431a22e..0ce20a15e 100644
--- a/core/src/test/resources/flow.properties
+++ b/core/src/test/resources/flow.properties
@@ -5,6 +5,8 @@ app.secret=qazWSXedcRFV12#$
app.auto-local-agent-host=false
app.default-smtp-config=false
app.socket-container=false
+app.core-pool-size=100
+app.max-pool-size=200
app.auth.enabled=false
app.auth.expire-seconds=5
diff --git a/core/src/test/resources/step-in-step.yml b/core/src/test/resources/step-in-step.yml
index 32f34cf94..63368877b 100644
--- a/core/src/test/resources/step-in-step.yml
+++ b/core/src/test/resources/step-in-step.yml
@@ -19,6 +19,8 @@ steps:
is_runtime: true
- image: "mysql"
bash: "echo 2"
+ timeout: 500
+ retry: 2
steps:
- name: step-2-1
@@ -28,6 +30,8 @@ steps:
echo "step-2-1"
- name: step-2-2
+ timeout: 1000
+ retry: 5
docker:
image: "redis"
bash: |
diff --git a/tree/src/main/java/com/flowci/tree/FlowNode.java b/tree/src/main/java/com/flowci/tree/FlowNode.java
index af49fcfe7..f96a207ad 100644
--- a/tree/src/main/java/com/flowci/tree/FlowNode.java
+++ b/tree/src/main/java/com/flowci/tree/FlowNode.java
@@ -18,11 +18,6 @@ public class FlowNode extends Node {
*/
private Selector selector;
- /**
- * Node start trigger
- */
- private TriggerFilter trigger = new TriggerFilter();
-
/**
* Notification list that run locally
*/
diff --git a/tree/src/main/java/com/flowci/tree/Node.java b/tree/src/main/java/com/flowci/tree/Node.java
index 74a043c72..c989457ba 100644
--- a/tree/src/main/java/com/flowci/tree/Node.java
+++ b/tree/src/main/java/com/flowci/tree/Node.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flowci.domain.DockerOption;
import com.flowci.domain.StringVars;
+import com.flowci.util.StringHelper;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@@ -40,6 +41,11 @@ public abstract class Node implements Serializable {
protected String name;
+ /**
+ * Node before groovy script;
+ */
+ protected String condition;
+
protected NodePath path;
/**
@@ -78,6 +84,11 @@ public String getEnv(String name) {
return environments.get(name);
}
+ @JsonIgnore
+ public boolean hasCondition() {
+ return StringHelper.hasValue(condition);
+ }
+
@JsonIgnore
public boolean hasDocker() {
return !dockers.isEmpty();
diff --git a/tree/src/main/java/com/flowci/tree/StepNode.java b/tree/src/main/java/com/flowci/tree/StepNode.java
index e14084ebf..16333cee2 100644
--- a/tree/src/main/java/com/flowci/tree/StepNode.java
+++ b/tree/src/main/java/com/flowci/tree/StepNode.java
@@ -16,11 +16,6 @@ public class StepNode extends Node {
public static final boolean ALLOW_FAILURE_DEFAULT = false;
- /**
- * Node before groovy script;
- */
- private String condition;
-
/**
* bash script
*/
@@ -36,8 +31,24 @@ public class StepNode extends Node {
*/
private String plugin;
+ /**
+ * Step timeout in seconds
+ */
+ private Integer timeout;
+
+ /**
+ * Num of retry times
+ */
+ private Integer retry; // num of retry
+
+ /**
+ * Env vars export to job context
+ */
private Set exports = new HashSet<>(0);
+ /**
+ * Order in the node tree
+ */
private int order;
/**
@@ -68,4 +79,14 @@ public boolean hasScript() {
public boolean isRootStep() {
return parent instanceof FlowNode;
}
+
+ @JsonIgnore
+ public boolean hasTimeout() {
+ return timeout != null;
+ }
+
+ @JsonIgnore
+ public boolean hasRetry() {
+ return retry != null;
+ }
}
diff --git a/tree/src/main/java/com/flowci/tree/yml/FlowYml.java b/tree/src/main/java/com/flowci/tree/yml/FlowYml.java
index ad11e342a..7639179c5 100644
--- a/tree/src/main/java/com/flowci/tree/yml/FlowYml.java
+++ b/tree/src/main/java/com/flowci/tree/yml/FlowYml.java
@@ -35,8 +35,6 @@ public class FlowYml extends YmlBase {
private Selector selector = new Selector();
- private TriggerFilter trigger = new TriggerFilter();
-
private List notifications = new LinkedList<>();
public FlowYml(FlowNode node) {
@@ -55,7 +53,7 @@ public FlowNode toNode() {
FlowNode node = new FlowNode(name);
node.setSelector(selector);
- node.setTrigger(trigger);
+ node.setCondition(condition);
node.setEnvironments(getVariableMap());
setDocker(node);
diff --git a/tree/src/main/java/com/flowci/tree/yml/StepYml.java b/tree/src/main/java/com/flowci/tree/yml/StepYml.java
index 4c14b1421..78849569b 100644
--- a/tree/src/main/java/com/flowci/tree/yml/StepYml.java
+++ b/tree/src/main/java/com/flowci/tree/yml/StepYml.java
@@ -40,11 +40,6 @@ public class StepYml extends YmlBase {
private static final String DefaultStepPrefix = "step-";
- /**
- * Groovy script
- */
- private String condition;
-
private String bash; // bash script
private String pwsh; // powershell script
@@ -53,6 +48,10 @@ public class StepYml extends YmlBase {
private String plugin;
+ private Integer retry; // num of retry
+
+ private Integer timeout; // timeout in seconds
+
private List exports = new LinkedList<>();
private boolean allow_failure = false;
@@ -62,6 +61,8 @@ public class StepYml extends YmlBase {
setEnvs(node.getEnvironments());
setBash(node.getBash());
setPwsh(node.getPwsh());
+ setRetry(node.getRetry());
+ setTimeout(node.getTimeout());
setPlugin(node.getPlugin());
setAllow_failure(node.isAllowFailure());
}
@@ -71,6 +72,8 @@ public StepNode toNode(Node parent, int index) {
node.setCondition(condition);
node.setBash(bash);
node.setPwsh(pwsh);
+ node.setRetry(retry);
+ node.setTimeout(timeout);
node.setPlugin(plugin);
node.setExports(Sets.newHashSet(exports));
node.setAllowFailure(allow_failure);
diff --git a/tree/src/main/java/com/flowci/tree/yml/YmlBase.java b/tree/src/main/java/com/flowci/tree/yml/YmlBase.java
index 06031b5b5..1565613eb 100644
--- a/tree/src/main/java/com/flowci/tree/yml/YmlBase.java
+++ b/tree/src/main/java/com/flowci/tree/yml/YmlBase.java
@@ -40,6 +40,11 @@ public abstract class YmlBase implements Serializable {
public Map envs = new LinkedHashMap<>();
+ /**
+ * Groovy script
+ */
+ public String condition;
+
// either docker
public DockerYml docker;
diff --git a/tree/src/test/java/com/flowci/tree/test/YmlParserTest.java b/tree/src/test/java/com/flowci/tree/test/YmlParserTest.java
index da228b58b..7eb5985f3 100644
--- a/tree/src/test/java/com/flowci/tree/test/YmlParserTest.java
+++ b/tree/src/test/java/com/flowci/tree/test/YmlParserTest.java
@@ -60,8 +60,7 @@ public void should_get_node_from_yml() {
Assert.assertTrue(root.getSelector().getLabel().contains("ios"));
Assert.assertTrue(root.getSelector().getLabel().contains("local"));
- Assert.assertEquals(3, root.getTrigger().getBranch().size());
- Assert.assertEquals(1, root.getTrigger().getTag().size());
+ Assert.assertNotNull(root.getCondition());
// verify docker
Assert.assertTrue(root.getDockers().size() > 0);
@@ -80,6 +79,7 @@ public void should_get_node_from_yml() {
Assert.assertEquals("step-1", step1.getName()); // step-1 is default name
Assert.assertEquals("echo step", step1.getEnv("FLOW_WORKSPACE"));
Assert.assertEquals("echo step version", step1.getEnv("FLOW_VERSION"));
+ Assert.assertEquals(3600, step1.getTimeout().intValue());
Assert.assertTrue(step1.isAllowFailure());
Assert.assertEquals("println(FLOW_WORKSPACE)\ntrue\n", step1.getCondition());
@@ -88,6 +88,7 @@ public void should_get_node_from_yml() {
Assert.assertEquals("step2", step2.getName());
Assert.assertEquals("echo 2", step2.getBash());
Assert.assertEquals("echo powershell", step2.getPwsh());
+ Assert.assertNull(step2.getTimeout());
DockerOption dockerOption = step2.getDockers().get(0);
Assert.assertNotNull(dockerOption);
diff --git a/tree/src/test/resources/flow-with-exports.yml b/tree/src/test/resources/flow-with-exports.yml
index b0de5499d..f58ff83dc 100644
--- a/tree/src/test/resources/flow-with-exports.yml
+++ b/tree/src/test/resources/flow-with-exports.yml
@@ -3,14 +3,6 @@ envs:
FLOW_WORKSPACE: "echo hello"
FLOW_VERSION: "echo version"
-trigger:
- branch:
- - "develop"
- - "master"
- - "feature/*"
- tag:
- - "*"
-
selector:
label:
- ios
diff --git a/tree/src/test/resources/flow-with-invalid-name.yml b/tree/src/test/resources/flow-with-invalid-name.yml
index 283020113..4d74b1569 100644
--- a/tree/src/test/resources/flow-with-invalid-name.yml
+++ b/tree/src/test/resources/flow-with-invalid-name.yml
@@ -3,14 +3,6 @@ envs:
FLOW_WORKSPACE: "echo hello"
FLOW_VERSION: "echo version"
-trigger:
- branch:
- - "develop"
- - "master"
- - "feature/*"
- tag:
- - "*"
-
selector:
label:
- ios
diff --git a/tree/src/test/resources/flow.yml b/tree/src/test/resources/flow.yml
index 1bf2ae606..3d0730027 100644
--- a/tree/src/test/resources/flow.yml
+++ b/tree/src/test/resources/flow.yml
@@ -3,13 +3,8 @@ envs:
FLOW_WORKSPACE: "echo hello"
FLOW_VERSION: "echo version"
-trigger:
- branch:
- - "develop"
- - "master"
- - "feature/*"
- tag:
- - "*"
+condition: |
+ return $FLOWCI_GIT_BRANCH == "develop" || $FLOWCI_GIT_BRANCH == "master";
docker:
image: "helloworld:0.1"
@@ -31,6 +26,7 @@ steps:
envs:
FLOW_WORKSPACE: "echo step"
FLOW_VERSION: "echo step version"
+ timeout: 3600
allow_failure: true
- name: step2