首先,是我想要这个疯狂的东西的背景。我正在Jenkins中构建一个插件,该插件为脚本提供API,这些API是从管道脚本开始的,以与jenkins独立通信。例如,shell脚本然后可以告诉jenkins从正在运行的脚本开始新的阶段。
我已经在脚本和Jenkins之间进行了交流,但是问题是我现在想尝试从代码中的回调开始一个阶段,但是我似乎不知道该怎么做。] >
我尝试过但失败的东西:
开始新的StageStep.java
DSL.java
,但是似乎无法到达实例来调用invokeStep()
,也无法找出如何在正确的环境下实例化DSL.java
的方法。看StageStepExecution.java
并执行其操作。
将自定义字符串作为Groovy正文并使用csc.newBodyInvoker()
进行调用>我想到的一个骇人解决方案只是生成groovy脚本并像ParallelStep一样运行它。但是沙盒不允许我调用new GroovyShell().evaluate("")
,如果我批准该调用,则“阶段”步骤将引发MissingMethodException。因此,我也不会在正确的环境中使脚本实例化。提供EnvironmentExpander没有任何区别。
参考和修改工作流/ {n} .xml在相关的workflow/{n}.xml
中更改阶段的名称并重新启动服务器会更新该阶段的名称,但是将我的自定义阶段修改为看起来像常规阶段一样,似乎并没有将该阶段添加为阶段。
我研究过的东西:
我在这个看似微不足道的电话上花了很多天,但我似乎无法弄清楚。
首先,是我想要这个疯狂的东西的背景。我正在Jenkins中构建一个插件,该插件为脚本提供API,这些API是从管道脚本开始的,以与jenkins独立通信。 ...
因此,我尝试过的最新操作实际上可以正常工作,并且可以正确显示,但是效果不佳。我基本上重新实现了DSL.invokeStep()
的实现,这需要我使用反射A LOT。这是不安全的,并且会随着任何变化而中断,因此我将在詹金斯的票务系统中打开一个问题,希望他们会为此添加一个公共接口。我只是希望这不会给我带来任何怪异的副作用。
// First, get some environment stuff
CpsThread cpsThread = CpsThread.current();
CpsFlowExecution currentFlowExecution = (CpsFlowExecution) getContext().get(FlowExecution.class);
// instantiate the stage's descriptor
StageStep.DescriptorImpl stageStepDescriptor = new StageStep.DescriptorImpl();
// now we need to put a new FlowNode as the head of the step-stack. This is of course not possible directly,
// but everything is also outside of the sandbox, so putting the class in the same package doesn't work
// get the 'head' field
Field cpsHeadField = CpsThread.class.getDeclaredField("head");
cpsHeadField.setAccessible(true);
Object headValue = cpsHeadField.get(cpsThread);
// get it's value
Method head_get = headValue.getClass().getDeclaredMethod("get");
head_get.setAccessible(true);
FlowNode currentHead = (FlowNode) head_get.invoke(headValue);
// crate a new StepAtomNode starting at the current value of 'head'.
FlowNode an = new StepAtomNode(currentFlowExecution, stageStepDescriptor, currentHead);
// now set this as the new head.
Method head_setNewHead = headValue.getClass().getDeclaredMethod("setNewHead", FlowNode.class);
head_setNewHead.setAccessible(true);
head_setNewHead.invoke(headValue, an);
// Create a new CpsStepContext, and as the constructor is protected, use reflection again
Constructor<?> declaredConstructor = CpsStepContext.class.getDeclaredConstructors()[0];
declaredConstructor.setAccessible(true);
CpsStepContext context = (CpsStepContext) declaredConstructor.newInstance(stageStepDescriptor,cpsThread,currentFlowExecution.getOwner(),an,null);
stageStepDescriptor.checkContextAvailability(context); // Good to check stuff I guess
// Create a new instance of the step, passing in arguments as a Map
Map<String, Object> stageArguments = new HashMap<>();
stageArguments.put("name", "mynutest");
Step stageStep = stageStepDescriptor.newInstance(stageArguments);
// so start the damd thing
StepExecution execution = stageStep.start(context);
// now that we have a callable instance, we set the step on the Cps Thread. Reflection to the rescue
Method mSetStep = cpsThread.getClass().getDeclaredMethod("setStep", StepExecution.class);
mSetStep.setAccessible(true);
mSetStep.invoke(cpsThread, execution);
// Finally. Start running the step
execution.start();