在Jenkins作业中止时在Python中运行清理函数

-1 投票
1 回答
34 浏览
提问于 2025-04-12 13:02

我写了一个Python脚本,这个脚本运行了一段时间,并在正常结束时会进行清理,比如:

import time

def cleanUp():
     #delete some temporary files and so on
     print("Clean up done")

if __name__=="__main__":
     startTime = time.time()
     while time.time() - startTime < 60:
          # Performing some commands
          print("Still running")
     
     cleanUp()

我把这段代码作为Jenkins的一个任务在运行。为了确保无论如何都能进行清理,也就是说即使我中止了Jenkins的任务,我也用了signal.signal这个功能。经过研究发现,Jenkins在终止一个任务时会发送一个“TERM”信号(具体可以查看这个链接:https://gist.github.com/datagrok/dfe9604cb907523f4a2f)。因此,我添加了以下代码,并且加了一个控制机制来检查它是否有效,因为Jenkins在停止任务后不会显示剩下的日志(会立即显示“ABORTED”,见链接):

import time
import signal

pythonObj = None

def cleanUp():
     # object that I defined globally, reconstructing it in a separate script is quite cumbersome
     global pythonObj
     #delete some temporary files and so on
     print("Clean up done")
     # Write some text to a file, to see if this part was actually run
     with open(r"C:\temp\abx.txt", 'w') as file:
        file.write("Process has been finished prematurely")

signal.signal(signal.SIGTERM, cleanUp)

if __name__=="__main__":
     startTime = time.time()
     while time.time() - startTime < 60:
          # Creation of python object
          pythonObj = someFunct()
          print("Still running")
     
     cleanUp()

可惜的是,我在运行Jenkins任务的电脑的C:\temp目录下找不到“控制”文件。

我的问题:使用 signal.signal(signal.SIGTERM, cleanUp) 这种方式是否合适(还是应该用别的方式)?或者我在使用signal.signal时犯了错误?

我也尝试过用 atexit 来注册cleanUp,但这也没有给我想要的结果。

编辑 1:我没有提到的一点是,我需要将一些复杂的输入传递给cleanUp函数。我已经考虑过在管道中使用构建后的操作。

编辑 2:如下面提到的,理论上可以将输入传递过去以重建对象。然而,这个重建过程相当麻烦,因为我只想快速清理,如果需要重建整个过程,那就不可能了,所以我尽量避免这个选项。

非常感谢!

1 个回答

1

一个合适的方法是使用内置的功能——Jenkinsfile 语法中有一个专门为你的需求设计的块:

来自 Pipeline 语法

post 部分定义了一些额外的步骤,这些步骤会在 Pipeline 或某个阶段完成后运行(具体取决于 post 部分在 Pipeline 中的位置)。post 可以支持以下任意的后置条件块:always(总是)、changed(变化)、fixed(修复)、regression(回归)、aborted(中止)、failure(失败)、success(成功)、unstable(不稳定)、unsuccessful(不成功)和 cleanup(清理)。这些条件块允许根据 Pipeline 或阶段的完成状态在每个条件内执行步骤。条件块的执行顺序如下所示。

// Jenkinsfile
def arg1 = 'one'
def arg2

pipeline {
  // ...
  stages {
    stage('Some build logic') {
      steps {
        script {
          arg2 = 'two'
          env.ARG3 = 'three'
        }
        // build steps
      }
    }
  }
  post {
    aborted {
      sh "python3 cleanup.py $arg1 $arg2"
    }
  }
}

根据你的目标,你可以选择任何后置条件来使用,也可以将它们组合在一起。

我需要将一些输入传递给 cleanUp 函数

在你的例子中,cleanUp() 不接受任何参数。不过,abortedcleanup 或其他任何 post 块的语法和任何 stage 中的 steps 是一样的:

后置条件 块包含的 步骤steps 部分是相同的。

这意味着你对变量有相同的控制权——你可以在之前的阶段中设置它们,并在你的函数中使用。注意将单引号改为双引号,以启用 Groovy 字符串插值。

撰写回答