从一次解决Nancy参数绑定“bug”开始发布自己的第一个nuget包(扩展篇)

前言

  上一篇,我们已经实现了nuget包的打包,发布到nuget。最近,发现github也有持续集成持续交付部署(CICD)的功能(请原谅我的菜)。原来也没太关注这块,主要原来一直专注于写业务。最近开始系统学习微服务相关的内容,了解到是通过jenkins之类的工具来实现的部署。在github上闲逛的时候,发现有个Actions的功能,就尝试了一番。它是通过一个yml配置文件来实现,代码推送到仓库后,自动打包发布到nuget源和github源。这个过程就是在一个docker环境或者虚拟服务器环境中,将代码clone下来,通过nuget.exe这样的命令行打包上传工具来实现原本人工的操作过程(我猜的)。所以,本文主要实现的就是当我们的代码推送到git仓库后,自动打包nuget package,并推送到nuget.org和github.com。

 

准备工作

  1.首先,我们先修改一下nuget package的配置文件,准备一个待发布的新版本源码。说明一下,这一步不是必须的,我是为了演示一下这个过程,所以故意把版本号改了。只要你的代码是有修改,未推送到git仓库就行了。

   2.通过copy命令将我们打包所需的文件copy到一个独立的文件夹中,方便打包。

 

 命令行完整代码,这里copy后的文件结构和我们在nuget package explorer里面是一样的。

copy "$(SolutionDir)Nancy.FixQueryDictionary\bin\Release\Nancy.FixQueryDictionary.dll" "$(SolutionDir)\NugetPublish\Release\lib\Nancy.FixQueryDictionary.dll"
copy "$(SolutionDir)Nancy.FixQueryDictionary\bin\Release\Nancy.FixQueryDictionary.xml" "$(SolutionDir)\NugetPublish\Release\lib\Nancy.FixQueryDictionary.xml"
copy "$(SolutionDir)Nancy.FixQueryDictionary\bin\Release\Nancy.FixQueryDictionary.pdb" "$(SolutionDir)\NugetPublish\Release\lib\Nancy.FixQueryDictionary.pdb"
copy "$(SolutionDir)README.md" "$(SolutionDir)\NugetPublish\Release\README.md"

   3.在我们的项目中新建一个yml文件,我这里直接上我自己的yml文件。

name: GitHub Actions
on:
  push:
    branches: [ publish ]
  pull_request:
    branches: [ publish ]
jobs:
  Explore-GitHub-Actions:
    runs-on: ubuntu-latest
    steps:
      - run: echo "该作业由${{github.event_name}}事件自动触发"
      - run: echo "此作业现在正在GitHub托管的${{runner.os}}服务器上运行"
      - run: echo "分支的名称是${{github.ref}},存储库是${{github.repository}}"
      - name: 签出存储库代码
        uses: actions/[email protected]
      - run: echo "${{github.repository}}存储库已克隆到运行程序"
      - name: 列出存储库中的文件
        run: |
          ls ${{ github.workspace }}
      - name: 列出Release发布的文件
        run: |
          ls NugetPublish/Release
      - name: 安装nuget 
        uses: nuget/[email protected]
        with:        
            nuget-version: '6.x'
      - name: 打包nuget package
        run: |
             nuget pack NugetPublish/Nancy.FixQueryDictionary.nuspec -OutputDirectory NugetPublish/Release/
      - name: 添加Github源
        run: |
            dotnet nuget add source --name github "//nuget.pkg.github.com/OWNER/index.json"
      - name: 发布到Github
        run: |
            dotnet nuget push NugetPublish/Release/*.nupkg  --api-key ${{ secrets.GITHUB_TOKEN }} --source "github" --skip-duplicate
      - name: 发布到NuGet
        run: |
             dotnet nuget push NugetPublish/Release/Nancy.FixQueryDictionary.1.1.3.nupkg --api-key ${{secrets.NUGET_API_KEY}} --source //api.nuget.org/v3/index.json --skip-duplicate
      - run: echo "此作业的状态为${{job.status}}"

Github工作流yml配置说明

  下面就这个yml配置文件做一下()简()单()的()说()明(),我是经过了九九54次(见下图)的尝试,爬过坑,才最终实现想要的效果。

 

 第一段是定义这个Action(工作流)的名称,触发的仓库分支名称。

name: GitHub Actions
on:
  push:  
    branches: [ publish ]#这个表示当代码推送到publish分支时触发工作流执行
pull_request:
  branches: [ publish ]#这个表示当代码合并推送到publish分支时触发工作流执行(我猜的,等下我们验证一下)

第二段,定义工作流的job,执行的系统环境,将当前仓库名称打印输出。这些echo的内容并不是必须的,通过这些输出你可以了解到有哪些内置的变量,这些变量通过${{}}来绑定输出。每个独立的动作前面都有一个  ,如果是一个简单的动作,这个短横线是直接放在run前面,且有一个空格。 

jobs:
  Explore-GitHub-Actions:
    runs-on: ubuntu-latest
    steps:
      - run: echo "该作业由${{github.event_name}}事件自动触发"
      - run: echo "此作业现在正在GitHub托管的${{runner.os}}服务器上运行"
      - run: echo "分支的名称是${{github.ref}},存储库是${{github.repository}}" 

这里需要注意空格的数量,多了少了都会报错,具体见下图。具体的格式我也没有仔细去研究,毕竟我们全宇宙第一的IDE会有格式纠错提示。如果你直接在github上创建这个yml文件并进行编辑的话,也是有纠错提示的,多尝试几次就好了。

  第三段,这一段表示将我们要用来打包的代码checkout(签出),这里的users是固定写法(貌似是这样的,我看好几个都是这样的)。后面的echo表示已经job已经执行到这里了,代码已经签出了。这就像我们在控制台打印一些自定义的消息一样的。

- name: 签出存储库代码
        uses: actions/[email protected]
      - run: echo "${{github.repository}}存储库已克隆到运行程序"

第四段,这一段有两个动作,用来显示签出的源代码文件列表和我们要打包的文件列表。这个ls命令不就是Linux里面查看文件目录用的吗?同样的,这两段也不是必须的。

- name: 列出存储库中的文件
        run: |
          ls ${{ github.workspace }}
      - name: 列出Release发布的文件
        run: |
          ls NugetPublish/Release

  第五段,安装nuget打包工具,这个工具和我们在nuget.org网站下载的那个nuget.exe命令行工具应该是一样的(我猜的)。

- name: 安装nuget 
        uses: nuget/[email protected]
        with:        
            nuget-version: '6.x' 

第六段,使用nuget pack命令打包生成nuget package文件,这个命令我们在nuget.exe执行是一样的效果,见下图。

  后面的OutputDirectory用来指定输出的目录,可以使用相对路径,不指定这个参数的话,会生成在当前目录下。 

- name: 打包nuget package
        run: |
             nuget pack NugetPublish/Nancy.FixQueryDictionary.nuspec -OutputDirectory NugetPublish/Release/

第七段,添加github源,参数name用来指定源的名称,这个名称下面会用到。说实话,我是没搞懂,为啥要设置这个源。这个命令如果你在系统上执行,就是会在你nuget源中新增一个源地址,你可以在vs的nuget package管理器中查看到这个源,也可以使用nuget命令nuget sources来查看,见下图。

- name: 添加GitHub源        
  run: | dotnet nuget add source --name github "//nuget.pkg.github.com/OWNER/index.json"

第八段,这才是重点的一步,将nuget package发布到github上。发布完成之后,这里会有显示,见下图。

 点进去,是这样的。

 

 这里使用nuget push命令来推送,参数api-key用来指定github的PAT(Personal access tokens),毕竟你在脚本中总不能使用明文的github账户密码吧,当然要使用令牌来访问你的账户。参数source用来指定上面的github源,参数skip-duplicate用来指示当上传已经存在的相同版本的nuget package时就跳过推送,否则会报错。这里最大的坑莫过于这个PAT了,坑的我不要不要的。

- name: 发布到Github
        run: |
            dotnet nuget push NugetPublish/Release/*.nupkg  --api-key ${{ secrets.GITHUB_TOKEN }} --source "github" --skip-duplicates

申请PAT的流程如下所示:Settings>Developer settings>Personal access tokens

  

   

 

 选择 Generate new token,输入token名称,选择有效期,选择权限。

 

 我这里简单粗暴,直接选择了永不过期+全部权限(哈哈)。然后,你就可以得到一个PAT令牌了。原本我以为在脚本中配置这个令牌应该是这样的${{secrets.nuget_ApiKey}},结果呢,死活都不行,执行的过程中提示401。然而,我在nuget.exe中执行的时候,输入的明文令牌是可以的。后来的后来,我才发现,这里是要这样写才行。

${{ secrets.GITHUB_TOKEN }}

真的是坑的我不要不要的!!!你们说,如果我再弄个令牌,想换一个同时还保留这个,应该怎么办呢?咱也不知道,咱也不敢问,也不纠结了。

第九段,将nuget package推送到nuget.org,这里需要配置nuget.org申请的ApiKey,怎么申请?上一篇已经说过了,这里我们需要将它作为变量配置在我们的github里。配置过程,如下所示。

首先,打开仓库的Settings。

 

 选择Secrets,点击New repository secret,输入ApiKey的名称和ApiKey保存即可。

 然后,我们在脚本中使用变量来绑定nuget的ApiKey。

- name: 发布到NuGet
        run: |
             dotnet nuget push NugetPublish/Release/Nancy.FixQueryDictionary.1.1.3.nupkg --api-key ${{secrets.NUGET_API_KEY}} --source //api.nuget.org/v3/index.json --skip-duplicate

第十段,用一个echo来输出任务的状态,查看任务是否完成。

- run: echo "此作业的状态为${{job.status}}" 

 至此,我们已经对yml有了大概的了解。当你在Github的Actions里直接新建工作流yml文件时,右边会出现一些推荐的yml文件,你可以直接点击将其加入到你的yml。甚至,官方还有一个工作流的yml市场,你可以寻找和使用别人已经写好的各种工作流yml文件,详见//github.com/marketplace?type=actions。工作流的文档地址//docs.github.com/en/actions/learn-github-actions/creating-starter-workflows-for-your-organization

开始演示

  接下来,我们把代码push到master分支,并将master合并到publish分支,以此来触发工作流的job执行。下面,我们介绍如何在Github上进行分支合并。当然,你也可以选择用git命令行操作或者用VS自带的管理分支功能进行合并,我比较喜欢这种图形化的操作,不容易出错。在Github上进行分支合并,我也是第一次操作,以下操作基于个人的理解和实践总结。

首先,找到你的仓库中的Pull requests,点击New pull request。

 

 base选择publish,compare选择master。意思就是将master分支合并到publish分支,如果你是publish合并到master,则选择是相反,你懂得!如果版本之间无代码冲突的话,会显示Able to merge。正常情况来说,这里不会有冲突。因为我们修改的是master,然后合并到publish,不会单独修改publish,自然不会冲突。如果出现冲突,自然是需要处理好冲突文件的,有时间我来试一下github的冲突解决应该怎么操作。

 

 接下来,点击Create pull request,填写一下合并原因,变动内容,再次点击Create pull request,即可完成分支合并提交。

 

 

 

 这里,我有个疑问:实际上到此,我们的代码已经合并推送到publish分支了,为什么这里还需要再次确认合并,有点多此一举啊?知道的麻烦告诉我一声,谢谢!

 

 这时候,你打开Actions,会发现工作流已经准备启动了,有个黄色的点点标记了我们最新的一次提交。

点进去,可以发现工作流已经处于loading状态,点击Explore-GitHub-Actions,可以查看到工作流job每一步的执行过程。

 

 我们可以看见工作流job已经都执行完成了,nuget package也已经成功推送到了github源和nuget.org源。

 

 我们打开nuget package的主页,点击Versions,可以发现最新的版本1.1.3已经推送过来了,处于验证状态,几分钟后就会收到nuget package发布成功的通知邮件。

 

 

 

 我们的最新版nuget package版本也已经处于正常对外发布状态了。

 

 最后,我们在仓库code页面,右侧找到Packages,可以发现已经显示了我们在github源上发布的nuget package。点进去,可以查看nuget package的详情。

 

 

 至此,我们的nuget package通过工作流job自动打包自动发布推送就完成了。可是我的疑问还是没有得到解答,为什么已经合并推送了代码,Pull requests里还有一个Merge pull request呢?让我们直接来点一下完事!

结果呢,它又触发了工作流,job都重新来了一遍。由于推送的版本号已经存在,且push命令增加了相同版本号跳过的参数skip-duplicate,所以自动跳过了,没有报错。如果没有设置参数skip-duplicate,推送已存在的版本号工作流则会报错。

 

 

 写在最后

  本文主要简单介绍通过Github工作流,实现nuget package代码更新push到github仓库后的自动打包,同时推送到github源及nuget源(CI/CD),还有github的分支合并操作简单演示。对于我的疑问,欢迎大佬给予解答,在此谢过!