Jenkins 插件开发之旅:两天内从 idea 到发布(下篇)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文分上下两篇,上篇介绍了从产生 idea 到插件开发完成的过程; 下篇将介绍将插件托管到 Jenkins 插件更新中心的一系列过程。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"托管插件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"托管插件包括一系列流程步骤。 笔者完成了它所有步骤(包括非必须的步骤),其中主要有两个具有标志性的任务: "}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插件代码被托管在 jenkinsci GitHub 组织的一个仓库,然后作者拥有它的管理权限。笔者插件的代码仓库为:"},{"type":"link","attrs":{"href":"https://github.com/jenkinsci/maven-snapshot-check-plugin","title":null},"content":[{"type":"text","text":"jenkinsci/maven-snapshot-check-plugin"}]},{"type":"text","text":" 。 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以将插件发布到 Jenkins 项目的 Maven 仓库,它是 Jenkins 项目所使用的更新站点的数据来源。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"准备工作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在请求插件托管之前,需要完成以下几个步骤。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"查找类似的插件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 社区欢迎任何人的贡献,但为了让 Jenkins 用户受益, 它要求查找解决相同或类似问题的插件,看看是否可以与现有的维护人员联手。 可以在 "},{"type":"link","attrs":{"href":"https://plugins.jenkins.io/","title":null},"content":[{"type":"text","text":"https://plugins.jenkins.io"}]},{"type":"text","text":" 查看所有的插件, 以确认是否已有类似的插件实现了你计划实现的功能。 笔者在之前已进行过查找,并没有找到可以实现笔者计划实现的功能的类似插件。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"命名规约"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 制定了一些与插件相关的命名规约。 插件开发者要确保遵循这些命名规约。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"artifactId"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插件的 artifactId 被用于文件基本名称,是 Jenkins 插件和更新站点的唯一标识。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它需要遵循一些发布规约:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用小写 ID ,并根据需要使用连字符分隔术语。 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除非名称有任何意义,否则不要在 ID 中包含 jenkins 或 plugin 。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"插件名称"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插件的名称在 Jenkins UI 和其它地方(如:插件站点)展示给用户。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果可以,建议使用简短的描述性名称,如 "},{"type":"text","marks":[{"type":"italic"}],"text":"Subversion"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"笔者所写的插件的名称为:"},{"type":"text","marks":[{"type":"italic"}],"text":"Maven SNAPSHOT Check"},{"type":"text","text":" 。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"groupId"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"推荐使用 "},{"type":"codeinline","content":[{"type":"text","text":"io.jenkins.plugins"}]},{"type":"text","text":" 或 "},{"type":"codeinline","content":[{"type":"text","text":"org.jenkins-ci.plugins"}]},{"type":"text","text":" 作为 groupId 。 但是不禁止其他组织 ID ,除非它们是恶意的(例如引用与你没有关系的组织)。 笔者所写的插件使用的 groupId 为: "},{"type":"codeinline","content":[{"type":"text","text":"org.jenkins-ci.plugins"}]},{"type":"text","text":" 。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"Java 源代码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 项目一般遵循 "},{"type":"link","attrs":{"href":"https://jenkins.io/doc/developer/publishing/style-guides/www.oracle.com/technetwork/java/codeconvtoc-136057.html","title":null},"content":[{"type":"text","text":"Oracle Java 代码规约"}]},{"type":"text","text":", 但是并没有很好的强制甚至在核心组件中。 个别的插件维护者有时会选择使用不同的风格指南作为插件。 笔者日常使用 IDEA 进行开发,之前安装了「阿里 Java 规约插件」, 因而使用它作为编码规约。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"提交消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Git 提交消息应该从引用与之相关的 JIRA 问题开始(如果适用), 然后在第一行进行简短的总结,并在随后的行中提供更多详细信息。例如:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"[JENKINS-00000] Frobnicate the widget"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果给定的提交修复了指定的问题, 那么使用以下前缀中的任何一个将会自动化解决相关的 JIRA 问题。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"[FIX JENKINS-00000] Frobnicate the widget\n[FIXED JENKINS-00000] Frobnicate the widget\n[FIXES JENKINS-00000] Frobnicate the widget"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于还没过将插件托管,笔者并没有遵循该规约, 等插件发布后,笔者将考虑遵循该规约。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"License"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 项目分发的所有插件都需要是免费的开源软件。 这适用于插件源代码及其所有依赖项。 要确保在 "},{"type":"codeinline","content":[{"type":"text","text":"pom.xml"}]},{"type":"text","text":" 文件和仓库中的 "},{"type":"codeinline","content":[{"type":"text","text":"LICENSE"}]},{"type":"text","text":" 文件指定协议。 官方建议使用 MIT license ,它用于 Jenkins 核心和大多数插件和库, 但是任何 OSI 批准的开源 license 都可以。 笔者这里使用了 MIT license 。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"要求注册的账号"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过 Jenkins 项目更新站点分发的插件需要托管在 "},{"type":"link","attrs":{"href":"https://github.com/jenkinsci","title":null},"content":[{"type":"text","text":"jenkinsci GitHub 组织"}]},{"type":"text","text":"中, 因此需要在 GitHub 上有一个账号,并且需要有一个公共仓库来存放插件源代码。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了完整地发布你的插件,需要注册一个 "},{"type":"link","attrs":{"href":"https://accounts.jenkins.io/","title":null},"content":[{"type":"text","text":"Jenkins 社区帐号"}]},{"type":"text","text":", 它可以让你访问 "},{"type":"link","attrs":{"href":"https://issues.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"JIRA"}]},{"type":"text","text":","},{"type":"link","attrs":{"href":"https://wiki.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"wiki"}]},{"type":"text","text":" 和 "},{"type":"link","attrs":{"href":"https://repo.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"Maven 仓库"}]},{"type":"text","text":" 。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"发起托管请求"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:Jenkins 官方自动化流程使用更容易实现的 fork + 删除的方式(见下文),而不是转移仓库所有者。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"登录到 "},{"type":"link","attrs":{"href":"https://issues.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"JIRA"}]},{"type":"text","text":" 然后在 "},{"type":"link","attrs":{"href":"https://issues.jenkins-ci.org/browse/HOSTING","title":null},"content":[{"type":"text","text":"HOSTING"}]},{"type":"text","text":" 项目创建一个问题。 请确保按照描述填写所有字段。 Jenkins 项目成员将在几天内审查你的请求。 如果审查人员要求你更改,那么请按照要求进行更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"笔者提交的申请为:"},{"type":"link","attrs":{"href":"https://issues.jenkins-ci.org/browse/HOSTING-750","title":null},"content":[{"type":"text","text":"HOSTING-750"}]},{"type":"text","text":", 比较幸运的是当天凌晨(北京时间)笔者的请求就被审查, 正巧那时笔者未眠,于是随后按要求进行了更改并在不久后该申请被审批通过。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一旦满足了所有的需求,你的仓库将被 fork 到 jenkinsci 组织中, 并且你将被邀请加入该组织,并且将为你在 JENKINS 项目中创建 JIRA 组件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时,将要求你删除 Jenkins 从中 fork 的仓库。 之后你可以通过再次从 "},{"type":"codeinline","content":[{"type":"text","text":"jenkinsci"}]},{"type":"text","text":" 那里 fork 来重新创建它。 这将确保 "},{"type":"codeinline","content":[{"type":"text","text":"jenkinsci"}]},{"type":"text","text":" 仓库是 Github 上网络图的根。 这意味着: "}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不会混淆哪个仓库是权威仓库。 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使在 GitHub 上没有大量的关注者,"},{"type":"link","attrs":{"href":"https://help.github.com/articles/searching-in-forks/","title":null},"content":[{"type":"text","text":"源代码搜索"}]},{"type":"text","text":"也会成功。 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其他人更可能在 "},{"type":"codeinline","content":[{"type":"text","text":"jenkinsci"}]},{"type":"text","text":" 仓库中提交 pull request(这是协作的理想选择)。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"创建 wiki 页面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尽管这对发布插件来说这不是严格要求的,但最好为插件创建一个 wiki 页面来存储文档。关于如何执行此操作的详细信息,请参阅"},{"type":"link","attrs":{"href":"https://jenkins.io/doc/developer/publishing/wiki-page","title":null},"content":[{"type":"text","text":"插件 wiki 页面指南"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"笔者所写的插件的 wiki 页面为:"},{"type":"link","attrs":{"href":"https://wiki.jenkins.io/display/JENKINS/Maven+SNAPSHOT+Check+Plugin","title":null},"content":[{"type":"text","text":"Maven SNAPSHOT Check Plugin"}]},{"type":"text","text":" 。 其间除了官方文档,笔者还参考了其它插件 wiki 页面的排版。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"开启 CI 构建"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 项目托管了一个 Jenkins 实例来执行插件的持续集成构建。 官方推荐通过在插件的 Github 仓库根目录创建一个 Jenkinsfile, 为在 Jenkinsci Github 组织中的插件设置 CI 构建。 典型的插件构建( Maven 或 Gradle )只需在 Jenkinsfile 中包含以下语句即可运行:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"buildPlugin()"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"申请上传权限"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在源代码被 fork 到 "},{"type":"codeinline","content":[{"type":"text","text":"jenkinsci"}]},{"type":"text","text":" 组织后,需要提交一个上传权限请求。 按照 "},{"type":"link","attrs":{"href":"https://github.com/jenkins-infra/repository-permissions-updater/","title":null},"content":[{"type":"text","text":"jenkins-infra/repository-permissions-updater/"}]},{"type":"text","text":" 仓库的 README 文件中所说的来做就可以。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 项目在 "},{"type":"link","attrs":{"href":"https://repo.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"Artifactory"}]},{"type":"text","text":" 上托管 Maven 制品,例如核心和插件发布。 它的权限系统与 Github 是独立的, 限制了那些用户(由 Jenkins LDAP 帐户标识,与 wiki 和 JIRA 一样)可以上传。 这个仓库包含 YAML 格式的 Artifactory 上传权限定义, 以及将它们同步到 Artifactory 的工具。 "}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先决条件:在申请权限之前,需要先用 Jenkins 社区帐号登录一次 "},{"type":"link","attrs":{"href":"https://repo.jenkins-ci.org/","title":null},"content":[{"type":"text","text":"Artifactory"}]},{"type":"text","text":" 。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要请求对制品(通常是插件)的上传权限,需要提交一个 PR , 该 PR 需要创建与申请上传权限相关的 YAML 文件。 笔者所提交的 PR 为:"},{"type":"link","attrs":{"href":"https://github.com/jenkins-infra/repository-permissions-updater/pull/1107","title":null},"content":[{"type":"text","text":"Plugin: Permission for maven-snapshot-check-plugin"}]},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过查看它可以看出该 PR 增加了一个文件:permissions/plugin-maven-snapshot-check.yml ,其内容如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"name: \"maven-snapshot-check\"\ngithub: \"jenkinsci/maven-snapshot-check-plugin\"\npaths:\n- \"org/jenkins-ci/plugins/maven-snapshot-check\"\ndevelopers:\n- \"donhui\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在创建 PR 后,会有帮助说明以及 checklist 让提交人对该 PR 进行检查确认。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等这个 PR 被审批后,插件开发者就会拥有该插件的发布权限。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"发布插件"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"前提"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要先确认拥有发布该插件的权限。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Maven 要使用的 Artifactory 凭据"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要告诉 Maven 访问 Artifactory 的凭据。 登录 Artifactory ,从用户 profile 中获取"},{"type":"codeinline","content":[{"type":"text","text":"加密的密码"}]},{"type":"text","text":"。 在 "},{"type":"codeinline","content":[{"type":"text","text":"~/.m2/settings.xml"}]},{"type":"text","text":" 文件配置 server 认证信息,如下所示:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n\n \n \n maven.jenkins-ci.org \n your_user_name_here\n your_encrypted_password_here\n \n \n\n"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"配置 GitHub 以接受你的 SSH key"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当执行 release 时,Maven Release Plugin 会自动往仓库推送代码, 所以需要"},{"type":"link","attrs":{"href":"https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/","title":null},"content":[{"type":"text","text":"配置 GitHub 以接受你的 SSH key"}]},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更多信息可以参考:"},{"type":"link","attrs":{"href":"https://help.github.com/articles/connecting-to-github-with-ssh/","title":null},"content":[{"type":"text","text":"GitHub help on SSH"}]},{"type":"text","text":" 。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"执行发布"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当 GitHub 和 Maven 凭据配置好后, 执行一次发布应该很简单,只需要运行下面的命令:"}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"mvn release:prepare release:perform"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可能在执行发布时会遇到 “401 Unauthorized” 或 “403 Forbidden” 之类问题, 这一般是 settings.xml 配置问题或是没有上传权限。 一些公共的问题处理方案可以查看:"},{"type":"link","attrs":{"href":"https://wiki.jenkins.io/display/JENKINS/Hosting+Plugins#HostingPlugins-Workingaroundcommonissues","title":null},"content":[{"type":"text","text":"HostingPlugins-Workingaroundcommonissues"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插件发布后,8 小时内,将可以在插件更新中心看到它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"笔者所写的 maven-snapshot-check 插件, 在插件列表页的地址为:"},{"type":"link","attrs":{"href":"https://plugins.jenkins.io/maven-snapshot-check","title":null},"content":[{"type":"text","text":"https://plugins.jenkins.io/maven-snapshot-check"}]},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jenkins 实例的插件管理页面的「可选插件」选项截图如下:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ce/cef045d4069da89a3f9487bccd5b5186.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"为插件分类"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Jenkins "},{"type":"link","attrs":{"href":"https://plugins.jenkins.io/","title":null},"content":[{"type":"text","text":"插件列表页面"}]},{"type":"text","text":",可以对插件进行分类显示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要为插件添加一个分类,需要向 "},{"type":"link","attrs":{"href":"https://github.com/jenkins-infra/update-center2","title":null},"content":[{"type":"text","text":"jenkins-infra/update-center2"}]},{"type":"text","text":" 仓库提交一个 PR 。 笔者所提交的 PR 为:"},{"type":"link","attrs":{"href":"https://github.com/jenkins-infra/update-center2/pull/271","title":null},"content":[{"type":"text","text":"add maven-snapshot-check category"}]},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过查看它可以看出该 PR 在 "},{"type":"codeinline","content":[{"type":"text","text":"src/main/resources/label-definitions.properties"}]},{"type":"text","text":" 文件增加了一行,如下所示:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"maven-snapshot-check=builder"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"两天的 Jenkins 插件开发之旅, 让笔者了解了插件开发的基本知识,并在托管插件的过程中学到一些知识。 然后在周末花了几个小时总结回顾,并将它写成文档。 同时也希望此文能给 Jenkins 插件开发者入门带来一点帮助!"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"参考"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://jenkins.io/doc/developer/publishing/requesting-hosting/","title":null},"content":[{"type":"text","text":"Guide to Plugin Hosting"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://jenkins.io/doc/developer/publishing/releasing/","title":null},"content":[{"type":"text","text":"Performing a Plugin Release"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章