对应英文文档:https://jenkins.io/doc/book/pipeline/shared-libraries/
本系列主贴直达:https://testerhome.com/topics/11265
Pipeline 被越来越多的组织和项目所使用,一些常用的模式开始出现。通常共享不同项目的 Pipeline 片段是非常有用的,能减少冗余,保持代码 DRY(Don't_repeat_yourself)。
Pipeline 支持创建共享库,这些库能被外部源代码库定义,并能被装载到现有的 Pipeline 中。
共享库被一个名字,一个例如 SCM 的代码段定义,默认的版本是可选的。名字是一个短标识符,它能被用在脚本中。
版本可以是 SCM 能理解的任何东西;例如分支,标记和提交 hashes 等所有 Git 的东西你同样可以申明哪些脚本明确要求下面定义的库,或者默认是禁止的。更进一步,如果你在 Jenkins 配置中说明一个版本,你就能阻止脚本选择一个不同的版本。
说明 SCM 最好的方式就是使用 SCM 插件,插件已经被更新支持新的 API,检查任意的命名版本。最新版本的 Git 和 Subversion 插件都支持这个模式;其他也应该这么做。
如果你的 SCM 插件没有被整合,你可以选择 Legacy SCM,并且挑选已经提供的。在本例中,你需要在 SCM 配置的某个地方包含 (include)${library.yourLibName.version},以便于在迁出代码阶段,插件能读取这个变量选择想要的版本。例如,对于 Subversion,你能设置 URI 为 svnserver/project/${library.yourLibName.version},然后使用例如trunk或者branches/dev或者tags/1.0。
共享库的目录结构如下:
src目录看起来像是标准的 java 代码目录。当执行 Pipeline 的时候,这个目录会被加到 classpath。
vars目录保存脚本,脚本定义 Pipeline 可以存取的全局变量。每一个 groovy 的基本名应该是一个 Groovy 标识符,方便起见都是驼峰命名法。
这些目录中的 Groovy 原文件都与脚本 Pipeline 同样的” CPS transformation”。
resources目录允许libraryResource被外部库使用加载非 Groovy 文件。现在这个特性不支持内部库。
有好几个可以定义共享库的地方,取决于用户情况。Manage Jenkins » Configure System » Global Pipeline Libraries,在这里许多库都能被定义。
因为这些库是全局使用,系统中的任何 Pipeline 都可以用这些库中的功能。
这些库被认为是” 可信任的”,他们能运行 Java/Geoovy/Jenkins 内部 API/Jenkins 插件或者第三方库的任何方法。这允许你以一个更高级别封装去包裹独立的不安全的 APIs,供其他 Pipeline 使用。要知道,任何能提交文件到这个 SCM 库的人都能对 Jenkins 进行无限的存取。你需要全部/运行权限去配置库 (通常权限被授予 jenkins 管理员)。
任何创建的目录都可以有和它相关联的共享库。这个机制允许特殊库对目录和子目录中的 Pipeline 作用范围。
目录为基础的库并不被认为是” 可信任的”;它们在 Groovy sandbox 中的运行就像典型的 Pipeline 一样。
其他插件可以增加定义库的方法。例如插件 github-branch-source 提供一个 GitHub 组织目录项,这个目录项允许脚本不用任何附加配置就可以使用不受信任的库,例如 github.com/someorg/somerepo。本例中,指定的 GitHub 库将被加载。
隐式加载的共享库允许 Pipeline 使用任意这样的库定义的类或者全局变量。存取其他的库,Jenkinsfile需要使用@Library
标记,说明库的名字:
这些标记可以在 Groovy 脚本允许的任何地方。当引用类库 (src目录) 的时候,相应标记需要一个import表达式:
提示:对于只定义了全局变量 (vars/) 的共享库,或者只需要一个全局变量的 Jenkinsfile,annotation 标记使用@Library('my-shared-library') _
保持代码精炼是有用的。本质上,替代标记一个不必要的import
表达式,符号_
被标记。
不推荐 import 一个全局变量/函数,因为这将强迫编译器将字段和方法解释为静态 (static) 的。此种情况下的 Groovy 编译器会产生令人迷惑的错误信息。
在脚本的编译过程中, 库在开始执行之前被解析和加载。这使得 Groovy 编译器能够理解静态类型检查中使用的符号的含义, 并允许它们在脚本中的类型声明中使用, 例如
但是, 全局变量在运行时解析。
从 Pipeline2.7 版: 共享的 Groovy 库插件中, 有一个新的选项用于在脚本中加载 (non-implicit) 库: 在生成过程中的任何时候动态加载库的一个library步骤。
如果你只对使用全局变脸函数感兴趣 (从vars目录),语法是相当的简单:
此后, 脚本将可以访问该库中的任何全局变量。
也可以使用src/目录中的类, 但更棘手。虽然 @Library
注释在编译之前已经准备脚本的 classpath, 但在遇到library step 时, 脚本已经编译完毕。因此, 您无法从库中import或以其他方式,静态引用类型。
但是, 您可以动态地使用库类 (没有类型检查), 通过完全访问来自library step 的返回值的限定名。可以使用类似于 Java 的语法调用静态static方法:
您还可以访问静态字段, 并调用构造函数, 就好像它们是名为 new 的静态方法一样:
当隐式加载被选中的时候,共享库使用默认版本,否则 pipeline 只通过名字引用库,例如@Library(‘my-shared-library’)
。如果默认版本没有定义,Pipeline 必须说明一个版本,例如@Library(‘my-shared-library@master’)
。
如果共享库配置中的” 允许默认版本被覆盖” 被选中,@Library
标记有可能覆盖库的默认版本。如果可能的话,这同样允许隐式加载的库能被加载不同的版本。
当时使用library step,你可以说明一个版本:
由于这是一个常规步骤 (library step), 因此可以计算该版本而不是常量, 就像注释;例如:
将使用与 multibranch Jenkinsfile 相同的 SCM 分支加载库。另外一个例子中,你可以通过参数选择一个库:
注意:Library step 可能不可以被用来覆盖一个隐式加载库的版本。它在脚本开始的时候已经加载,并且一个用名字加载的库不能被加载两次。
说明 SCM 最好的方式是使用 SCM 插件,插件已经被更新支持新的 API 去检查版本。在写本文时,Git 和 Subversion 插件已经支持这个模式。
SCM 插件有可能仍然通过 Legacy SCM 选项使用,因为插件还没有支持交心的共享库特性。本例中,包含${library.yourlibrarynamehere.version},在这里分支/tag/ref 为了特殊的 SCM 插件被配置。这确保,在迁出库的源代码时,SCM 插件能扩展它的变量去迁出合适的库版本。
如果你在Library step 中仅仅指明一个库名字 (可选的,在 version 后面@) ,Jenkins 将寻找那个名字的预先配置的库。(或者加载一个github.com/owner/repo 自动库)
但您也可以动态地指定检索方法, 在这种情况下, 不需要在 Jenkins 中预先定义库。例子如下:
最好使用 Pipeline Syntax 寻找你的 SCM 的精确的语法。
注意:在这些 cases 中,必须要说明库版本。
一般来说,任何合法的 Groovy 代码都可以使用。不同的数据结构,功能方法等,例如:
库的类不能明确的调用 steps,例如sh或者git。但是, 它们可以在封闭类的范围之外实现方法, 进而调用 Pipeline step,例如:
然后这个方法可以被脚本式 Pipeline 调用。
这个方法是有限制的,例如,它阻止一个父类的定义。
或者,在构造函数中,一套steps集合能用this被明确的传递到一个库类,或者仅仅一个方法:
当保存类的状态的时候,例如上面,类必须实现Serializable接口。
这确保在 Jenkins 中,一个使用类的 Pipeline,像下面的例子,能挂起 (suspend) 和回复 (resume)。
如果库需要存取全局变量,例如env,那么这些库应该被明确的传递到库类,或者方法中。
而不是将大量的变量从脚本式 Pipeline 传递到库中,
上面的例子展示了参数被传递到一个静态 (static)方法,脚本式 Pipeline 按照如下方式调用:
在内部,vars目录中的脚本按要求实例化为单例。这允许在单个. groovy 文件中定义多个方法以方便使用。
声明式 Pipeline 不允许在脚本指令之外使用全局变量用法 (JENKINS-42360)。
① script在声明式 Pipeline 中访问全局变量所需的脚本指令。
注意:在 Jenkins 加载并使用该库作为成功的 Pipeline 运行的一部分之后, 在共享库中定义的变量将只显示在全局变量引用 (在 Pipeline Syntax 中)。
警告:
避免在全局变量中保留状态
避免使用交互或保留状态的方法来定义全局变量。改用静态类或实例化类的局部变量。
共享库还可以定义与内置步骤 (如 sh 或 gitgit) 类似的全局变量。在共享库中定义的全局变量必须用所有 lower-case 或法则命名, 以便通过管道正确加载。
例如, 要定义 sayHello, 应创建文件 vars/sayHello.groovy, 并应实现调用方法。调用方法允许以类似于步骤的方式调用全局变量:
Pipeline 能引用和调用这个变量:
如果用块调用, 则调用方法将接收关闭。应显式定义该类型以阐明步骤的意图, 例如:
然后, Pipeline 可以像任何接受块的内置步骤一样使用此变量:
如果您有很多类似的管道, 则全局变量机制提供了一个方便的工具来构建一个 higher-level 的 DSL 来捕获相似性。例如, 所有的詹金斯插件都是以同样的方式构建和测试的, 所以我们可以写一个名为buildPlugin的 step。
假设脚本已作为全局共享库或文件夹级共享库加载, 结果 Jenkinsfile 将大大简化:
可以使用 third-party 的 Java 库, 通常是在 Maven 中心, 从受信任的库代码使用@Grab注释。有关细节, 请参阅 Grape document, 但简单地把:
默认情况下, 第三方库被缓存在~/. groovy/grapes/Jenkins 的 master 节点上。
外部库可以使用libraryResource step 从resources/目录加载辅助文件。该参数是一个相对路径名, 类似于 Java 资源加载:。
该文件以字符串形式加载, 适合传递给某些 api 或使用 writeFile 保存到工作区。
最好使用一个唯一的包结构, 这样您就不会意外地与另一个库发生冲突。
如果您注意到在使用不受信任的库的生成中出现错误, 只需单击重播链接以尝试编辑其一个或多个源文件, 并查看所产生的生成是否按预期的方式工作。一旦你对结果满意,跟随 Build 状态页上的不同链接,应用不同的库变化,并保存到代码库中。
(即使为库请求的版本是一个分支, 而不是像标记这样的固定版本, 重播的生成将使用与原始生成完全相同的修订: 将不会再次签出库源。
受信任的库当前不支持重播。在重播过程中, 当前也不支持修改资源文件。
从 2017 年 9 月下旬发布的声明性 1.2 开始, 可以定义声明性管道。也可在共享库中进行。下面是一个示例, 它将执行不同的声明管道, 具体取决于内部版本号是奇数还是偶数:
此时, 只能在共享库中定义整个 Pipeline。这只能在” vars/*.groovy”,仅仅只能在一个方法调用中。在单个生成中只能执行一个声明性 Pipeline, 如果尝试执行第二个 Pipeline, 则生成结果将失败。
本系列主贴直达:https://testerhome.com/topics/11265