首先,请提供一些背景说明为什么我想要这个疯狂的东西。我正在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
中更改阶段的名称并重新启动服务器会更新该阶段的名称,但是将我的自定义阶段修改为看起来像常规阶段一样,似乎并没有将该阶段添加为阶段。
我研究过的东西
我在这个看似微不足道的电话上花了很多天,但我似乎无法弄清楚。
答案 0 :(得分:0)
因此,我尝试过的最新操作实际上可以正常工作,并且可以正确显示,但是效果不佳。
我基本上重新实现了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();