【DEVOPS】实现Inno Setup打包自动化

矫情吗;* 2022-08-31 00:47 445阅读 0赞

借助Jenkins + Ansible,辅助一系列的COC实现Inno Setup打包自动化。

0. 目录

        1. 流程图
        1. 环境准备
          • 2.1 lftp工具安装
          • 2.2 svn命令行工具
          • 2.3 7zip工具安装
          • 2.4 Inno Setup安装
          • 2.5 Jenkins File Params
        1. 各脚本
          • 3.1 Inno Setup打包脚本
          • 3.2 Jenkins Pipeline脚本
          • 3.3 Ansible 部署脚本
        1. Links

1. 流程图

相关的交互流程图参见:CI - Inno Setup打包

为了方便之后的叙述和读者的理解,以下穷举涉及到的服务器:

  1. 安装了Jenkins的Linux服务器250。
  2. 安装了自动部署工具Ansible的Linux服务器254。
  3. 安装了Inno Setup的WIndows服务器3。
  4. 安装了FTP Server的Windows服务器3。(服务器资源紧张,所以我们将Inno Setup和FTP Server的安装合并在了同一台服务器上;不过按照我们的设计来说,这两者并没有隐含依赖关系)

2. 环境准备

Jenkins和Ansible的基本安装和配置不是本文的重点,以下列举其他的一些关键性的前期环境准备。

2.1 lftp工具安装

在本文的设计实现中,我们需要使用lftp来实现如下效果:

  1. 将用户上传的待打包软件推送到FTP服务器上,以供Inno Setup打包时使用。这一步操作发生在250服务器。
  2. 将上一步推送到FTP服务器的待打包软件拉取到本地,以执行Inno Setup打包工作。这一步操作发生在3服务器。

因此这里我们需要分别在250服务器,3服务器上安装lftp:

  1. # 250服务器(Centos7)
  2. yum install lftp
  3. # 3服务器(Windows Server2012)
  4. 1. https://lftp.nwgat.ninja/lftp-4.4.15/ 下载对应版本的压缩包
  5. 2. 解压到任意目录
  6. 3. 将以上目录添加到Windows环境变量
  7. 4. 验证: lftp /?
2.2 svn命令行工具

践行DevOps”一切皆版本控制”的思想,我们将Inno Setup脚本进行了源代码版本控制,因此每次打包前我们都需要从svn拉取最新的代码。

关于svn命令行工具,得益于对于zentao的了解,我们可以直接从zentao的安装目录下获取免安装的svn命令行工具(这一步操作也发生在3服务器上)。

2.3 7zip工具安装

本工具的安装发生在3服务器上,目的是为了在使用Inno Setup生成打包之后,将打包过程中收集的一些必要元信息汇总为专门的元文件,然后使用7zip压缩工具将它们合并为一个压缩包,以便于之后的版本追溯。

2.4 Inno Setup安装

作为主角的 Inno Setup,其安装操作发生在3服务器上,我们将借助其命令行工具来实现打包操作。

作为一款打包工具,Inno Setup的安装过程不出意料地非常简单 —— 一路”下一步”即可。

安装完毕之后,将安装路径添加到环境变量Path中,并进行如下验证:

  1. ISCC.exe /?
2.5 Jenkins File Params

现阶段我们暂时没有实现C++应用的集成编译,所以在本文介绍的自动化流程中,待打包软件的提供还是由用户自行上传的,因此我们需要Jenkins的File Params支持。

安装过程参见 Jenkins Pipeline使用File parameter,具体的使用参见本文下面的细节。

3. 各脚本

脚本涉及三大块:

  1. Inno Setup打包脚本
  2. Jenkins Pipeline脚本
  3. Ansible部署脚本
3.1 Inno Setup打包脚本
  1. ; Script generated by the Inno Setup Script Wizard.
  2. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
  3. #define MyAppName "ZzCamera"
  4. #define MyAppVersion "1.0.0"
  5. #define MyAppPublisher "ZZZZ, Inc."
  6. #define MyAppURL "http://www.ZZZZ.com/"
  7. #define MyAppExeName "ZZZZ-camera.exe"
  8. ; // 以本脚本所在的目录为基础, 进行相对路径跳转
  9. #define CompileBasePath ".\..\"
  10. #define AppFolderNameFromOutside "ZZZZ-camera-" ; 由外部传入
  11. [Setup]
  12. ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
  13. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
  14. AppId={
  15. {33CD0F63-DE20-44E9-BF1D-8A82D45B97FD}
  16. AppName={#MyAppName}
  17. AppVersion={#MyAppVersion}
  18. ;AppVerName={#MyAppName} {#MyAppVersion}
  19. AppPublisher={#MyAppPublisher}
  20. AppPublisherURL={#MyAppURL}
  21. AppSupportURL={#MyAppURL}
  22. AppUpdatesURL={#MyAppURL}
  23. DefaultDirName=D:/{#MyAppName}
  24. DisableProgramGroupPage=yes
  25. ; Uncomment the following line to run in non administrative install mode (install for current user only.)
  26. ;PrivilegesRequired=lowest
  27. OutputDir={#CompileBasePath}\compileOutput
  28. OutputBaseFilename=ZZZZ-camera-installer-{#MyAppVersion}
  29. SetupIconFile={#CompileBasePath}\{#AppFolderNameFromOutside}\ZZZZCamera.ico
  30. Compression=lzma
  31. SolidCompression=yes
  32. WizardStyle=modern
  33. DefaultGroupName={#MyAppName}
  34. [Languages]
  35. Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
  36. ; 写入注册表进行protocol注册,以实现在浏览器输入 ZZZZCamera:// 打开应用的效果
  37. [Registry]
  38. Root: HKCR; Subkey: "ZZZZCamera"; Flags: uninsdeletekeyifempty; ValueType: expandsz; ValueName: "URL Protocol"; ValueData: "{app}\{#MyAppExeName}"
  39. Root: HKCR; Subkey: "ZZZZCamera"; Flags: uninsdeletekeyifempty; ValueType: expandsz; ValueName: ""; ValueData: "ZZZZCamera"
  40. Root: HKCR; Subkey: "ZZZZCamera\shell"; Flags: uninsdeletekeyifempty
  41. Root: HKCR; Subkey: "ZZZZCamera\shell\open"; Flags: uninsdeletekeyifempty
  42. Root: HKCR; Subkey: "ZZZZCamera\shell\open\command"; Flags: uninsdeletekeyifempty; ValueType: expandsz; ValueName: ""; ValueData: "{app}\{#MyAppExeName} %1"
  43. [Tasks]
  44. Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkablealone
  45. ;Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkablealone
  46. [Files]
  47. Source: "{#CompileBasePath}\{#AppFolderNameFromOutside}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
  48. Source: "{#CompileBasePath}\{#AppFolderNameFromOutside}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
  49. Source: "{#CompileBasePath}\code\image\*"; DestDir: "{app}\image"; Flags: ignoreversion recursesubdirs createallsubdirs
  50. Source: "{#CompileBasePath}\code\bin\uninstall.bat"; DestDir: "{app}"; Flags: ignoreversion
  51. Source: "{#CompileBasePath}\code\bin\stop.bat"; DestDir: "{app}"; Flags: ignoreversion
  52. ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
  53. ;Source: "{#CompileBasePath}\dll\ISTask.dll"; DestDir: {app}; Flags: ignoreversion; Attribs: hidden
  54. ;Source: "{#CompileBasePath}\dll\psvince.dll";DestDir: {app}; Flags: dontcopy noencryption
  55. ; Source: compiler:psvince.dll compile前缀为内置值,,,,
  56. [Icons]
  57. ;Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}";IconFilename: "{app}\ZZZZCamera.ico";
  58. Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}";IconFilename: "{app}\ZZZZCamera.ico"; Tasks: desktopicon
  59. Name: "{group}\{#MyAppName}-启动服务"; Filename: "{app}\{#MyAppExeName}";IconFilename: "{app}\ZZZZCamera.ico";
  60. Name: "{group}\{#MyAppName}-停止服务"; Filename:"{app}\stop.bat";Tasks:desktopicon;IconFilename: "{app}\image\stop.ico";
  61. Name: "{group}\{#MyAppName}-卸载"; Filename:"{app}\uninstall.bat";IconFilename: "{app}\image\uninstall.ico";
  62. [Run]
  63. Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
  64. [UninstallRun]
  65. Filename: "{app}\uninstall.bat"; RunOnceId: "DelService"
  66. [UninstallDelete]
  67. Type: files; Name: "{app}\uninstall.bat"
  68. ; 新版本安装时,先卸载旧有安装版本
  69. [Code]
  70. // https://blog.csdn.net/qq_36190858/article/details/84893661 卸载之后再安装
  71. function InitializeSetup(): boolean;
  72. var
  73. ResultStr: String;
  74. ResultCode: Integer;
  75. begin
  76. if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{33CD0F63-DE20-44E9-BF1D-8A82D45B97FD}_is1', 'UninstallString', ResultStr) then
  77. begin
  78. if MsgBox('ZZZZCamera进程正在运行,需要先卸载它.', mbConfirmation, MB_OKCANCEL) = IDOK then
  79. begin
  80. ResultStr := RemoveQuotes(ResultStr);
  81. //MsgBox(ResultStr, mbConfirmation, MB_OKCANCEL) ;
  82. // ResultStr : D:\ZZZZCamera\uninstall000.exe
  83. Exec('taskkill', '/f /t /fi "imagename eq ZZZZ-camera.exe"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  84. Exec(ResultStr, '/silent', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  85. Result := true;
  86. end
  87. else
  88. begin
  89. Result:= false;
  90. end;
  91. end
  92. else
  93. begin
  94. Result := true;
  95. end;
  96. end;
  97. // https://blog.csdn.net/qq_36190858/article/details/84894346 【卸载前删除某些文件】
  98. procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
  99. begin
  100. if CurUninstallStep = usDone then
  101. begin
  102. DelTree(ExpandConstant('{app}'), True, True, True);
  103. end;
  104. end;
3.2 Jenkins Pipeline脚本
  1. properties([
  2. parameters([
  3. choice(name: 'SERVER', choices: ['windows_3'], description: '编译服务器'),
  4. file(name: 'FILE_TO_COMPILE', description: '需要被Inno Setup打包的压缩包,请将待打包的文件夹打包为zip,直接根目录打包,不要包含一个根文件夹')
  5. ])
  6. ])
  7. library "jenkinsci-unstashParam-library"
  8. @Library('global-shared-library') _
  9. def msgTools = new org.devops.tools()
  10. def file_upload
  11. pipeline {
  12. agent any
  13. stages {
  14. stage('Initialize') {
  15. steps {
  16. echo "清空工作空间"
  17. cleanWs()
  18. }
  19. }
  20. stage('validate file params') {
  21. steps {
  22. script{
  23. if(!"${FILE_TO_COMPILE}".endsWith(".zip")){
  24. error "请将待打包的文件夹打包为zip,直接根目录打包,不要包含一个根文件夹"
  25. }
  26. println "${FILE_TO_COMPILE}"
  27. }
  28. }
  29. }
  30. stage('接收上传的文件'){
  31. steps {
  32. script{
  33. file_upload = unstashParam "FILE_TO_COMPILE"
  34. // 统一压缩包的名称
  35. sh "cd ${WORKSPACE} && mv ${file_upload} ZZZZ-camera-needComiple.zip"
  36. file_upload = "ZZZZ-camera-needComiple.zip"
  37. sh "pwd ${file_upload}"
  38. // sh "cat ${file_upload}"
  39. // sh "cat ${file_upload} |jq '.'"
  40. //sh "ls ${WORKSPACE}"
  41. //println "curr file is: ${file_upload}"
  42. }
  43. }
  44. }
  45. stage('上传文件到Inno Setup编译服务器.3'){
  46. steps {
  47. sh """ lftp 172.16.X.X -p 21 -u webXXX,webXXX -e "lcd ${WORKSPACE}/; cd /ZZZZCamera/;put ${file_upload}; quit" """
  48. }
  49. }
  50. stage('inno-setup-compile') {
  51. steps {
  52. sshPublisher(
  53. publishers: [
  54. sshPublisherDesc(
  55. configName: 'ansibleServerIn254',
  56. transfers: [
  57. sshTransfer(
  58. excludes: '',
  59. execCommand: ''' cd /etc/ansible/playbooks/common ansible-playbook windows_inno_setup_common.yaml \ -e "TARGET=${SERVER}" ''',
  60. )
  61. ],
  62. verbose: true
  63. )
  64. ]
  65. )
  66. }
  67. }
  68. /* */
  69. stage('show artifact download url'){
  70. steps {
  71. script{
  72. msgTools.PrintMes("ftp://xxx.xx.x.3/ZZZZCamera/ZZZZ-camera-installer-1.0.0.exe" , "red")
  73. }
  74. }
  75. }
  76. }
  77. post{
  78. success {
  79. echo 'pipeline post success'
  80. }
  81. }
  82. }
3.3 Ansible 部署脚本
  1. ---
  2. - hosts: "{ { TARGET | default('windows_3') }}"
  3. vars:
  4. WORKPATH: "E:/_XXXXX/_compileTempOfInnoSetup"
  5. ZIP_FILE_NAME_INNER: "{ {ZIP_FILE_NAME | default('ZZZZ-camera-needComiple.zip')}}"
  6. ZIP_FILE_NAME_FIRST_INNER: "{ {ZIP_FILE_NAME_INNER.split('.')[0]}}"
  7. vars_files:
  8. - vars-common.json
  9. tasks:
  10. ##### Preparation steps
  11. # 我们将 inno setup打包脚本进行了版本管理, 所以每次执行inno setup打包前我们都拉取一次最新代码
  12. - name: remove old inno-setup-script in windows-server-compiler
  13. win_file: path="{ { WORKPATH}}/code/" state=absent
  14. - name: download install.iss script of inno-setup from svn
  15. win_shell: | # sliksvn/ 是从 zentao的安装包中拷贝而来 cd E:/_devops/sliksvn/ ./svn.exe checkout https://xxxx.xxx.xxx.xxx:888/svn/DEVOPS/ZZZZ-installer-camera { {WORKPATH}} --username ccccc --password cccc
  16. # 这里我们为了优化执行速度,没有采用ftp拉取的方式
  17. - name: move package_will_compile to dest location { { WORKPATH}}
  18. win_shell: | mv D:\FTP\1WEB_SAVE\ZZZZCamera\{ {ZIP_FILE_NAME_INNER}} { {WORKPATH}}\{ {ZIP_FILE_NAME_INNER}} -Force
  19. - name: rm folder folder { { WORKPATH}}/{ { ZIP_FILE_NAME_FIRST_INNER}}/
  20. win_file: path="{ { WORKPATH}}/{ { ZIP_FILE_NAME_FIRST_INNER}}/" state=absent
  21. ignore_errors: yes
  22. - name: unzip { { ZIP_FILE_NAME_INNER}} to { { WORKPATH}}
  23. win_unzip: creates=yes src="{ { WORKPATH}}\{ { ZIP_FILE_NAME_INNER}}" dest="{ { WORKPATH}}\{ { ZIP_FILE_NAME_FIRST_INNER}}" delete_archive=yes
  24. - name: call ISCC.exe to compile
  25. win_shell: | $env:path = $env:path + "; E:/_devops/Inno Setup 6" cd { {WORKPATH}}/code/ echo "begin call iscc.exe" ISCC.exe /DAppFolderNameFromOutside={ {ZIP_FILE_NAME_FIRST_INNER}} .\install.iss
  26. - name: push package to ftp server
  27. win_command: | lftp 172.16.X.X -p 21 -u webXXX,webXXX -e "lcd { {WORKPATH}}/compileOutput/; cd /ZZZZCamera/;put ZZZZ-camera-installer-1.0.0.exe; quit"
  1. Jenkins Pipeline使用File parameter
  2. lftp For Windows
  3. Inno Setup官网

发表评论

表情:
评论列表 (有 0 条评论,445人围观)

还没有评论,来说两句吧...

相关阅读

    相关 打包工具 Inno

    打包工具 Inno 下载地址: 是什么? Inno Setup是一个免费的Windows 安装程序制作软件。Inno Setup是一个免费的安装制作软件,小巧、简