场景
使用Jenkins 做为应用的定时任务处理, 在上面建立的800个左右的Job, 这个环境运行了很多年, 当初安装的最新版本是Jenkins 1.642.3, 现在因为OS需要升级等原因, 驻在上面的Jenkins 服务器也需要一并升级,在新的服务器上安装了新的Jenkins版本Jenkins 2.401.3。
新版的Jenkins,界面更为清爽,Jenkins本身的升级很简单, 安装最新版本及相关插件就可以, 问题是对于旧的Job的迁移。
这里迁移的场景相对比较简单,只需要迁移Job的配置就可以,对于构建的历史记录可以忽略。
如果是在页面中进行配置的话, 一笔Job还好,几百笔时间上就是笔不小的开销,而且还要保证不出错,是否有什么快捷的方式呢?
答案当然是肯定的。
Job迁移的最简单方式-复制config.xml
可以通过直接复制config.xml文件实现迁移旧的Job。
config.xml文件包含了Jenkins Job的配置信息,包括源码配置、触发条件、构建步骤等等。迁移过程如下:
- 先停止新旧两个Jenkins实例,避免在迁移过程中进行任何更改。
- 导出旧Jenkins的Job配置文件,即config.xml,通常在${JENKINS_HOME}/jobs/[job_name]/目录下。
- 将config.xml文件复制到新Jenkins实例的相应位置,通常是${JENKINS_HOME}/jobs/[job_name]/。
- 重启新的Jenkins实例。
需要注意的是,这种方法只能迁移Job的配置,不包括构建历史等数据。如果有需要迁移的插件,还需确保新的Jenkins实例有相应的插件。
另外,Jenkins也提供了Job导入/导出插件,例如Jenkins Job Import Plugin,可以方便地实现Job迁移。
快速导出 config.xml
Jobs 的配置文件config.xml位于 ${JENKINS_HOME}/jobs
的子目录中, 目录是Jenkins Job的名字,这些目录中除了Job配置文件之外,还有构建的记录等文件,可能内容比较多, 如果快速的将目录名和config.xml 提取出来呢?
使用很多语言编写代码都可以实现这个功能, 比如Java , Python, Perl等, 考虑这台机器只有安装Java ,于是将以下代码复制到 ${JENKINS_HOME}/jobs
目录下:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;public class MigrateJenkins {public static void main(String[] args) {File jobsDirectory = new File("jobs"); // 指定要遍历的jobs目录File targetDirectory = new File("jobsMigrate"); // 指定目标目录// 确保目标目录存在if (!targetDirectory.exists()) {targetDirectory.mkdirs();}// 获取所有一级子目录File[] subDirectories = jobsDirectory.listFiles(File::isDirectory);if (subDirectories == null || subDirectories.length == 0) {System.out.println("jobs目录中没有子目录");return;}for (File subDirectory : subDirectories) {File configFile = new File(subDirectory, "config.xml");if (configFile.exists() && configFile.isFile()) {try {// 复制子目录及其config.xml文件到目标目录Files.copy(subDirectory.toPath(), new File(targetDirectory, subDirectory.getName()).toPath(),StandardCopyOption.COPY_ATTRIBUTES);Files.copy(configFile.toPath(),new File(targetDirectory, subDirectory.getName() + "/config.xml").toPath(),StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {e.printStackTrace();}} else {System.out.println("子目录" + subDirectory.getName() + "中不存在config.xml文件");}}}}
正常状况下执行以下两个命令行就可以完成代码的执行:
javac MigrateJenkins.java
java MigrateJenkins
但是在这里的实际环境中使用javac编译类文件的时候, 出现了 error: unmappable character for encoding Cp1252
错误。
### error: unmappable character for encoding Cp1252
问题解决
unmappable character for encoding Cp1252
通常发生在Java源文件包含不支持的字符,并且正在尝试使用Cp1252编码进行编译。Cp1252编码不能映射所有Unicode字符,因此对于某些字符来说可能不适用。
解决这个问题有两种方法:
一种方式是将javac编译器的源代码文件编码设置为UTF-8,这个编码比Cp1252支持更多的字符。要实现这个,你需要在编译时添加"-encoding UTF-8"选项。比如:
javac -encoding UTF-8 MigrateJenkins.java
另一种方法是检查和编辑你的源代码文件,删除那些不被Cp1252编码支持的字符。
也需要确保你的IDE(如Eclipse, IntelliJ等)的字符编码设置和Javac编译器的一致,以避免类似的错误。如果你在使用IDE,你可以在设置中查找encoding或character encoding,然后修改它为UTF-8。
重新执行编译命令,错误信息是不一样了, 但是还是会报 javac error: illegal character: '\ufeff'
的错误。
javac error: illegal character: '\ufeff'
错误解决
这个错误涉及一个特殊的Unicode字符–‘\ufeff’,也被称为"字节顺序标记"(Byte Order Mark,简称BOM)。这经常发生在你的文件是在特定编辑器中保存为UTF-8带BOM的编码,并且当文件被读取时,读取器(在这里是javac)并不能正确处理BOM。
虽然UTF-8编码本质上不需要BOM,一些编辑器(如Windows记事本)仍会在文本开头添加一个BOM。这就可能导致像javac这样的工具出现问题,因为它们并不期望看到BOM。
解决方法有以下两种:
-
使用能够删除BOM的文本编辑器。有的编辑器选项中可以直接设置保存为UTF-8无BOM编码。例如在PSPPSPad,Sublime Text,Notepadd++这样的编辑器中,都有"UTF-8无BOM"的保存选项。只需将文件以此方式保存,然后再次编译即可。
-
使用命令行工具删除BOM。例如在Linux中,你可以使用sed工具:
sed '1s/^\xEF\xBB\xBF//' originalFile > newFile
以上的命令会将原文件复制到新文件,同时移除开头的BOM。然后你可以使用新文件进行编译。
无论选择哪种方式,关键要点就是确保你的Java文件是以UTF-8无BOM格式进行保存的。
到这里问题解决,几百个Jenkins Job的Migrate的时间也就10分钟。
关于Jenkins Job Import Plugin
Jenkins Job Import Plugin是Jenkins的一个插件,它允许用户从其它Jenkins实例或XML文件中导入作业配置。这个插件可以简化新的Jenkins实例的配置,使用户能够轻松地重复使用经过测试的作业配置,并快速构建Jenkins实例。它还可以帮助用户在不同的Jenkins实例之间共享和复制作业配置,提高团队的协作效率
安装方式直接搜索Job Import 进行安装