windows下jenkins通过pipeline来部署和回滚maven、npm、小程序项目

目的

  • 实现后台java的maven部署
  • 实现前端npm的部署

准备

  • 两台服务器,我这边都是windows的:

    创新互联建站是一家以网站建设公司、网页设计、品牌设计、软件运维、成都网站推广、小程序App开发等移动开发为一体互联网公司。已累计为VR全景等众行业中小客户提供优质的互联网建站和软件开发服务。

    • 服务器A,用于安装jenkins等服务
    • 服务器B,待部署机器
  • Jenkins,除了默认插件外,额外需要手动安装插件:

    • Blue Ocean,更方便的展示进度等
    • Publish Over SSH,用于通过ssh来部署远程服务器
    • DingTalk,钉钉机器人通知插件
  • OpenSSH

  • Git

  • 其他的javamavennpm等工具

安装软件

  • 服务器A中安装jenkins,由于我这边还是旧的windows server 2012,对docker支持不好,所以直接安装的msi文件,具体安装步骤参考官方文档,安装完成后再安装上述的几个插件。
  • 服务器A安装Git,方便从github等代码工具中远程拉取代码,具体安装步骤可以自行网上搜索,由于我们后期会用到sshtar等工具,所以需要将安装目录\Git\usr\bin此路径加入到windows环境变量中,这样就可以用里面的各种工具了,如果是npm项目,需要在服务器B中也要安装,主要是为了用tar打包命令,如果用其他打包方式也可以不安装。
  • 服务器A安装javamavennpm,最好与开发服务上版本保持一致,并将其加入到windows环境变量中,这样的话jenkins就可以直接使用。
  • 服务器B安装openssh,安装流程查看官方文档:Install Win32 OpenSSH,同样需要将此路径加入环境变量,且需要按照文档开启端口、开机自启动等

ssh密钥联通

  • 整体的流程就是服务器B相当于服务端,服务器A相当于客户端,我们需要用客户端通过ssh链接到服务端,然后传输部署的文件过去,所以需要在客户端中生成私钥和公钥,公钥会给到服务端中,私钥则还是留在客户端,这样ssh链接时通过私钥和公钥验证通过链接到一起就可以传输文件了。

  • 服务器A生成密钥,需要注意的是jenkinsPublish Over SSH不支持最新的密钥格式,只支持旧的pem格式的,所以需要加一些参数才行,在刚安装的git命令工具中输入ssh-keygen -m PEM -t rsa -b 4096,按照提示一步步执行,生成的会放在~/.ssh文件下:

    xxx_rsa     #私钥
    xxx_rsa.pub #公钥
    
  • 服务器B可以先启动一下openssh服务然后关闭,此时会自动生成C:\Program Data\ssh目录,然后我们配置此目录下的sshd_config文件,改动如下:

    PubkeyAcceptedKeyTypes=+ssh-rsa #由于上方说了用的旧的格式密钥,新版本默认已不支持了,所以这里需要额外配置增加支持
    PubkeyAuthentication yes #原先是注释来着,打开并设置为yes,表示开启密钥验证
    PasswordAuthentication no #原先注释来着,打开并设置为no, 关闭密码验证,否则不安全
    
  • 服务器A生产的公钥放到服务B中,这里需要注意,服务器B的用户是管理员用户,则需要放到C:\ProgramData\ssh\administrators_authorized_keys文件中,如果是普通用户,则需要放到C:\Users\用户名\.ssh\authorized_keys文件中,通过这里我们也可以发现密钥实际上是和用户名没有关系的,放到哪个用户名下,是根据位置来的,如果这个用户有权限访问到这个密钥实际上就是可以用这个用户名来登陆的。

  • 测试联通性,可以在服务器A~/.ssh/config中配置:

    Host b_name #服务器B随便起个名字
      HostName 192.168.0.31 #服务器B的IP地址
      IdentityFile ~/.ssh/xxx_rsa #自身生成的私钥
      User administrator #服务器B的登陆用户名
      Port 22 #没改端口的话默认22
      ServerAliveInterval 60
    

    然后可以ssh b_name来测试是否可以联通

  • jenkinsPublish Over SSH插件配置中并测试联通性,位置Manage Jenkins->Configure System->SSH Servers中:

    点击测试,看是否联通成功,如果出现类似Fail Auth等错误,可以参考此解决方法来解决:Jenkins Publish over ssh authentification failed with private key

git 密钥联通

  • 同上在服务器A中同上创建私钥和公钥,但是github只支持最新版本的,可以不用加那些参数了,具体参考官方文档:新增 SSH 密钥到 GitHub 帐户 - GitHub Docs
  • 以github为例,在个人主页的Setting->SSH AND GPG keys ->SSH keys中新增,将生成的公钥粘贴到github中的字段中就可以了
  • 和上方一样来测试下联通是否OK,其他gitee等原理基本一样

maven项目部署

  • 如果不熟悉jenkinspipeline相关的话,请先按照官方文档demo操作一遍:Build a Java app with Maven

  • 官方文档demo中都是用的docker,我这边没有,所以不太一样,而且官方文档没有回滚等操作,所以这边给出参考的Jenkinsfile:

    pipeline {
        agent any //没有用docker
        parameters {//这个是参数化构建
            choice choices: ['deploy', 'rollback'], description: '', name: 'action'
            string description: 'rollback ID (only number)', name: 'version'
        }
        stages {
            stage('Build') {
                when {//如果是部署
                    expression { params.action == 'deploy' }
                }
                steps {//maven打包
                    bat 'mvn -B -DskipTests clean package'//linux下是用sh,windows下则bat
                }
            }
            stage('Archive') {
                when {//如果是部署
                    expression { params.action == 'deploy' }
                }
                steps {//将打包出来的jar包归档,方便回滚时用到
                    archiveArtifacts artifacts: '自身相关路径/target/*.jar', followSymlinks: false, onlyIfSuccessful: true
                }
            }
            stage('Rollback') {
                when {//如果是回滚
                    expression { params.action == 'rollback' }
                }
                steps {//回滚就不需要打包了,直接将原先版本的归档复制到打包出来的路径
                    bat 'xcopy /s /f /y "%JENKINS_HOME%\\jobs\\%JOB_NAME%\\builds\\%version%\\archive\\自身相关路径\\target" 自身相关路径\\target'
                }
            }
            stage('Publish') {
                steps {//publish over ssh插件相关参数,verbose为true,则jenkins_deploy.bat脚本的执行信息会返回
                    sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'cmd /c E:\\服务器B相关部署路径\jenkins_deploy.bat $BUILD_NUMBER', execTimeout: , flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关路径', remoteDirectorySDF: false, removePrefix: '自身相关路径/target', sourceFiles: '自身相关路径/target/*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
                }
            }
        }
    }
    
  • 上方代码一些注解:

    • 上方的参数化构建归档publish over ssh语法都是可以由可视化界面生成的,随便点开一个job后在左侧有流水线语法这个选项,点开后页面在左侧 Declarative Directive Generator中选择parameters:Parameters可以生成参数化构建片段;而左侧片段生成器archiveArtifacts archive the artifacts用来生成归档片段,sshPublishers: Send build artifacts over SSH则用来生成发布的片段,这些具体的使用,可以参考官方文档也可以自行搜索更详细的用法文章。

    • 参数化构建如果在pipeline中写了后,不需要在界面中再配置,只需要运行一次后会自动从pipeline中获取,下次再构建就发现有参数需要填写了,要想回滚则选择rollback然后输入回滚的构建数字运行则会回滚:

    • 一些全局变量,可以参考官方文档:Using a Jenkinsfile,需要注意的是在linux中可以$JENKINS_HOME,但是在windows中就必须%JENKINS_HOME%这样了,需要注意的是publish over sshjenkins_deploy.bat传递参数的 $BUILD_NUMBER则还是用$才行,用%%不起作用,且执行windows脚本时需要用cmd /c开头。

  • 服务器B端的结构及脚本:

    • 项目结构:

      abc:
      ├─backup
      │    └─test_2022_06_27__3.jar #在服务端也备份一份,格式:名称+日期+构建号+jar
      └─prod
      │    └─test.jar #生产文件
      └─jenkins_deploy.bat
      
    • jenkins_deploy.bat脚本:

      @echo off
      ::设置utf8编码运行
      chcp 
      ::移动到当前目录
      cd /d E:\abc
      ::设置时间变量,需要注意的是utf编码和ansi编码所取时间的位数不一样
      set "Ymd=%date:~3,4%_%date:~8,2%_%date:~11,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
      ::将时间的空格用0补齐;将参数可能携带的引号去掉
      set "param=%1"
      ::%Ymd: =0%是将空格转为0,%param:"=%是将"去掉
      set backup_name=%Ymd: =0%_%param:"=%
      ::将现有的移到backup中
      copy prod\test.jar backup\test_%backup_name%.jar
      echo 'prod中的备份到backup中'
      ::关闭服务
      net stop test
      ::将最新的移动到生产目录中
      move /y test.jar prod\test.jar
      echo '最新的移动到prod中'
      ::开启服务
      net start test
      @echo on
      
  • 上方脚本一些注释:

    • jenkins编码没有做设置,所以要用utf8编码来执行bat脚本,否则在jenkins中的信息是乱码。
    • 时间参数在utf8ansi中取值是不一样的,需要注意.
    • 1%是bat语法中第一个参数,就是上方的$BUILD_NUMBER构建号,但传递过来可能后面带引号,例如2",所以要把"也去掉
    • %Ymd: =0%意思是将其中的空格转为0,%param:"=%意思是将其中的"去掉
    • 需要先将test.jar改成用服务的方式运行,最好也要有优雅关闭,可以参考:spring boot不同版本的优雅关闭(graceful shutdown)和在windows下winsw服务方式运行的配置

npm项目部署

  • 整体流程和maven一样,同样官方也有参考demo,Build a Node.js and React app with npm,这里只给出参考的文件:

  • Jenkinsfile:

    pipeline {
        agent any
        parameters {
            choice choices: ['deploy', 'rollback'], description: '', name: 'action'
            string description: 'rollback ID (only number)', name: 'version'
        }
        stages {
            stage('Install') {
                when {
                    expression { params.action == 'deploy' }
                }
                steps {//install
                    bat 'npm ci --registry https://registry.npm.taobao.org'
                }
            }
            stage("Build"){
                when {
                    expression { params.action == 'deploy' }
                }
                steps {//build
                    bat 'npm run build'
                }
            }
            stage("Archive"){
                when {
                    expression { params.action == 'deploy' }
                }
                steps {//先打包成压缩文件,再归档,同时也方便传输
                    bat 'tar -zcvf dist.tar.gz -C dist .'
                    archiveArtifacts artifacts: '*.tar.gz', followSymlinks: false, onlyIfSuccessful: true
                }
            }
            stage('rollback') {
                when {
                    expression { params.action == 'rollback' }
                }
                steps {//将压缩文件拷贝到正常打包后的位置
                    bat 'copy /y "%JENKINS_HOME%\\jobs\\%JOB_NAME%\\builds\\%version%\\archive\\dist.tar.gz" dist.tar.gz'
                }
            }
            stage("Publish"){
                steps {
                    sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'cmd /c E:\\服务器B相关部署路径\\jenkins_deploy.bat $BUILD_NUMBER', execTimeout: , flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关部署路径', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'dist.tar.gz')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
                }
            }
        }
    }
    
  • 服务器B的jenkins_deploy.bat脚本,结构和maven项目差不多。

    • 项目结构:

      abc:
      ├─backup
      │    └─test_2022_06_27__3.tar.gz #在服务端也备份一份,格式:名称+日期+构建号+tar.gz
      └─prod
      │    ├─static
      │    └─index.html
      └─jenkins_deploy.bat
      
    • jenkins_deploy.bat脚本:

      @echo off
      ::设置utf8编码运行
      chcp 
      ::移动到当前目录
      cd /d E:\abc
      ::设置时间变量,需要注意的是utf编码和ansi编码所取时间的位数不一样
      set "Ymd=%date:~3,4%_%date:~8,2%_%date:~11,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
      ::获取第一个参数
      set "param=%1"
      ::将时间的空格用0补齐;将参数可能携带的引号去掉
      set backup_name=%Ymd: =0%_%param:"=%
      ::将现有的打包后移到backup中
      tar -zcvf test_%backup_name%.tar.gz -C prod .
      move test_%backup_name%.tar.gz backup\test_%backup_name%.tar.gz
      echo 'prod中内容打包到backup中'
      ::清空prod文件夹,然后将传递过来的解压到prod中
      del /q prod\*
      for /d %%x in (prod\*) do @rd /s /q "%%x"
      tar -zxvf dist.tar.gz -C prod
      echo '最新的解压到prod中'
      ::删除传递过来的
      del dist.tar.gz
      echo '删除传递过来的文件'
      @echo on
      

构建通知

  • 目前插件可以通过钉钉和企业微信机器人通知,目前只测试了钉钉的,感觉还可以,目前我这边没有在pipeline中自定义,只是用了通用功能,更多功能可查看官方文档或自行搜索相关文章。

完工

  • 以上就是整体流程,只是记录了下一些关键点。

另外补充下小程序的部署和用powershell脚本的写法

  • 小程序

    • 主要是利用官方的miniprogram-ci来完成,此类库可以实现预览和上传(上传后还是需要登陆小程序平台来设置体验版或者是提交正式版本),这个类库是nodejs写的,所以需要大体了解下nodejs脚本的写法,可以参考Node.js 入门教程

    • 小程序用miniprogram-ci上传前需要先到小程序平台“开发管理”->"开发设置"->"小程序代码上传"中生成小程序代码上传密钥,实际上就和ssh的密钥类似,在编写脚本时会用到此密钥,此密钥不要放到和代码一起的版本控制中,否则太不安全了,有两种方式,一种是直接放到jenkins所在服务器的某个路径,然后在脚本时直接引;一种是放到jenkins的密钥管理中,类似github的密钥一样,然后在pipeline通过withCredentials可以获取到,这里采用的是后一种方法,在jinkins中新建credentials,选择SSH Username with private key,其中id则为Jenkinsfile中引用的名称,用户名可以随便填写,Private Key则从小程序端下载的key文件中直接复制过来填入即可。

    • jenkins所在服务器安装nodejs(最好与开发的版本一致),同时在 jenkins插件管理中安装nodejs插件并在全局配置中设置nodejs版本(安装插件后会多出此选项);需要在项目中引入miniprogram-ci 库:npm install miniprogram-ci --save,项目结构如下所示,脚本放到ci\upload.js

      xxx:
      ├─ci
      │    └─upload.js #脚本路径
      └─dist
      │    └─...
      └─public
      │    └─...
      └─src
      │    └─...
      └─JenkinsFile
      
    • upload.js

      const ci = require('miniprogram-ci');//小程序类库
      const path = require('path');
      const fs = require("fs");
      const argv = require('minimist')(process.argv.slice(2));//此类库主要用于解析参数,实现--version 12,则argv.version就为12,如果系统没有自带,则npm安装下
      const manifest = require('../src/manifest.json');//用的uniapp,直接从这里取appid,需要确保manifest.json不要有注释,或者自己新建个json文件也行
      const appDirectory = fs.realpathSync(process.cwd());//当前命令行路径,主要用于获取绝对路径
      let projectPath = path.resolve(appDirectory, './dist/build/mp-weixin');//组装项目路径
      let qrCodeImg = path.resolve(appDirectory, './qrcode.jpg');//组装二维码路径
      let type = argv.type;//参数预览还是上传
      let privateKeyPath = argv.key;//传递upload的私钥路径
      let version = argv.version ? argv.version : argv.number;//版本号,如果有传递则用,没传递则用构建号
      let desc = argv.desc ? argv.desc : '暂无描述';//描述
      //配置
      const project = new ci.Project({
        appid: manifest["mp-weixin"].appid,
        type: 'miniProgram',
        projectPath: projectPath,
        privateKeyPath: privateKeyPath,
        ignores: ['node_modules/**/*'],
      });
      if (type === 'preview') {
        //预览
        (async () => {
          await ci.preview({
            project,
            desc: desc,
            setting: {
              es6: true,
              es7: true
            },
            qrcodeFormat: 'image',
            qrcodeOutputDest: qrCodeImg,
            onProgressUpdate: console.log,
          });
        })();
      } else if (type === 'upload') {
        //上传
        (async () => {
          await ci.upload({
            project,
            version: version,
            desc: desc,
            setting: {
              es6: true,
              es7: true
            },
            onProgressUpdate: console.log,
          });
        })();
      }
      
    • JenkinsFile(cmd版本),小程序自带回退上一版本的功能,所以这里就没有做回退选项,如果想做可参考上方的实现,是同一个逻辑

      pipeline {
          agent any
          parameters {
              string description: 'version (example: 1.0.0)', name: 'version'
              string description: 'description', name: 'desc'
          }
          stages {
              stage('Install') {
                  steps {
                      bat 'npm ci --registry https://registry.npm.taobao.org'
                  }
              }
              stage("Build"){
                  steps {
                      bat 'npm run build:mp-weixin'//用的uniapp
                  }
              }
              stage("Preview"){
                  steps {//先掉用预览来生成二维码图片,用于归档后在jenkins中查看,这一步也可以不用
                      withCredentials([sshUserPrivateKey(credentialsId: '小程序密钥在jenkins保存后的id', keyFileVariable: 'identity')]) {
                          bat 'node ci\\upload.js --type=preview --desc="%desc%" --key="%identity%"'
                      }
                  }
              }
              stage("Archive"){
                  steps {//同时将二维码也归档,方便在jenkins中查看,如果是jenkins外网可以访问,还可以将归档后的预览二维码发送到钉钉等通知工具中,或者内网也可以用oss自带的cli工具上传到oss中供外网访问,此二维码有效时间是25分钟
                      bat 'tar -zcvf dist.tar.gz -C dist\\build\\mp-weixin .'
                      archiveArtifacts artifacts: '*.tar.gz,qrcode.jpg', followSymlinks: false, onlyIfSuccessful: true
                  }
              }
              stage("Publish"){
                  steps {//上传,这里将version和BUILD_NUMBER都传递过去,如果version没有填写,则用构建号作为小程序版本号
                      withCredentials([sshUserPrivateKey(credentialsId: '小程序密钥在jenkins保存后的id', keyFileVariable: 'identity')]) {
                          bat 'node ci\\upload.js --type=upload --version="%version%" --number="%BUILD_NUMBER%" --desc="%desc%" --key="%identity%"'
                      }
                  }
              }
          }
      }
      
    • jenkinsFile(powershell版本)

      pipeline {
          agent any
          parameters {
              string description: 'version (example: 1.0.0)', name: 'version'
              string description: 'description', name: 'desc'
          }
          stages {
              stage('Install') {
                  steps {
                      powershell 'npm ci --registry https://registry.npm.taobao.org'
                  }
              }
              stage("Build"){
                  steps {
                      powershell 'npm run build:mp-weixin'
                  }
              }
              stage("Preview"){
                  steps {
                      withCredentials([sshUserPrivateKey(credentialsId: '小程序密钥在jenkins保存后的id', keyFileVariable: 'identity')]) {
                          //这个参数中如果有空格会解析参数不完整,所以必须加引号
                          powershell "node ci\\upload.js --type=preview --desc='$desc' --key='$identity'"
                      }
                  }
              }
              stage("Archive"){
                  steps {
                      powershell 'tar -zcvf dist.tar.gz -C dist\\build\\mp-weixin .'
                      archiveArtifacts artifacts: '*.tar.gz,qrcode.jpg', followSymlinks: false, onlyIfSuccessful: true
                  }
              }
              stage("Publish"){
                  steps {
                      withCredentials([sshUserPrivateKey(credentialsId: '小程序密钥在jenkins保存后的id', keyFileVariable: 'identity')]) {
                          powershell "node ci\\upload.js --type=upload --version='$version' --number='$BUILD_NUMBER' --desc='$desc' --key='$identity'"
                      }
                  }
              }
          }
      }
      
  • maven的powershell写法

    • jenkinsfiles

      pipeline {
          agent any //没有用docker
          parameters {//这个是参数化构建
              choice choices: ['deploy', 'rollback'], description: '', name: 'action'
              string description: 'rollback ID (only number)', name: 'version'
          }
          stages {
              stage('Build') {
                  when {//如果是部署
                      expression { params.action == 'deploy' }
                  }
                  steps {//maven打包
                      powershell 'mvn -B -DskipTests clean package'
                  }
              }
              stage('Archive') {
                  when {//如果是部署
                      expression { params.action == 'deploy' }
                  }
                  steps {//将打包出来的jar包归档,方便回滚时用到
                      archiveArtifacts artifacts: '自身相关路径/target/*.jar', followSymlinks: false, onlyIfSuccessful: true
                  }
              }
              stage('Rollback') {
                  when {//如果是回滚
                      expression { params.action == 'rollback' }
                  }
                  steps {//回滚就不需要打包了,直接将原先版本的归档复制到打包出来的路径
                      powershell 'copy-item -path "$JENKINS_HOME\\jobs\\$JOB_NAME\\builds\\$version\\archive\\自身相关路径\\target\\*" -destination 自身相关路径\\target\\ -recurse -force'
                  }
              }
              stage('Publish') {
                  steps {//publish over ssh插件相关参数,verbose为true,则jenkins_deploy脚本的执行信息会返回
                      sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'powershell E:\\服务器B相关部署路径\jenkins_deploy.sp1 -number $BUILD_NUMBER', execTimeout: , flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关路径', remoteDirectorySDF: false, removePrefix: '自身相关路径/target', sourceFiles: '自身相关路径/target/*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
                  }
              }
          }
      }
      
    • jenkins_deploy.ps1

      #获取参数
      param (
      	[string]$number
      )
      #移动到当前目录
      Set-Location E:\abc
      #设置时间变量
      $date=(get-date).ToString('yyyy_MM_dd_HHmmss')
      #备份文件名
      $backup_name = 'test_'+$date+'_'+$number+'.jar'
      #将现有的复制backup中
      Copy-Item -path prod\test.jar -destination backup\$backup_name -force
      Write-Output 'backup end'
      #关闭服务
      net stop test
      #将最新的移动到生产目录中
      Move-Item -path test.jar -destination prod\test.jar -force
      Write-Output 'move new to prod'
      #开启服务
      net start test
      Write-Output 'finished'
      
  • npm的powershell写法

    • jenkinsfiles

      pipeline {
          agent any
          parameters {
              choice choices: ['deploy', 'rollback'], description: '', name: 'action'
              string description: 'rollback ID (only number)', name: 'version'
          }
          stages {
              stage('Install') {
                  when {
                      expression { params.action == 'deploy' }
                  }
                  steps {//install
                      powershell 'npm ci --registry https://registry.npm.taobao.org'
                  }
              }
              stage("Build"){
                  when {
                      expression { params.action == 'deploy' }
                  }
                  steps {//build
                      powershell 'npm run build'
                  }
              }
              stage("Archive"){
                  when {
                      expression { params.action == 'deploy' }
                  }
                  steps {//先打包成压缩文件,再归档,同时也方便传输
                      powershell 'tar -zcvf dist.tar.gz -C dist .'
                      archiveArtifacts artifacts: '*.tar.gz', followSymlinks: false, onlyIfSuccessful: true
                  }
              }
              stage('rollback') {
                  when {
                      expression { params.action == 'rollback' }
                  }
                  steps {//将压缩文件拷贝到正常打包后的位置
                      powershell 'copy-item -path  "$JENKINS_HOME\\jobs\\$JOB_NAME\\builds\\$version\\archive\\dist.tar.gz" -destination dist.tar.gz -force'
                  }
              }
              stage("Publish"){
                  steps {
                      sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'powershell E:\\服务器B相关部署路径\\jenkins_deploy.ps1 -number $BUILD_NUMBER', execTimeout: , flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关部署路径', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'dist.tar.gz')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
                  }
              }
          }
      }
      
    • jenkins_deploy.ps1

      #获取参数
      param (
      	[string]$number
      )
      #移动到当前目录
      Set-Location E:\abc
      #设置时间变量
      $date=(get-date).ToString('yyyy_MM_dd_HHmmss')
      #备份文件名
      $backup_name = 'test_'+$date+'_'+$number+'.tar.gz'
      #将现有的打包
      tar -zcvf $backup_name -C prod .
      #将打包后的移动到backup中
      Move-Item -path $backup_name -destination backup\$backup_name -force
      Write-Output 'backup end'
      #清空prod文件夹
      Remove-Item -path prod\* -recurse -force
      #将最新的解压到prod中
      tar -zxvf dist.tar.gz -C prod
      Write-Output 'extra to prod'
      #删除传递过来的
      Remove-item -path dist.tar.gz -force
      Write-Output 'finished'
      
  • powershell各种方法的调用会更现代、简单一些,个人更推荐,需要大体了解下相关语法,参考PowerShell 在线教程或者PowerShell教程 。例如如果tar没有加入环境变量,需要这样写才行& "xxxx\git\usr\bin\tar.exe" -zcvf xxxx才行。

  • windows下jenkinscmdpowershell的编码也是费劲,目前powershell脚本文件不管是设置成ansi,utf8-bom,utf8在jenkins中都是中文乱码,但是在本地运行ansi,utf8-bom格式不是乱码,但是utf8格式是乱码,貌似windows powershell 5.1如果用utf8则必须用带bom的,但最新的powershell 7则改成不带bomutf8jenkins也是,改了编码配置命令行展示正常了,但另一个地方又出现乱码,压根是冲突的,所以目前能写英文的都写英文的,就这样系统报错的话还是乱码,真是一堆的坑。

  • 目前针对powershell还算正常的方式是:

    • 设置ps1脚本文件编码为utf8-bom,可以用notepad++等软件来转换下。
    • 设置powershell软件本身的编码为utf8,具体操作是打印$profile可得到配置文件所在位置名称,在配置文件内(没有则自己新建个同名的)输入:$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding,保存后再次打开powershell默认就是utf8展示了。
    • 设置jenkins的编码为utf8,在其安装目录下的jenkins.xml文件中在java启动项中增加-Dfile.encoding=UTF-8,重启即可。

当前名称:windows下jenkins通过pipeline来部署和回滚maven、npm、小程序项目
文章位置:http://ybzwz.com/article/dschgpe.html