├── .gitignore ├── .travis.yml ├── .version.sh ├── CHANGELOG.md ├── LICENSE ├── README.e.md ├── README.md ├── Vagrantfile ├── _config.yml ├── appveyor-recipe.md ├── appveyor.yml ├── change.log ├── deb.json ├── glide.lock ├── glide.yaml ├── main.go ├── manifest └── index.go ├── rpm.json ├── rtf └── index.go ├── templates ├── LicenseAgreementDlg_HK.wxs ├── WixUI_HK.wxs ├── choco │ ├── LICENSE.txt │ ├── VERIFICATION.txt │ ├── chocolateyInstall.ps1 │ ├── chocolateyUninstall.ps1 │ └── pkg.nuspec └── product.wxs ├── testing ├── README.md ├── Vagrantfile ├── glide.lock ├── glide.yaml ├── hello │ ├── LICENSE │ ├── README.md │ ├── assets │ │ ├── dir1 │ │ │ └── file2 │ │ └── file1 │ ├── change.log │ ├── glide.lock │ ├── glide.yaml │ ├── hello.go │ ├── ico.ico │ └── wix.json ├── main.go ├── test.bat ├── vagrant-off.sh ├── vagrant-setup.sh └── vagrant-test.sh ├── tpls └── index.go ├── unice-recipe.md ├── util └── index.go ├── wix.json └── wix └── index.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.vagrant/ 2 | /vendor/ 3 | /notes.md 4 | /builder/ 5 | /testing/demo/templates/ 6 | /testing/demo/build/ 7 | /testing/demo/pkg-build/ 8 | /testing/test.exe 9 | /testing/.vagrant/ 10 | /.glide/ 11 | testing/vendor 12 | testing/hello/vendor 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: go 7 | go: 8 | - 1.9 9 | 10 | env: 11 | matrix: 12 | - OKARCH=amd64 OSARCH=amd64 13 | - OKARCH=386 OSARCH=i386 14 | global: 15 | - VERSION=${TRAVIS_TAG} 16 | - GH_USER=${TRAVIS_REPO_SLUG%/*} 17 | - GH_APP=${TRAVIS_REPO_SLUG#*/} 18 | - JFROG_CLI_OFFER_CONFIG=false 19 | # bintray token 20 | - secure: IRw6MHgW2lWOTywZVxn9rF5Sp91Iji/XJNT1dGs/gCogEojzE84K8OlWmPb090cGmJR+wv5wp0be5G34qYV0vssxseOVRZ4JZN0EqOOQGSPZxrbVc1aXMv4JKfe8SlBh18IG6O2bZW1uQHCpBz1QSygzG7o7wD1/j7q83PMxy4IMzfRNcbjRUmqK10/TMiDUuPTYXIAi5RKgrIghvfaIvtl3DG++BquhfYKex5u98kTcLDJ2eHTfWsl7UMcvHDamYVIDt5WFfPidfkkG8BXD5bwjgldGZ7CiXPevrslKeE5ZV3cjBRvSxcnGGOEE/hN9hhCiVCz/YJIc6v9QoTIRG9nv9RulCBAHsZJZXa3NymERioMi/+b5H7J3hluRSPpGHsGCefygi3lL7mq4lPhZAd78Rq6gkEG10sNcRjiP3nCsSFlTWIMyZmiTy27FvV9C8rCihAg0vMdx/mA0AWLDLjAvzSeNJAOiV6pXTDVIZVTiE82cTyqV9YrKZ9E7cxJz9mOfBEZ1AT5SRw6hFsQ7g+cPvscpL0l10S5WinfmGJzu+vjJ9RzYETSRrO1PUiOk4puyjW+AQmFy7CjL7lS7fL8xGsx3xENq1FW1NhRFP22jwA0wcWjXlDCRYzCLbuDBRfSasFZCQKEUTeOuvplTA/Yx62Es36TtgGKIwwYk+1U= 21 | 22 | before_install: 23 | - sudo add-apt-repository 'deb https://dl.bintray.com/mh-cbon/deb unstable main' 24 | - sudo apt-get -qq update 25 | - sudo apt-get install --allow-unauthenticated changelog go-bin-deb fakeroot 26 | - mkdir -p ${GOPATH}/bin 27 | - cd ~ 28 | - curl https://glide.sh/get | sh 29 | 30 | install: 31 | - cd $GOPATH/src/github.com/$TRAVIS_REPO_SLUG 32 | - glide install 33 | - go install 34 | 35 | script: 36 | - echo "pass, ok." 37 | 38 | before_deploy: 39 | # create the deb package 40 | - cd $GOPATH/src/github.com/$TRAVIS_REPO_SLUG 41 | - mkdir -p build/$OSARCH 42 | - GOOS=linux go build --ldflags "-X main.VERSION=$VERSION" -o build/$OSARCH/$GH_APP main.go 43 | - go-bin-deb generate --file deb.json -a $OSARCH --version $VERSION -o $GH_APP-$OSARCH-$VERSION.deb 44 | # copy the deb for gh release (backward compatibility) 45 | - cp $GH_APP-$OSARCH-$VERSION.deb $GH_APP-$OKARCH.deb 46 | # upload to bintray 47 | - curl -fL https://getcli.jfrog.io | sh 48 | - ls -alh 49 | - ./jfrog bt pc --key=$BTKEY --user=$GH_USER --licenses=MIT --vcs-url=https://github.com/$GH_USER/deb $GH_USER/deb/$GH_APP || echo "package already exists" 50 | - ./jfrog bt upload --override=true --key $BTKEY --publish=true --deb=unstable/main/$OSARCH $GH_APP-$OSARCH-$VERSION.deb $GH_USER/deb/$GH_APP/$VERSION pool/g/$GH_APP/ 51 | # prepare rpm package creation 52 | - docker pull fedora 53 | # create the package in the docker 54 | - > 55 | docker run -v $PWD:/mnt/travis fedora /bin/sh -c 56 | "cd /mnt/travis && (curl -s -L https://bintray.com/mh-cbon/rpm/rpm > /etc/yum.repos.d/w.repo) && dnf install go-bin-rpm changelog rpm-build -y --quiet && go-bin-rpm generate --file rpm.json -a $OSARCH --version $VERSION -o $GH_APP-$OSARCH-$VERSION.rpm" 57 | # copy the rpm for gh release (backward compatibility) 58 | - cp $GH_APP-$OSARCH-$VERSION.rpm $GH_APP-$OKARCH.rpm 59 | # upload to bintray 60 | - ./jfrog bt pc --key=$BTKEY --user=$GH_USER --licenses=MIT --vcs-url=https://github.com/$GH_USER/rpm $GH_USER/rpm/$GH_APP || echo "package already exists" 61 | - ./jfrog bt upload --override=true --key $BTKEY --publish=true $GH_APP-$OSARCH-$VERSION.rpm $GH_USER/rpm/$GH_APP/$VERSION pool/$POOL/$GH_APP/ 62 | # generate the repo metadata 63 | - curl -X POST -u ${GH_USER}:${BTKEY} https://api.bintray.com/calc_metadata/${GH_USER}/rpm 64 | 65 | deploy: 66 | provider: releases 67 | api_key: 68 | secure: Dmi4to/C++oBkvIX2Q+hYvUkikb3au3T0jbhXDgl3WxCaz//TX28TJNzjYU/kR0etXGuQCwafPd8rUcWnz02X9ZUN2PG0Vx28BmOmQ3n7+1EDEkRTOP5e6vI6ZUSeti/YYsNjPLiw4UpQYcCIVCp07Cw4MlNfS/8IL5gcfnsQWU+YiBxG8zp6v6oz8utBpqa2Ax6JZ5aELoOiO7s11LB2M8VwbE3/drwz0ztEpAkocIGIq5zgG2SuG3yPKqA80Pimu/k96qxxlLy/ZBE+EYWz2JFaN88V9RO01l7ccYesENeYABK+iU0bI88UTFfY+QAU71zPB75u1dQpEP5K2u3OseaUhq59YObKBHKl6Z6mPb61SbdTeEokf2XEPBN3RTHPPKIYezxYKCavjGXkAXTQAR5FBZdvGZk04Kxw5jT13X+UzTxeo9gSkQGng4JgSIO4M1wJT+gPezbxYF7huIFFMGKUaCDy60PU0gN7HuDjkdcFwjdYfk5BQkeoaI8sUTp4fhHtjXjiVU8opk+LPIYd7muAV3tNtQz14P+0zQlceRimP2hAklVRF/8XdSq+X5f1bKnRnolSlSYj6x8laGOpE2cQ1CPfVQIkxMD9r9ZZmER4VHvhsgXHmuvhnjGIp36u5aT7uBhdtPocyZsW56KhuwMLpuePmy7ykQr+PI11z4= 69 | file_glob: true 70 | file: 71 | - $GH_APP-$OKARCH.deb 72 | - $GH_APP-$OKARCH.rpm 73 | skip_cleanup: true 74 | on: 75 | tags: true 76 | -------------------------------------------------------------------------------- /.version.sh: -------------------------------------------------------------------------------- 1 | PREBUMP= 2 | 666 git fetch --tags origin master 3 | 666 git pull origin master 4 | 5 | PREVERSION= 6 | philea -s "666 go vet %s" "666 go-fmt-fail %s" 7 | go run main.go -v 8 | 666 changelog finalize --version !newversion! 9 | 666 commit -q -m "changelog: !newversion!" -f change.log 10 | 666 emd gen -out README.md 11 | 666 commit -q -m "README: !newversion!" -f README.md 12 | 13 | POSTVERSION= 14 | 666 changelog md -o CHANGELOG.md --guess 15 | 666 commit -q -m "changelog: !newversion!" -f CHANGELOG.md 16 | 666 go install --ldflags "-X main.VERSION=!newversion!" 17 | 666 git push 18 | 666 git push --tags 19 | 666 gh-api-cli create-release -n release -o mh-cbon -r go-msi \ 20 | --ver !newversion! -c "changelog ghrelease --version !newversion!" \ 21 | --draft !isprerelease! 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog - go-msi 2 | 3 | ### 1.0.2 4 | 5 | __Changes__ 6 | 7 | - `go-msi set-guid`: close #22: add `--force,-f` flags. 8 | - `go-msi check-env`: display a report of your environment. 9 | - close #17 #27: improve error message when --msi flag is not provided in the command line 10 | - choco: packages now include `tools/VERIFICATION.txt` and `tools/LICENSE.txt` to the package. 11 | 12 | __Contributors__ 13 | 14 | - mh-cbon 15 | - solvingJ 16 | 17 | Released by mh-cbon, Wed 23 Aug 2017 - 18 | [see the diff](https://github.com/mh-cbon/go-msi/compare/1.0.1...1.0.2#diff) 19 | ______________ 20 | 21 | ### 1.0.1 22 | 23 | __Changes__ 24 | 25 | - closes #2 Add support for (un)install hooks (run with elevated privileges) 26 | - workaround #9 Add support for services setup 27 | - closes #10 Add support for e2e tests with CI support 28 | - demo: add support for service 29 | - chocolatey: uninstall script, changed msiexec /qr argument to /q 30 | - testing: improve e2e tests to support services 31 | - testing: update vagrant scripts to run tests 32 | - package: make use of emd 33 | - package: change bump script to sh version 34 | - uuid(minor): close #7 remove useless code to generate an uuid 35 | - wix(minor): close #8 update product template to propagate environment variable changes 36 | - choco(minor): close #5 updated uninstaller script 37 | 38 | __Contributors__ 39 | 40 | - Alfonso Acosta 41 | - mh-cbon 42 | 43 | Released by mh-cbon, Tue 07 Mar 2017 - 44 | [see the diff](https://github.com/mh-cbon/go-msi/compare/1.0.0...1.0.1#diff) 45 | ______________ 46 | 47 | ### 1.0.0 48 | 49 | __Changes__ 50 | 51 | - winters udpate, udpated documentation and lint 52 | - templates(minor): renamed Id and Guid properties, 53 | remove try-catch in chocoinstall, 54 | set CRLF instead OF LF eol 55 | - lint(break): renamed fields 56 | ChocoSpec.Id to ChocoSpec.ID 57 | Chocopec.ProjectUrl to Chocopec.ProjectURL 58 | ChocoSpec.LicenseUrl to ChocoSpec.LicenseURL 59 | ChocoSpec.IconUrl to ChocoSpec.IconURL 60 | WixFiles.Guid to WixFiles.GUID 61 | WixEnvList.Guid to WixEnvList.GUID 62 | WixShortcuts.Guid to WixShortcuts.GUID 63 | This change does not impact json file format. 64 | - choco(minor): on chocolatey reviewer request, geet ride of the try catch 65 | 66 | __Contributors__ 67 | 68 | - mh-cbon 69 | 70 | Released by mh-cbon, Sun 08 Jan 2017 - 71 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.39...1.0.0#diff) 72 | ______________ 73 | 74 | ### 0.0.39 75 | 76 | __Changes__ 77 | 78 | - appveyor: update choco push key 79 | - choco: add checksum support. Closes #1 80 | - choco: fix pack command invokation, it was colliding with cmake 81 | 82 | __Contributors__ 83 | 84 | - mh-cbon 85 | 86 | Released by mh-cbon, Mon 15 Aug 2016 - 87 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.38...0.0.39#diff) 88 | ______________ 89 | 90 | ### 0.0.38 91 | 92 | __Changes__ 93 | 94 | - choco: ensure tags always contains admin value to pass chocolatey validation 95 | 96 | __Contributors__ 97 | 98 | - mh-cbon 99 | 100 | Released by mh-cbon, Fri 29 Jul 2016 - 101 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.37...0.0.38#diff) 102 | ______________ 103 | 104 | ### 0.0.37 105 | 106 | __Changes__ 107 | 108 | - Fix chocolatey package generation: Tags should not contain 'chocolatey' as a tag. 109 | 110 | __Contributors__ 111 | 112 | - mh-cbon 113 | 114 | Released by mh-cbon, Fri 29 Jul 2016 - 115 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.36...0.0.37#diff) 116 | ______________ 117 | 118 | ### 0.0.36 119 | 120 | __Changes__ 121 | 122 | - travis: fix gh secure token 123 | 124 | __Contributors__ 125 | 126 | - mh-cbon 127 | 128 | Released by mh-cbon, Fri 29 Jul 2016 - 129 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.35...0.0.36#diff) 130 | ______________ 131 | 132 | ### 0.0.35 133 | 134 | __Changes__ 135 | 136 | - rpm: fix templates path inlusion 137 | - README: update install section 138 | 139 | __Contributors__ 140 | 141 | - mh-cbon 142 | 143 | Released by mh-cbon, Fri 29 Jul 2016 - 144 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.34...0.0.35#diff) 145 | ______________ 146 | 147 | ### 0.0.34 148 | 149 | __Changes__ 150 | 151 | - build: fix the msi file generation 152 | - appveyor: artifacts must be created in build_script section 153 | 154 | __Contributors__ 155 | 156 | - mh-cbon 157 | 158 | Released by mh-cbon, Fri 29 Jul 2016 - 159 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.33...0.0.34#diff) 160 | ______________ 161 | 162 | ### 0.0.33 163 | 164 | __Changes__ 165 | 166 | - cli: add choco command to generate chocolatey packages. 167 | - Demo: add choco commands 168 | - build: update build scripts 169 | 170 | __Contributors__ 171 | 172 | - mh-cbon 173 | 174 | Released by mh-cbon, Fri 29 Jul 2016 - 175 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.32...0.0.33#diff) 176 | ______________ 177 | 178 | ### 0.0.32 179 | 180 | __Changes__ 181 | 182 | - wix: fix minimum/maximum version value of UpgradeVersion field in the product template 183 | 184 | __Contributors__ 185 | 186 | - mh-cbon 187 | 188 | Released by mh-cbon, Sat 23 Jul 2016 - 189 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.31...0.0.32#diff) 190 | ______________ 191 | 192 | ### 0.0.31 193 | 194 | __Changes__ 195 | 196 | - wix: fix version format for Product element field. 197 | When version value contains prerelease/metadata, it is not acceptable 198 | for wix. A new field is added to the manifest VersionOk containing the version 199 | string without prerelease/metadata value. 200 | product.wxs template now uses this new VersionOk field 201 | instead of the original Version field. 202 | - glide: add semver dependency 203 | - README: install section 204 | 205 | __Contributors__ 206 | 207 | - mh-cbon 208 | 209 | Released by mh-cbon, Sat 23 Jul 2016 - 210 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.30...0.0.31#diff) 211 | ______________ 212 | 213 | ### 0.0.30 214 | 215 | __Changes__ 216 | 217 | - travis: template inclusion 218 | 219 | __Contributors__ 220 | 221 | - mh-cbon 222 | 223 | Released by mh-cbon, Fri 15 Jul 2016 - 224 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.29...0.0.30#diff) 225 | ______________ 226 | 227 | ### 0.0.29 228 | 229 | __Changes__ 230 | 231 | - travis: fix missing changelog setup into docker image 232 | 233 | __Contributors__ 234 | 235 | - mh-cbon 236 | 237 | Released by mh-cbon, Fri 15 Jul 2016 - 238 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.28...0.0.29#diff) 239 | ______________ 240 | 241 | ### 0.0.28 242 | 243 | __Changes__ 244 | 245 | - rpm: add missing docker support 246 | 247 | __Contributors__ 248 | 249 | - mh-cbon 250 | 251 | Released by mh-cbon, Fri 15 Jul 2016 - 252 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.27...0.0.28#diff) 253 | ______________ 254 | 255 | ### 0.0.27 256 | 257 | __Changes__ 258 | 259 | - rpm: add rpm support 260 | - debian: remove useless urgency var 261 | 262 | __Contributors__ 263 | 264 | - mh-cbon 265 | 266 | Released by mh-cbon, Fri 15 Jul 2016 - 267 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.26...0.0.27#diff) 268 | ______________ 269 | 270 | ### 0.0.26 271 | 272 | __Changes__ 273 | 274 | - travis: update deb installers 275 | 276 | __Contributors__ 277 | 278 | - mh-cbon 279 | 280 | Released by mh-cbon, Fri 15 Jul 2016 - 281 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.25...0.0.26#diff) 282 | ______________ 283 | 284 | ### 0.0.25 285 | 286 | __Changes__ 287 | 288 | - Demo: add a demo with recipe commands 289 | - Code: add comments 290 | - Wix: Add Shortcuts icon support 291 | - Manifest: add icon support for shotcuts, add comments 292 | - wix.json: env var does not need to be set system wide 293 | 294 | __Contributors__ 295 | 296 | - mh-cbon 297 | 298 | Released by mh-cbon, Fri 15 Jul 2016 - 299 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.24...0.0.25#diff) 300 | ______________ 301 | 302 | ### 0.0.24 303 | 304 | __Changes__ 305 | 306 | - travis: ensure changelog is installed 307 | - recipe: fix curl options and register go-msi PATH 308 | - appveyor: remove useless DIR command 309 | - appveyor: remove -v option to curl 310 | 311 | __Contributors__ 312 | 313 | - mh-cbon 314 | 315 | Released by mh-cbon, Tue 12 Jul 2016 - 316 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.23...0.0.24#diff) 317 | ______________ 318 | 319 | ### 0.0.23 320 | 321 | __Changes__ 322 | 323 | - pkg: add deb package support 324 | - env: set env as system wide 325 | - main: add option for non windows built 326 | - appveyor: fix cur options to follow location redirects 327 | - release: add changelog support to release script 328 | - changelog: add new changelog 329 | - manifest: omit json fields when empty 330 | - wix.json: remove useless version field 331 | - README: add install from source section 332 | - recipes: improve commands and typos 333 | 334 | __Contributors__ 335 | 336 | - mh-cbon 337 | 338 | Released by mh-cbon, Mon 11 Jul 2016 - 339 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.22...0.0.23#diff) 340 | ______________ 341 | 342 | ### 0.0.22 343 | 344 | __Changes__ 345 | 346 | - align arch arguments with GO standards 347 | - improve recipes commands and typos 348 | 349 | __Contributors__ 350 | 351 | - mh-cbon 352 | 353 | Released by mh-cbon, Sun 26 Jun 2016 - 354 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.21...0.0.22#diff) 355 | ______________ 356 | 357 | ### 0.0.21 358 | 359 | __Changes__ 360 | 361 | - go fmt 362 | - improve recipes commands and typos 363 | - align arch arguments with GO standards 364 | - update recipes 365 | 366 | __Contributors__ 367 | 368 | - mh-cbon 369 | 370 | Released by mh-cbon, Sun 26 Jun 2016 - 371 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.20...0.0.21#diff) 372 | ______________ 373 | 374 | ### 0.0.20 375 | 376 | __Changes__ 377 | 378 | - update appveyor recipe 379 | - tryfix for ldflags 380 | 381 | __Contributors__ 382 | 383 | - mh-cbon 384 | 385 | Released by mh-cbon, Sun 26 Jun 2016 - 386 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.19...0.0.20#diff) 387 | ______________ 388 | 389 | ### 0.0.19 390 | 391 | __Changes__ 392 | 393 | - go fmt 394 | - fix path lookpath in guuid make for windows 395 | - Set version to the build 396 | - README 397 | 398 | __Contributors__ 399 | 400 | - mh-cbon 401 | 402 | Released by mh-cbon, Sun 26 Jun 2016 - 403 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.18...0.0.19#diff) 404 | ______________ 405 | 406 | ### 0.0.18 407 | 408 | __Changes__ 409 | 410 | - go fmt 411 | - avoid variable shadowing 412 | - update recipes 413 | 414 | __Contributors__ 415 | 416 | - mh-cbon 417 | 418 | Released by mh-cbon, Sun 26 Jun 2016 - 419 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.17...0.0.18#diff) 420 | ______________ 421 | 422 | ### 0.0.17 423 | 424 | __Changes__ 425 | 426 | - go fmt 427 | 428 | __Contributors__ 429 | 430 | - mh-cbon 431 | 432 | Released by mh-cbon, Sat 25 Jun 2016 - 433 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.16...0.0.17#diff) 434 | ______________ 435 | 436 | ### 0.0.16 437 | 438 | __Changes__ 439 | 440 | - go fmt 441 | 442 | __Contributors__ 443 | 444 | - mh-cbon 445 | 446 | Released by mh-cbon, Sat 25 Jun 2016 - 447 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.15...0.0.16#diff) 448 | ______________ 449 | 450 | ### 0.0.15 451 | 452 | __Changes__ 453 | 454 | - go fmt 455 | - fix HOWTOs 456 | - fix bin path detection 457 | - updates 458 | 459 | __Contributors__ 460 | 461 | - mh-cbon 462 | 463 | Released by mh-cbon, Sat 25 Jun 2016 - 464 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.14...0.0.15#diff) 465 | ______________ 466 | 467 | ### 0.0.14 468 | 469 | __Changes__ 470 | 471 | - appveyor 472 | 473 | __Contributors__ 474 | 475 | - mh-cbon 476 | 477 | Released by mh-cbon, Sat 25 Jun 2016 - 478 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.13...0.0.14#diff) 479 | ______________ 480 | 481 | ### 0.0.13 482 | 483 | __Changes__ 484 | 485 | - appveyor 486 | 487 | __Contributors__ 488 | 489 | - mh-cbon 490 | 491 | Released by mh-cbon, Sat 25 Jun 2016 - 492 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.12...0.0.13#diff) 493 | ______________ 494 | 495 | ### 0.0.12 496 | 497 | __Changes__ 498 | 499 | - appveyor 500 | 501 | __Contributors__ 502 | 503 | - mh-cbon 504 | 505 | Released by mh-cbon, Sat 25 Jun 2016 - 506 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.11...0.0.12#diff) 507 | ______________ 508 | 509 | ### 0.0.11 510 | 511 | __Changes__ 512 | 513 | - appveyor 514 | 515 | __Contributors__ 516 | 517 | - mh-cbon 518 | 519 | Released by mh-cbon, Sat 25 Jun 2016 - 520 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.10...0.0.11#diff) 521 | ______________ 522 | 523 | ### 0.0.10 524 | 525 | __Changes__ 526 | 527 | - appveyor 528 | 529 | __Contributors__ 530 | 531 | - mh-cbon 532 | 533 | Released by mh-cbon, Sat 25 Jun 2016 - 534 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.9...0.0.10#diff) 535 | ______________ 536 | 537 | ### 0.0.9 538 | 539 | __Changes__ 540 | 541 | - appveyor 542 | 543 | __Contributors__ 544 | 545 | - mh-cbon 546 | 547 | Released by mh-cbon, Sat 25 Jun 2016 - 548 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.8...0.0.9#diff) 549 | ______________ 550 | 551 | ### 0.0.8 552 | 553 | __Changes__ 554 | 555 | - appveyor 556 | 557 | __Contributors__ 558 | 559 | - mh-cbon 560 | 561 | Released by mh-cbon, Sat 25 Jun 2016 - 562 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.7...0.0.8#diff) 563 | ______________ 564 | 565 | ### 0.0.7 566 | 567 | __Changes__ 568 | 569 | - appveyor 570 | 571 | __Contributors__ 572 | 573 | - mh-cbon 574 | 575 | Released by mh-cbon, Sat 25 Jun 2016 - 576 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.6...0.0.7#diff) 577 | ______________ 578 | 579 | ### 0.0.6 580 | 581 | __Changes__ 582 | 583 | - appveyor 584 | 585 | __Contributors__ 586 | 587 | - mh-cbon 588 | 589 | Released by mh-cbon, Sat 25 Jun 2016 - 590 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.5...0.0.6#diff) 591 | ______________ 592 | 593 | ### 0.0.5 594 | 595 | __Changes__ 596 | 597 | - appveyor 598 | 599 | __Contributors__ 600 | 601 | - mh-cbon 602 | 603 | Released by mh-cbon, Sat 25 Jun 2016 - 604 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.4...0.0.5#diff) 605 | ______________ 606 | 607 | ### 0.0.4 608 | 609 | __Changes__ 610 | 611 | - appveyor 612 | 613 | __Contributors__ 614 | 615 | - mh-cbon 616 | 617 | Released by mh-cbon, Sat 25 Jun 2016 - 618 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.3...0.0.4#diff) 619 | ______________ 620 | 621 | ### 0.0.3 622 | 623 | __Changes__ 624 | 625 | - appveyor 626 | - appveyor 627 | 628 | __Contributors__ 629 | 630 | - mh-cbon 631 | 632 | Released by mh-cbon, Sat 25 Jun 2016 - 633 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.2...0.0.3#diff) 634 | ______________ 635 | 636 | ### 0.0.2 637 | 638 | __Changes__ 639 | 640 | - appveyor 641 | 642 | __Contributors__ 643 | 644 | - mh-cbon 645 | 646 | Released by mh-cbon, Sat 25 Jun 2016 - 647 | [see the diff](https://github.com/mh-cbon/go-msi/compare/0.0.1...0.0.2#diff) 648 | ______________ 649 | 650 | ### 0.0.1 651 | 652 | __Changes__ 653 | 654 | - Initial release 655 | 656 | __Contributors__ 657 | 658 | - mh-cbon 659 | 660 | Released by mh-cbon, Sat 25 Jun 2016 - 661 | [see the diff](https://github.com/mh-cbon/go-msi/compare/f4041400c510163f8e0aa684d68ebbc3e9ad4e44...0.0.1#diff) 662 | ______________ 663 | 664 | 665 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 西郷 隆盛 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.e.md: -------------------------------------------------------------------------------- 1 | Sponsor 2 | 3 | # {{.Name}} 4 | 5 | {{template "badge/appveyor" .}} 6 | 7 | {{pkgdoc}} 8 | 9 | This tool is part of the [go-github-release workflow](https://github.com/mh-cbon/go-github-release) 10 | 11 | Find a demo program [here](https://github.com/mh-cbon/go-msi/tree/master/testing/hello) 12 | 13 | # {{toc 5}} 14 | 15 | # Install 16 | 17 | {{template "gh/releases" .}} 18 | 19 | #### Go 20 | {{template "go/install" .}} 21 | 22 | #### Bintray 23 | {{template "choco_bintray/install" .}} 24 | 25 | #### Chocolatey 26 | {{template "choco/install" .}} 27 | 28 | #### linux rpm/deb repository 29 | {{template "linux/bintray_repo" .}} 30 | 31 | #### linux rpm/deb standalone package 32 | {{template "linux/gh_pkg" .}} 33 | 34 | # Usage 35 | 36 | ### Requirements 37 | 38 | - A windows machine (see [here](https://github.com/mh-cbon/go-msi/blob/master/appveyor-recipe.md) for an appveyor file, see [here](https://github.com/mh-cbon/go-msi/blob/master/unice-recipe.md) for unix friendly users) 39 | - wix >= 3.10 (may work on older release, but it is untested, feel free to report) 40 | - you must add wix bin to your `PATH` 41 | - use `check-env` sub command to get a report. 42 | 43 | ### Workflow 44 | 45 | For simple cases, 46 | 47 | - Create a `wix.json` file like [this one](https://github.com/mh-cbon/go-msi/blob/master/wix.json) 48 | - Apply it guids with `go-msi set-guid`, you must do it once only for each app. 49 | - Run `go-msi make --msi your_program.msi --version 0.0.2` 50 | 51 | ### configuration file 52 | 53 | `wix.json` file describe the desired packaging rules between your sources and the resulting msi file. 54 | 55 | [Check the demo json file](https://github.com/mh-cbon/go-msi/blob/master/testing/hello/wix.json) 56 | 57 | Post an issue if it is not self-explanatory. 58 | 59 | Always double check the documentation and [SO](https://stackoverflow.com) 60 | when you face a difficulty with `heat`, `candle`, `light` 61 | 62 | - http://wixtoolset.org/documentation/ 63 | - http://stackoverflow.com/questions/tagged/wix 64 | 65 | If you wonder why `INSTALLDIR`, `[INSTALLDIR]`, this is part of wix rules, please check their documentation. 66 | 67 | ### License file 68 | 69 | Take care to the license file, it must be an `rtf` file, it must be encoded with `Windows1252` charset. 70 | 71 | I have provided some tools to help with that matter. 72 | 73 | # Personnalization 74 | 75 | ### wix templates 76 | 77 | For simplicity a default install flow is provided, which you can find [here](https://github.com/mh-cbon/go-msi/tree/master/templates) 78 | 79 | You can create a new one for your own personalization, 80 | you should only take care to reproduce the go templating already 81 | defined for `files`, `directories`, `environment variables`, `license` and `shortcuts`. 82 | 83 | I guess most of your changes will be about the `WixUI_HK.wxs` file. 84 | 85 | # Cli 86 | 87 | ###### $ {{exec "go-msi" "-h" | color "sh"}} 88 | 89 | ###### $ {{exec "go-msi" "check-env" "-h" | color "sh"}} 90 | 91 | ###### $ {{exec "go-msi" "check-json" "-h" | color "sh"}} 92 | 93 | ###### $ {{exec "go-msi" "set-guid" "-h" | color "sh"}} 94 | 95 | ###### $ {{exec "go-msi" "make" "-h" | color "sh"}} 96 | 97 | ###### $ {{exec "go-msi" "choco" "-h" | color "sh"}} 98 | 99 | ###### $ {{exec "go-msi" "generate-templates" "-h" | color "sh"}} 100 | 101 | ###### $ {{exec "go-msi" "to-windows" "-h" | color "sh"}} 102 | 103 | ###### $ {{exec "go-msi" "to-rtf" "-h" | color "sh"}} 104 | 105 | ###### $ {{exec "go-msi" "gen-wix-cmd" "-h" | color "sh"}} 106 | 107 | ###### $ {{exec "go-msi" "run-wix-cmd" "-h" | color "sh"}} 108 | 109 | # Recipes 110 | 111 | ### Appveyor 112 | 113 | Please check [this](https://github.com/mh-cbon/go-msi/blob/master/appveyor-recipe.md) 114 | 115 | ### Unix like 116 | 117 | Please check [this](https://github.com/mh-cbon/go-msi/blob/master/unice-recipe.md) 118 | 119 | ### Release the project 120 | 121 | ```sh 122 | gump patch -d # check 123 | gump patch # bump 124 | ``` 125 | 126 | # History 127 | 128 | [CHANGELOG](CHANGELOG.md) 129 | 130 | # Credits 131 | 132 | A big big thanks to 133 | 134 | - `Helge Klein`, which i do not know personally, but made this project possible by sharing a real world example at 135 | https://helgeklein.com/blog/2014/09/real-world-example-wix-msi-application-installer/ 136 | - all SO contributors on `wix` tag. 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sponsor 2 | 3 | # go-msi 4 | 5 | [![Appveyor Status](https://ci.appveyor.com/api/projects/status/github/mh-cbon/go-msi?branch=master&svg=true)](https://ci.appveyor.com/project/mh-cbon/go-msi) 6 | 7 | Package go-msi helps to generate msi package for a Go project. 8 | 9 | 10 | This tool is part of the [go-github-release workflow](https://github.com/mh-cbon/go-github-release) 11 | 12 | Find a demo program [here](https://github.com/mh-cbon/go-msi/tree/master/testing/hello) 13 | 14 | # TOC 15 | - [Install](#install) 16 | - [Go](#go) 17 | - [Bintray](#bintray) 18 | - [Chocolatey](#chocolatey) 19 | - [linux rpm/deb repository](#linux-rpmdeb-repository) 20 | - [linux rpm/deb standalone package](#linux-rpmdeb-standalone-package) 21 | - [Usage](#usage) 22 | - [Requirements](#requirements) 23 | - [Workflow](#workflow) 24 | - [configuration file](#configuration-file) 25 | - [License file](#license-file) 26 | - [Personnalization](#personnalization) 27 | - [wix templates](#wix-templates) 28 | - [Cli](#cli) 29 | - [Recipes](#recipes) 30 | - [Appveyor](#appveyor) 31 | - [Unix like](#unix-like) 32 | - [Release the project](#release-the-project) 33 | - [History](#history) 34 | - [Credits](#credits) 35 | 36 | # Install 37 | 38 | Check the [release page](https://github.com/mh-cbon/go-msi/releases)! 39 | 40 | #### Go 41 | ```sh 42 | go get github.com/mh-cbon/go-msi 43 | ``` 44 | 45 | #### Bintray 46 | ```sh 47 | choco source add -n=mh-cbon -s="https://api.bintray.com/nuget/mh-cbon/choco" 48 | choco install go-msi 49 | ``` 50 | 51 | #### Chocolatey 52 | ```sh 53 | choco install go-msi 54 | ``` 55 | 56 | #### linux rpm/deb repository 57 | ```sh 58 | wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 59 | | GH=mh-cbon/go-msi sh -xe 60 | # or 61 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 62 | | GH=mh-cbon/go-msi sh -xe 63 | ``` 64 | 65 | #### linux rpm/deb standalone package 66 | ```sh 67 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 68 | | GH=mh-cbon/go-msi sh -xe 69 | # or 70 | wget -q -O - --no-check-certificate \ 71 | https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 72 | | GH=mh-cbon/go-msi sh -xe 73 | ``` 74 | 75 | # Usage 76 | 77 | ### Requirements 78 | 79 | - A windows machine (see [here](https://github.com/mh-cbon/go-msi/blob/master/appveyor-recipe.md) for an appveyor file, see [here](https://github.com/mh-cbon/go-msi/blob/master/unice-recipe.md) for unix friendly users) 80 | - wix >= 3.10 (may work on older release, but it is untested, feel free to report) 81 | - you must add wix bin to your `PATH` 82 | - use `check-env` sub command to get a report. 83 | 84 | ### Workflow 85 | 86 | For simple cases, 87 | 88 | - Create a `wix.json` file like [this one](https://github.com/mh-cbon/go-msi/blob/master/wix.json) 89 | - Apply it guids with `go-msi set-guid`, you must do it once only for each app. 90 | - Run `go-msi make --msi your_program.msi --version 0.0.2` 91 | 92 | ### configuration file 93 | 94 | `wix.json` file describe the desired packaging rules between your sources and the resulting msi file. 95 | 96 | [Check the demo json file](https://github.com/mh-cbon/go-msi/blob/master/testing/hello/wix.json) 97 | 98 | Post an issue if it is not self-explanatory. 99 | 100 | Always double check the documentation and [SO](https://stackoverflow.com) 101 | when you face a difficulty with `heat`, `candle`, `light` 102 | 103 | - http://wixtoolset.org/documentation/ 104 | - http://stackoverflow.com/questions/tagged/wix 105 | 106 | If you wonder why `INSTALLDIR`, `[INSTALLDIR]`, this is part of wix rules, please check their documentation. 107 | 108 | ### License file 109 | 110 | Take care to the license file, it must be an `rtf` file, it must be encoded with `Windows1252` charset. 111 | 112 | I have provided some tools to help with that matter. 113 | 114 | # Personnalization 115 | 116 | ### wix templates 117 | 118 | For simplicity a default install flow is provided, which you can find [here](https://github.com/mh-cbon/go-msi/tree/master/templates) 119 | 120 | You can create a new one for your own personalization, 121 | you should only take care to reproduce the go templating already 122 | defined for `files`, `directories`, `environment variables`, `license` and `shortcuts`. 123 | 124 | I guess most of your changes will be about the `WixUI_HK.wxs` file. 125 | 126 | # Cli 127 | 128 | ###### $ go-msi -h 129 | ```sh 130 | NAME: 131 | go-msi - Easy msi pakage for Go 132 | 133 | USAGE: 134 | go-msi 135 | 136 | VERSION: 137 | 0.0.0 138 | 139 | COMMANDS: 140 | check-json Check the JSON wix manifest 141 | check-env Provide a report about your environment setup 142 | set-guid Sets appropriate guids in your wix manifest 143 | generate-templates Generate wix templates 144 | to-windows Write Windows1252 encoded file 145 | to-rtf Write RTF formatted file 146 | gen-wix-cmd Generate a batch file of Wix commands to run 147 | run-wix-cmd Run the batch file of Wix commands 148 | make All-in-one command to make MSI files 149 | choco Generate a chocolatey package of your msi files 150 | help, h Shows a list of commands or help for one command 151 | 152 | GLOBAL OPTIONS: 153 | --help, -h show help 154 | --version, -v print the version 155 | ``` 156 | 157 | ###### $ go-msi check-env -h 158 | ```sh 159 | NAME: 160 | go-msi check-env - Provide a report about your environment setup 161 | 162 | USAGE: 163 | go-msi check-env [arguments...] 164 | ``` 165 | 166 | ###### $ go-msi check-json -h 167 | ```sh 168 | NAME: 169 | go-msi check-json - Check the JSON wix manifest 170 | 171 | USAGE: 172 | go-msi check-json [command options] [arguments...] 173 | 174 | OPTIONS: 175 | --path value, -p value Path to the wix manifest file (default: "wix.json") 176 | ``` 177 | 178 | ###### $ go-msi set-guid -h 179 | ```sh 180 | NAME: 181 | go-msi set-guid - Sets appropriate guids in your wix manifest 182 | 183 | USAGE: 184 | go-msi set-guid [command options] [arguments...] 185 | 186 | OPTIONS: 187 | --path value, -p value Path to the wix manifest file (default: "wix.json") 188 | --force, -f Force update the guids 189 | ``` 190 | 191 | ###### $ go-msi make -h 192 | ```sh 193 | NAME: 194 | go-msi make - All-in-one command to make MSI files 195 | 196 | USAGE: 197 | go-msi make [command options] [arguments...] 198 | 199 | OPTIONS: 200 | --path value, -p value Path to the wix manifest file (default: "wix.json") 201 | --src value, -s value Directory path to the wix templates files (default: "/home/mh-cbon/gow/bin/templates") 202 | --out value, -o value Directory path to the generated wix cmd file (default: "/tmp/go-msi645264968") 203 | --arch value, -a value A target architecture, amd64 or 386 (ia64 is not handled) 204 | --msi value, -m value Path to write resulting msi file to 205 | --version value The version of your program 206 | --license value, -l value Path to the license file 207 | --keep, -k Keep output directory containing build files (useful for debug) 208 | ``` 209 | 210 | ###### $ go-msi choco -h 211 | ```sh 212 | NAME: 213 | go-msi choco - Generate a chocolatey package of your msi files 214 | 215 | USAGE: 216 | go-msi choco [command options] [arguments...] 217 | 218 | OPTIONS: 219 | --path value, -p value Path to the wix manifest file (default: "wix.json") 220 | --src value, -s value Directory path to the wix templates files (default: "/home/mh-cbon/gow/bin/templates/choco") 221 | --version value The version of your program 222 | --out value, -o value Directory path to the generated chocolatey build file (default: "/tmp/go-msi697894350") 223 | --input value, -i value Path to the msi file to package into the chocolatey package 224 | --changelog-cmd value, -c value A command to generate the content of the changlog in the package 225 | --keep, -k Keep output directory containing build files (useful for debug) 226 | ``` 227 | 228 | ###### $ go-msi generate-templates -h 229 | ```sh 230 | NAME: 231 | go-msi generate-templates - Generate wix templates 232 | 233 | USAGE: 234 | go-msi generate-templates [command options] [arguments...] 235 | 236 | OPTIONS: 237 | --path value, -p value Path to the wix manifest file (default: "wix.json") 238 | --src value, -s value Directory path to the wix templates files (default: "/home/mh-cbon/gow/bin/templates") 239 | --out value, -o value Directory path to the generated wix templates files (default: "/tmp/go-msi522345138") 240 | --version value The version of your program 241 | --license value, -l value Path to the license file 242 | ``` 243 | 244 | ###### $ go-msi to-windows -h 245 | ```sh 246 | NAME: 247 | go-msi to-windows - Write Windows1252 encoded file 248 | 249 | USAGE: 250 | go-msi to-windows [command options] [arguments...] 251 | 252 | OPTIONS: 253 | --src value, -s value Path to an UTF-8 encoded file 254 | --out value, -o value Path to the ANSI generated file 255 | ``` 256 | 257 | ###### $ go-msi to-rtf -h 258 | ```sh 259 | NAME: 260 | go-msi to-rtf - Write RTF formatted file 261 | 262 | USAGE: 263 | go-msi to-rtf [command options] [arguments...] 264 | 265 | OPTIONS: 266 | --src value, -s value Path to a text file 267 | --out value, -o value Path to the RTF generated file 268 | --reencode, -e Also re encode UTF-8 to Windows1252 charset 269 | ``` 270 | 271 | ###### $ go-msi gen-wix-cmd -h 272 | ```sh 273 | NAME: 274 | go-msi gen-wix-cmd - Generate a batch file of Wix commands to run 275 | 276 | USAGE: 277 | go-msi gen-wix-cmd [command options] [arguments...] 278 | 279 | OPTIONS: 280 | --path value, -p value Path to the wix manifest file (default: "wix.json") 281 | --src value, -s value Directory path to the wix templates files (default: "/home/mh-cbon/gow/bin/templates") 282 | --out value, -o value Directory path to the generated wix cmd file (default: "/tmp/go-msi844736928") 283 | --arch value, -a value A target architecture, amd64 or 386 (ia64 is not handled) 284 | --msi value, -m value Path to write resulting msi file to 285 | ``` 286 | 287 | ###### $ go-msi run-wix-cmd -h 288 | ```sh 289 | NAME: 290 | go-msi run-wix-cmd - Run the batch file of Wix commands 291 | 292 | USAGE: 293 | go-msi run-wix-cmd [command options] [arguments...] 294 | 295 | OPTIONS: 296 | --out value, -o value Directory path to the generated wix cmd file (default: "/tmp/go-msi773158361") 297 | ``` 298 | 299 | # Recipes 300 | 301 | ### Appveyor 302 | 303 | Please check [this](https://github.com/mh-cbon/go-msi/blob/master/appveyor-recipe.md) 304 | 305 | ### Unix like 306 | 307 | Please check [this](https://github.com/mh-cbon/go-msi/blob/master/unice-recipe.md) 308 | 309 | ### Release the project 310 | 311 | ```sh 312 | gump patch -d # check 313 | gump patch # bump 314 | ``` 315 | 316 | # History 317 | 318 | [CHANGELOG](CHANGELOG.md) 319 | 320 | # Credits 321 | 322 | A big big thanks to 323 | 324 | - `Helge Klein`, which i do not know personally, but made this project possible by sharing a real world example at 325 | https://helgeklein.com/blog/2014/09/real-world-example-wix-msi-application-installer/ 326 | - all SO contributors on `wix` tag. 327 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | # on fedora-like, you must use the downloadable package of vagrant 7 | # available on hashi corp website. 8 | # The per distrib provided package wont provide winrm support.. 9 | # see https://www.vagrantup.com/downloads.html 10 | 11 | config.vm.define :win2012 do |win| 12 | win.vm.box = "opentable/win-2012r2-standard-amd64-nocm" 13 | # big timeout since windows boot is very slow 14 | win.vm.boot_timeout = 500 15 | win.vm.communicator = :winrm 16 | win.vm.provider "virtualbox" do |vb| 17 | # first setup requires gui to be enabled so scripts can be executed in virtualbox guest screen 18 | vb.gui = false 19 | vb.gui = true 20 | vb.customize ["modifyvm", :id, "--memory", "1524"] 21 | vb.customize ["modifyvm", :id, "--vram", "128"] 22 | vb.customize ["modifyvm", :id, "--cpus", "1"] 23 | vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"] 24 | vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 25 | vb.customize ["guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 10000] 26 | end 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor-recipe.md: -------------------------------------------------------------------------------- 1 | # appveyor recipe to create msi package 2 | 3 | This is an HOWDOI build an msi package from a windows machine using appveyor cloud service. 4 | 5 | 6 | ### Requirements 7 | 8 | - a github account 9 | - a github repo 10 | - an appveyor account 11 | 12 | 13 | ### Repo setup 14 | 15 | 1. Add your repo to appveyor config 16 | 2. Generate a new Github API token (Settings->Personal access tokens->Generate new token). Give it authorizations to `repo`. 17 | 3. Save the new token! 18 | 3. Add a new `appveyor.yml` to the root of your repo 19 | 4. Encrypt the token value as an appveyor secure variable (AppVeyor->Dashboard->[Encrypt Data](https://ci.appveyor.com/tools/encrypt)) 20 | 5. Modify below appveyor template 21 | 22 | __appveyor.yml__ 23 | 24 | ```yml 25 | image: Visual Studio 2017 26 | clone_folder: c:\gopath\src\github.com\%APPVEYOR_REPO_NAME% 27 | 28 | # trigger build/deploy only on tag 29 | # if false, take care that %APPVEYOR_REPO_TAG_NAME% won t be set on commit 30 | # this will fail the build 31 | skip_non_tags: true 32 | 33 | environment: 34 | GOPATH: c:\gopath 35 | JFROG_CLI_OFFER_CONFIG: false 36 | VCS_URL: https://github.com/%APPVEYOR_REPO_NAME% 37 | BT_KEY: 38 | secure: yyyy 39 | CHOCOKEY: 40 | secure: xxxxx 41 | 42 | install: 43 | # wix setup 44 | - set PATH=%WIX%\bin;%PATH% 45 | # go setup 46 | - set PATH=%GOPATH%\bin;%PATH% 47 | # glide setup, if your package uses it 48 | - go get -u github.com/mh-cbon/never-fail 49 | - go get -u github.com/Masterminds/glide 50 | # setup go-msi, choose one method 51 | # 52 | # method 1: static link 53 | # - curl -fsSL -o C:\go-msi.msi https://github.com/mh-cbon/go-msi/releases/download/0.0.22/go-msi-amd64.msi 54 | # - msiexec.exe /i C:\go-msi.msi /quiet 55 | # - set PATH=C:\Program Files\go-msi\;%PATH% # for some reason, go-msi path needs to be added manually :(... 56 | # 57 | # method 2: via gh-api-cli 58 | # - curl -fsSL -o C:\latest.bat https://raw.githubusercontent.com/mh-cbon/latest/master/latest.bat 59 | # - cmd /C C:\latest.bat mh-cbon go-msi amd64 60 | # - set PATH=C:\Program Files\go-msi\;%PATH% 61 | # 62 | # method 3: via chocolatey 63 | - choco install changelog -y 64 | - choco install go-msi -y 65 | - glide install 66 | 67 | 68 | build_script: 69 | - set GH_APP=%APPVEYOR_PROJECT_NAME% 70 | - set GH_USER=%APPVEYOR_ACCOUNT_NAME% 71 | - set VERSION=%APPVEYOR_REPO_TAG_NAME% 72 | # - set VERSION=1.0.2 # fake it when needed 73 | - set GOARCH=386 74 | - go build -o %MYAPP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 75 | - .\go-msi.exe make --msi %GH_APP%-%GOARCH%.msi --version %VERSION% --arch %GOARCH% 76 | - set GOARCH=amd64 77 | - go build -o %MYAPP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 78 | - .\go-msi.exe make --msi %GH_APP%-%GOARCH%.msi --version %VERSION% --arch %GOARCH% 79 | 80 | after_deploy: 81 | # Choco push 82 | - .\go-msi.exe choco --input %GH_APP%-%GOARCH%.msi --version %VERSION% --changelog-cmd "changelog ghrelease --version %VERSION%" 83 | - choco push -k="'%CHOCOKEY%'" %GH_APP%.%VERSION%.nupkg 84 | # Bintray push 85 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/msi/%GH_APP% 86 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%-%GOARCH%-%VERSION%.msi %GH_USER%/msi/%GH_APP%/%VERSION% 87 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/choco/%GH_APP% 88 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%.%VERSION%.nupkg %GH_USER%/choco/%GH_APP%/%VERSION% 89 | 90 | 91 | # to disable automatic tests 92 | test: off 93 | 94 | # need this to deploy assets, 95 | # note that each MUST match only one file 96 | artifacts: 97 | - path: '*-386.msi' 98 | name: msi-x86 99 | - path: '*-amd64.msi' 100 | name: msi-x64 101 | 102 | # deploy section to github releases 103 | deploy: 104 | - provider: GitHub 105 | # it should be possible to use a regexp like this /msi.*/, 106 | # but I could not make it work, let me know if you find a solution 107 | artifact: msi-x86, msi-x64 108 | draft: false 109 | prerelease: false 110 | description: "Release {APPVEYOR_REPO_TAG_NAME}" 111 | auth_token: 112 | # Change this to your encrypted token value 113 | secure: xxxxx 114 | on: 115 | branch: 116 | - master 117 | - /v\d\.\d\.\d/ 118 | - /\d\.\d\.\d/ 119 | appveyor_repo_tag: true 120 | ``` 121 | 122 | ### Workflow 123 | 124 | With this `appveyor.yml` config, 125 | every time you create a tag and push it on the remote, 126 | `msi` files are generated and uploaded to your github release. 127 | 128 | `choco` package is generated from the amd64 build, 129 | and uploaded to your choco account. 130 | 131 | Both `msi` nad `nupkg` artifacts are uploaded to your bintray repos (/msi/ and /choco/) 132 | 133 | [go here](https://ci.appveyor.com/tools/encrypt) 134 | to generate the secure variable containing your `choco` api key and your `bintray` key. 135 | 136 | For an easy way to bump, 137 | you can use [gump](https://github.com/mh-cbon/gump), 138 | with a script like this, 139 | 140 | ```yml 141 | scripts: 142 | prebump: 666 git fetch --tags 143 | preversion: | 144 | philea -s "666 go vet %s" "666 go-fmt-fail %s" \ 145 | && go run main.go -v 146 | postversion: | 147 | 666 git push && 666 git push --tags \ 148 | && 666 gh-api-cli create-release -n release -o YOURUSER -r YOURREPO --ver !newversion! 149 | ``` 150 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | clone_folder: c:\gopath\src\github.com\%APPVEYOR_ACCOUNT_NAME%\%APPVEYOR_PROJECT_SLUG% 3 | 4 | # skip_non_tags: true 5 | 6 | # environment variables 7 | environment: 8 | GOPATH: c:\gopath 9 | JFROG_CLI_OFFER_CONFIG: false 10 | VCS_URL: https://github.com/%APPVEYOR_REPO_NAME% 11 | BT_KEY: 12 | secure: P9xGXGG38T4AvO8XXXXcbaDG2sSN4t6KH92zssGp0nrbyQAZuxHD9F84s6PM9mOD 13 | GH_TOKEN: 14 | secure: WVMaMjrLzXN8YNcnFRfcucTYWtvoDeE/4b2TUGQBZDvv7u+ERBQ///z5Q8qYSt0L 15 | 16 | install: 17 | - choco source add -n=mh-cbon -s="https://api.bintray.com/nuget/mh-cbon/choco" 18 | - choco install changelog gh-api-cli -y 19 | - refreshenv 20 | - set GH_APP=%APPVEYOR_PROJECT_NAME% 21 | - set GH_USER=%APPVEYOR_ACCOUNT_NAME% 22 | - set VERSION=%APPVEYOR_REPO_TAG_NAME% 23 | - if "%x%"=="%VERSION%" set VERSION=1.0.2 24 | - set PATH=%WIX%\bin;%PATH% 25 | - set PATH=%GOPATH%\bin;%PATH% 26 | - curl -fsSk -o jfrog.exe -L "https://api.bintray.com/content/jfrog/jfrog-cli-go/$latest/jfrog-cli-windows-amd64/jfrog.exe?bt_package=jfrog-cli-windows-amd64" 27 | - go get -u github.com/mh-cbon/never-fail 28 | - go get -u github.com/Masterminds/glide 29 | - glide install 30 | 31 | before_test: 32 | # do a manual installation of go-msi from pulled source code 33 | - mkdir C:\go-msi\ 34 | - set GOARCH=amd64 35 | - go build -o C:\go-msi\go-msi.exe --ldflags "-X main.VERSION=%VERSION%" main.go 36 | - XCOPY /I /E templates C:\go-msi\templates 37 | # prepare tests packages 38 | - cd testing\\hello 39 | - glide install 40 | - cd .. 41 | - glide install 42 | - cd .. 43 | # run tests 44 | - go run testing\\main.go 45 | - cd && dir *msi 46 | 47 | # build msi artifacts 48 | build_script: 49 | # x386 50 | - set GOARCH=386 51 | - go build -o %GH_APP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 52 | - .\go-msi.exe make --msi %GH_APP%-%GOARCH%-%VERSION%.msi --version %VERSION% --arch %GOARCH% 53 | - cp %GH_APP%-%GOARCH%-%VERSION%.msi %GH_APP%-%GOARCH%.msi 54 | # nuget package is built only for the x86 arch. 55 | - .\go-msi.exe choco --path wix.json --version %VERSION% --input %GH_APP%-%GOARCH%-%VERSION%.msi --changelog-cmd "changelog ghrelease --version %VERSION%" 56 | # amd64 57 | - set GOARCH=amd64 58 | - go build -o %GH_APP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 59 | - .\go-msi.exe make --msi %GH_APP%-%GOARCH%-%VERSION%.msi --version %VERSION% --arch %GOARCH% 60 | - cp %GH_APP%-%GOARCH%-%VERSION%.msi %GH_APP%-%GOARCH%.msi 61 | 62 | deploy_script: 63 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/msi/%GH_APP% 64 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/choco/%GH_APP% 65 | - never-fail curl -X "DELETE" -u%GH_USER%:%BT_KEY% https://api.bintray.com/content/%GH_USER%/choco/%GH_APP%.%VERSION%.nupkg # workaround until --override=true is honored in next upload 66 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%.%VERSION%.nupkg %GH_USER%/choco/%GH_APP%/%VERSION% 67 | - set GOARCH=386 68 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%-%GOARCH%-%VERSION%.msi %GH_USER%/msi/%GH_APP%/%VERSION% 69 | - set GOARCH=amd64 70 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%-%GOARCH%-%VERSION%.msi %GH_USER%/msi/%GH_APP%/%VERSION% 71 | # next section is a workaround for https://github.com/appveyor/ci/issues/1752 72 | - never-fail gh-api-cli upload-release-asset -t %GH_TOKEN% -g "*-386.msi" -o %GH_USER% -r %GH_APP% --ver %VERSION% 73 | - never-fail gh-api-cli upload-release-asset -t %GH_TOKEN% -g "*-amd64.msi" -o %GH_USER% -r %GH_APP% --ver %VERSION% 74 | 75 | artifacts: 76 | - path: '*-386.msi' 77 | name: msi-x86 78 | - path: '*-amd64.msi' 79 | name: msi-x64 80 | 81 | deploy: 82 | - provider: GitHub 83 | artifact: msi-x86, msi-x64 84 | draft: false 85 | prerelease: false 86 | desription: "Release %APPVEYOR_REPO_TAG_NAME%" 87 | auth_token: 88 | secure: WVMaMjrLzXN8YNcnFRfcucTYWtvoDeE/4b2TUGQBZDvv7u+ERBQ///z5Q8qYSt0L 89 | on: 90 | appveyor_repo_tag: true # deploy on tag push only 91 | -------------------------------------------------------------------------------- /change.log: -------------------------------------------------------------------------------- 1 | 2 | 1.0.2 3 | 4 | * `go-msi set-guid`: close #22: add `--force,-f` flags. 5 | * `go-msi check-env`: display a report of your environment. 6 | * close #17 #27: improve error message when --msi flag is not provided in the command line 7 | * choco: packages now include `tools/VERIFICATION.txt` and `tools/LICENSE.txt` to the package. 8 | 9 | - mh-cbon 10 | - solvingJ 11 | 12 | -- mh-cbon ; Wed, 23 Aug 2017 18:29:58 +0200 13 | 14 | 15 | 16 | 1.0.1 17 | 18 | * closes #2 Add support for (un)install hooks (run with elevated privileges) 19 | * workaround #9 Add support for services setup 20 | * closes #10 Add support for e2e tests with CI support 21 | * demo: add support for service 22 | * chocolatey: uninstall script, changed msiexec /qr argument to /q 23 | * testing: improve e2e tests to support services 24 | * testing: update vagrant scripts to run tests 25 | * package: make use of emd 26 | * package: change bump script to sh version 27 | * uuid(minor): close #7 remove useless code to generate an uuid 28 | * wix(minor): close #8 update product template to propagate environment variable changes 29 | * choco(minor): close #5 updated uninstaller script 30 | 31 | - Alfonso Acosta 32 | - mh-cbon 33 | 34 | -- mh-cbon ; Tue, 07 Mar 2017 17:21:16 +0100 35 | 36 | 37 | 38 | 1.0.0 39 | 40 | * winters udpate, udpated documentation and lint 41 | * templates(minor): renamed Id and Guid properties, 42 | remove try-catch in chocoinstall, 43 | set CRLF instead OF LF eol 44 | * lint(break): renamed fields 45 | ChocoSpec.Id to ChocoSpec.ID 46 | Chocopec.ProjectUrl to Chocopec.ProjectURL 47 | ChocoSpec.LicenseUrl to ChocoSpec.LicenseURL 48 | ChocoSpec.IconUrl to ChocoSpec.IconURL 49 | WixFiles.Guid to WixFiles.GUID 50 | WixEnvList.Guid to WixEnvList.GUID 51 | WixShortcuts.Guid to WixShortcuts.GUID 52 | This change does not impact json file format. 53 | * choco(minor): on chocolatey reviewer request, geet ride of the try catch 54 | 55 | - mh-cbon 56 | 57 | -- mh-cbon ; Sun, 08 Jan 2017 19:11:46 +0100 58 | 59 | 60 | 61 | 0.0.39 62 | 63 | * appveyor: update choco push key 64 | * choco: add checksum support. Closes #1 65 | * choco: fix pack command invokation, it was colliding with cmake 66 | 67 | - mh-cbon 68 | 69 | -- mh-cbon ; Mon, 15 Aug 2016 18:30:01 +0200 70 | 71 | 72 | 73 | 0.0.38 74 | 75 | * choco: ensure tags always contains admin value to pass chocolatey validation 76 | 77 | - mh-cbon 78 | 79 | -- mh-cbon ; Fri, 29 Jul 2016 20:06:03 +0200 80 | 81 | 82 | 83 | 0.0.37 84 | 85 | * Fix chocolatey package generation: Tags should not contain 'chocolatey' as a tag. 86 | 87 | - mh-cbon 88 | 89 | -- mh-cbon ; Fri, 29 Jul 2016 19:09:04 +0200 90 | 91 | 92 | 93 | 0.0.36 94 | 95 | * travis: fix gh secure token 96 | 97 | - mh-cbon 98 | 99 | -- mh-cbon ; Fri, 29 Jul 2016 18:56:09 +0200 100 | 101 | 102 | 103 | 0.0.35 104 | 105 | * rpm: fix templates path inlusion 106 | * README: update install section 107 | 108 | - mh-cbon 109 | 110 | -- mh-cbon ; Fri, 29 Jul 2016 18:42:52 +0200 111 | 112 | 113 | 114 | 0.0.34 115 | 116 | * build: fix the msi file generation 117 | * appveyor: artifacts must be created in build_script section 118 | 119 | - mh-cbon 120 | 121 | -- mh-cbon ; Fri, 29 Jul 2016 18:33:07 +0200 122 | 123 | 124 | 125 | 0.0.33 126 | 127 | * cli: add choco command to generate chocolatey packages. 128 | * Demo: add choco commands 129 | * build: update build scripts 130 | 131 | - mh-cbon 132 | 133 | -- mh-cbon ; Fri, 29 Jul 2016 18:23:59 +0200 134 | 135 | 136 | 137 | 0.0.32 138 | 139 | * wix: fix minimum/maximum version value of UpgradeVersion field in the product template 140 | 141 | - mh-cbon 142 | 143 | -- mh-cbon ; Sat, 23 Jul 2016 15:41:56 +0200 144 | 145 | 146 | 147 | 0.0.31 148 | 149 | * wix: fix version format for Product element field. 150 | When version value contains prerelease/metadata, it is not acceptable 151 | for wix. A new field is added to the manifest VersionOk containing the version 152 | string without prerelease/metadata value. 153 | product.wxs template now uses this new VersionOk field 154 | instead of the original Version field. 155 | * glide: add semver dependency 156 | * README: install section 157 | 158 | - mh-cbon 159 | 160 | -- mh-cbon ; Sat, 23 Jul 2016 15:34:40 +0200 161 | 162 | 163 | 164 | 0.0.30 165 | 166 | * travis: template inclusion 167 | 168 | - mh-cbon 169 | 170 | -- mh-cbon ; Fri, 15 Jul 2016 21:06:08 +0200 171 | 172 | 173 | 174 | 0.0.29 175 | 176 | * travis: fix missing changelog setup into docker image 177 | 178 | - mh-cbon 179 | 180 | -- mh-cbon ; Fri, 15 Jul 2016 20:55:39 +0200 181 | 182 | 183 | 184 | 0.0.28 185 | 186 | * rpm: add missing docker support 187 | 188 | - mh-cbon 189 | 190 | -- mh-cbon ; Fri, 15 Jul 2016 20:45:51 +0200 191 | 192 | 193 | 194 | 0.0.27 195 | 196 | * rpm: add rpm support 197 | * debian: remove useless urgency var 198 | 199 | - mh-cbon 200 | 201 | -- mh-cbon ; Fri, 15 Jul 2016 20:40:44 +0200 202 | 203 | 204 | 205 | 0.0.26 206 | 207 | * travis: update deb installers 208 | 209 | - mh-cbon 210 | 211 | -- mh-cbon ; Fri, 15 Jul 2016 19:36:46 +0200 212 | 213 | 214 | 215 | 0.0.25 216 | 217 | * Demo: add a demo with recipe commands 218 | * Code: add comments 219 | * Wix: Add Shortcuts icon support 220 | * Manifest: add icon support for shotcuts, add comments 221 | * wix.json: env var does not need to be set system wide 222 | 223 | - mh-cbon 224 | 225 | -- mh-cbon ; Fri, 15 Jul 2016 19:15:54 +0200 226 | 227 | 228 | 229 | 0.0.24 230 | 231 | * travis: ensure changelog is installed 232 | * recipe: fix curl options and register go-msi PATH 233 | * appveyor: remove useless DIR command 234 | * appveyor: remove -v option to curl 235 | 236 | - mh-cbon 237 | 238 | -- mh-cbon ; Tue, 12 Jul 2016 00:20:14 +0200 239 | 240 | 241 | 242 | 0.0.23 243 | 244 | * pkg: add deb package support 245 | * env: set env as system wide 246 | * main: add option for non windows built 247 | * appveyor: fix cur options to follow location redirects 248 | * release: add changelog support to release script 249 | * changelog: add new changelog 250 | * manifest: omit json fields when empty 251 | * wix.json: remove useless version field 252 | * README: add install from source section 253 | * recipes: improve commands and typos 254 | 255 | - mh-cbon 256 | 257 | -- mh-cbon ; Mon, 11 Jul 2016 23:57:07 +0200 258 | 259 | 260 | 261 | 0.0.22 262 | 263 | * align arch arguments with GO standards 264 | * improve recipes commands and typos 265 | 266 | - mh-cbon 267 | 268 | -- mh-cbon ; Sun, 26 Jun 2016 12:14:49 +0200 269 | 270 | 271 | 272 | 0.0.21 273 | 274 | * go fmt 275 | * improve recipes commands and typos 276 | * align arch arguments with GO standards 277 | * update recipes 278 | 279 | - mh-cbon 280 | 281 | -- mh-cbon ; Sun, 26 Jun 2016 12:08:59 +0200 282 | 283 | 284 | 285 | 0.0.20 286 | 287 | * update appveyor recipe 288 | * tryfix for ldflags 289 | 290 | - mh-cbon 291 | 292 | -- mh-cbon ; Sun, 26 Jun 2016 11:26:55 +0200 293 | 294 | 295 | 296 | 0.0.19 297 | 298 | * go fmt 299 | * fix path lookpath in guuid make for windows 300 | * Set version to the build 301 | * README 302 | 303 | - mh-cbon 304 | 305 | -- mh-cbon ; Sun, 26 Jun 2016 10:31:12 +0200 306 | 307 | 308 | 309 | 0.0.18 310 | 311 | * go fmt 312 | * avoid variable shadowing 313 | * update recipes 314 | 315 | - mh-cbon 316 | 317 | -- mh-cbon ; Sun, 26 Jun 2016 00:13:53 +0200 318 | 319 | 320 | 321 | 0.0.17 322 | 323 | * go fmt 324 | 325 | - mh-cbon 326 | 327 | -- mh-cbon ; Sat, 25 Jun 2016 23:41:59 +0200 328 | 329 | 330 | 331 | 0.0.16 332 | 333 | * go fmt 334 | 335 | - mh-cbon 336 | 337 | -- mh-cbon ; Sat, 25 Jun 2016 23:40:56 +0200 338 | 339 | 340 | 341 | 0.0.15 342 | 343 | * go fmt 344 | * fix HOWTOs 345 | * fix bin path detection 346 | * updates 347 | 348 | - mh-cbon 349 | 350 | -- mh-cbon ; Sat, 25 Jun 2016 23:28:40 +0200 351 | 352 | 353 | 354 | 0.0.14 355 | 356 | * appveyor 357 | 358 | - mh-cbon 359 | 360 | -- mh-cbon ; Sat, 25 Jun 2016 22:05:11 +0200 361 | 362 | 363 | 364 | 0.0.13 365 | 366 | * appveyor 367 | 368 | - mh-cbon 369 | 370 | -- mh-cbon ; Sat, 25 Jun 2016 22:00:52 +0200 371 | 372 | 373 | 374 | 0.0.12 375 | 376 | * appveyor 377 | 378 | - mh-cbon 379 | 380 | -- mh-cbon ; Sat, 25 Jun 2016 21:59:01 +0200 381 | 382 | 383 | 384 | 0.0.11 385 | 386 | * appveyor 387 | 388 | - mh-cbon 389 | 390 | -- mh-cbon ; Sat, 25 Jun 2016 21:52:11 +0200 391 | 392 | 393 | 394 | 0.0.10 395 | 396 | * appveyor 397 | 398 | - mh-cbon 399 | 400 | -- mh-cbon ; Sat, 25 Jun 2016 21:44:23 +0200 401 | 402 | 403 | 404 | 0.0.9 405 | 406 | * appveyor 407 | 408 | - mh-cbon 409 | 410 | -- mh-cbon ; Sat, 25 Jun 2016 21:17:43 +0200 411 | 412 | 413 | 414 | 0.0.8 415 | 416 | * appveyor 417 | 418 | - mh-cbon 419 | 420 | -- mh-cbon ; Sat, 25 Jun 2016 21:09:35 +0200 421 | 422 | 423 | 424 | 0.0.7 425 | 426 | * appveyor 427 | 428 | - mh-cbon 429 | 430 | -- mh-cbon ; Sat, 25 Jun 2016 21:04:38 +0200 431 | 432 | 433 | 434 | 0.0.6 435 | 436 | * appveyor 437 | 438 | - mh-cbon 439 | 440 | -- mh-cbon ; Sat, 25 Jun 2016 20:56:26 +0200 441 | 442 | 443 | 444 | 0.0.5 445 | 446 | * appveyor 447 | 448 | - mh-cbon 449 | 450 | -- mh-cbon ; Sat, 25 Jun 2016 20:51:23 +0200 451 | 452 | 453 | 454 | 0.0.4 455 | 456 | * appveyor 457 | 458 | - mh-cbon 459 | 460 | -- mh-cbon ; Sat, 25 Jun 2016 20:43:13 +0200 461 | 462 | 463 | 464 | 0.0.3 465 | 466 | * appveyor 467 | * appveyor 468 | 469 | - mh-cbon 470 | 471 | -- mh-cbon ; Sat, 25 Jun 2016 20:21:46 +0200 472 | 473 | 474 | 475 | 0.0.2 476 | 477 | * appveyor 478 | 479 | - mh-cbon 480 | 481 | -- mh-cbon ; Sat, 25 Jun 2016 20:12:08 +0200 482 | 483 | 484 | 485 | 0.0.1 486 | 487 | * Initial release 488 | 489 | - mh-cbon 490 | 491 | -- mh-cbon ; Sat, 25 Jun 2016 19:55:56 +0200 492 | 493 | 494 | -------------------------------------------------------------------------------- /deb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-msi", 3 | "maintainer": "mh-cbon ", 4 | "description": "Generate msi packages", 5 | "changelog-cmd": "changelog debian --vars='{\"name\":\"!name!\"}'", 6 | "homepage": "http://github.com/mh-cbon/!name!", 7 | "files": [ 8 | { 9 | "from": "build/!arch!/!name!", 10 | "to": "/usr/bin", 11 | "base" : "build/!arch!/", 12 | "fperm": "0755" 13 | }, 14 | { 15 | "from": "templates", 16 | "to": "/usr/share/!name!/" 17 | } 18 | ], 19 | "copyrights": [ 20 | { 21 | "files": "*", 22 | "copyright": "2016 mh-cbon ", 23 | "license": "MIT", 24 | "file": "LICENSE" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: ebc371608c61721e2c6b4a1728fcbe972ba7b103a4405104a78ea5c13c04a347 2 | updated: 2017-02-18T16:39:56.234882986+01:00 3 | imports: 4 | - name: github.com/Masterminds/semver 5 | version: 59c29afe1a994eacb71c833025ca7acf874bb1da 6 | - name: github.com/mattn/go-zglob 7 | version: 95345c4e1c0ebc9d16a3284177f09360f4d20fab 8 | subpackages: 9 | - fastwalk 10 | - name: github.com/mh-cbon/stringexec 11 | version: bc348d279f975052c5169eedf4efb1bc0469453f 12 | - name: github.com/satori/go.uuid 13 | version: 879c5887cd475cd7864858769793b2ceb0d44feb 14 | - name: github.com/urfave/cli 15 | version: 0bdeddeeb0f650497d603c4ad7b20cfe685682f6 16 | - name: golang.org/x/text 17 | version: 85c29909967d7f171f821e7a42e7b7af76fb9598 18 | subpackages: 19 | - encoding 20 | - encoding/charmap 21 | - encoding/internal 22 | - encoding/internal/identifier 23 | - runes 24 | - transform 25 | testImports: [] 26 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/mh-cbon/go-msi 2 | import: 3 | - package: github.com/urfave/cli 4 | version: ^1.17.0 5 | - package: golang.org/x/text 6 | - package: github.com/mattn/go-zglob 7 | - package: github.com/Masterminds/semver 8 | version: ^1.1.1 9 | - package: github.com/mh-cbon/stringexec 10 | version: ~0.0.1 11 | - package: github.com/satori/go.uuid 12 | version: ^1.1.0 13 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Package go-msi helps to generate msi package for a Go project. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "regexp" 11 | "strings" 12 | 13 | "github.com/Masterminds/semver" 14 | "github.com/mh-cbon/go-msi/manifest" 15 | "github.com/mh-cbon/go-msi/rtf" 16 | "github.com/mh-cbon/go-msi/tpls" 17 | "github.com/mh-cbon/go-msi/util" 18 | "github.com/mh-cbon/go-msi/wix" 19 | "github.com/mh-cbon/stringexec" 20 | "github.com/urfave/cli" 21 | ) 22 | 23 | // VERSION holds the program version. 24 | var VERSION = "0.0.0" 25 | 26 | // TPLPATH points to the template directory on the target system. 27 | // Should be used only for non windows systems to indicate template locations. 28 | var TPLPATH = "" // non-windows build, use ldflags to tell about that. 29 | 30 | func main() { 31 | 32 | if TPLPATH == "" { // built for windows 33 | b, err := util.GetBinPath() 34 | if err != nil { 35 | panic(err) 36 | } 37 | TPLPATH = b 38 | } 39 | tmpBuildDir, err := ioutil.TempDir("", "go-msi") 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | app := cli.NewApp() 45 | app.Name = "go-msi" 46 | app.Version = VERSION 47 | app.Usage = "Easy msi pakage for Go" 48 | app.UsageText = "go-msi " 49 | app.Commands = []cli.Command{ 50 | { 51 | Name: "check-json", 52 | Usage: "Check the JSON wix manifest", 53 | Action: checkJSON, 54 | Flags: []cli.Flag{ 55 | cli.StringFlag{ 56 | Name: "path, p", 57 | Value: "wix.json", 58 | Usage: "Path to the wix manifest file", 59 | }, 60 | }, 61 | }, 62 | { 63 | Name: "check-env", 64 | Usage: "Provide a report about your environment setup", 65 | Action: checkEnv, 66 | }, 67 | { 68 | Name: "set-guid", 69 | Usage: "Sets appropriate guids in your wix manifest", 70 | Action: setGUID, 71 | Flags: []cli.Flag{ 72 | cli.StringFlag{ 73 | Name: "path, p", 74 | Value: "wix.json", 75 | Usage: "Path to the wix manifest file", 76 | }, 77 | cli.BoolFlag{ 78 | Name: "force, f", 79 | Usage: "Force update the guids", 80 | }, 81 | }, 82 | }, 83 | { 84 | Name: "generate-templates", 85 | Usage: "Generate wix templates", 86 | Action: generateTemplates, 87 | Flags: []cli.Flag{ 88 | cli.StringFlag{ 89 | Name: "path, p", 90 | Value: "wix.json", 91 | Usage: "Path to the wix manifest file", 92 | }, 93 | cli.StringFlag{ 94 | Name: "src, s", 95 | Value: filepath.Join(TPLPATH, "templates"), 96 | Usage: "Directory path to the wix templates files", 97 | }, 98 | cli.StringFlag{ 99 | Name: "out, o", 100 | Value: tmpBuildDir, 101 | Usage: "Directory path to the generated wix templates files", 102 | }, 103 | cli.StringFlag{ 104 | Name: "version", 105 | Value: "", 106 | Usage: "The version of your program", 107 | }, 108 | cli.StringFlag{ 109 | Name: "license, l", 110 | Value: "", 111 | Usage: "Path to the license file", 112 | }, 113 | }, 114 | }, 115 | { 116 | Name: "to-windows", 117 | Usage: "Write Windows1252 encoded file", 118 | Action: toWindows1252, 119 | Flags: []cli.Flag{ 120 | cli.StringFlag{ 121 | Name: "src, s", 122 | Value: "", 123 | Usage: "Path to an UTF-8 encoded file", 124 | }, 125 | cli.StringFlag{ 126 | Name: "out, o", 127 | Value: "", 128 | Usage: "Path to the ANSI generated file", 129 | }, 130 | }, 131 | }, 132 | { 133 | Name: "to-rtf", 134 | Usage: "Write RTF formatted file", 135 | Action: toRtf, 136 | Flags: []cli.Flag{ 137 | cli.StringFlag{ 138 | Name: "src, s", 139 | Value: "", 140 | Usage: "Path to a text file", 141 | }, 142 | cli.StringFlag{ 143 | Name: "out, o", 144 | Value: "", 145 | Usage: "Path to the RTF generated file", 146 | }, 147 | cli.BoolFlag{ 148 | Name: "reencode, e", 149 | Usage: "Also re encode UTF-8 to Windows1252 charset", 150 | }, 151 | }, 152 | }, 153 | { 154 | Name: "gen-wix-cmd", 155 | Usage: "Generate a batch file of Wix commands to run", 156 | Action: generateWixCommands, 157 | Flags: []cli.Flag{ 158 | cli.StringFlag{ 159 | Name: "path, p", 160 | Value: "wix.json", 161 | Usage: "Path to the wix manifest file", 162 | }, 163 | cli.StringFlag{ 164 | Name: "src, s", 165 | Value: filepath.Join(TPLPATH, "templates"), 166 | Usage: "Directory path to the wix templates files", 167 | }, 168 | cli.StringFlag{ 169 | Name: "out, o", 170 | Value: tmpBuildDir, 171 | Usage: "Directory path to the generated wix cmd file", 172 | }, 173 | cli.StringFlag{ 174 | Name: "arch, a", 175 | Value: "", 176 | Usage: "A target architecture, amd64 or 386 (ia64 is not handled)", 177 | }, 178 | cli.StringFlag{ 179 | Name: "msi, m", 180 | Value: "", 181 | Usage: "Path to write resulting msi file to", 182 | }, 183 | }, 184 | }, 185 | { 186 | Name: "run-wix-cmd", 187 | Usage: "Run the batch file of Wix commands", 188 | Action: runWixCommands, 189 | Flags: []cli.Flag{ 190 | cli.StringFlag{ 191 | Name: "out, o", 192 | Value: tmpBuildDir, 193 | Usage: "Directory path to the generated wix cmd file", 194 | }, 195 | }, 196 | }, 197 | { 198 | Name: "make", 199 | Usage: "All-in-one command to make MSI files", 200 | Action: quickMake, 201 | Flags: []cli.Flag{ 202 | cli.StringFlag{ 203 | Name: "path, p", 204 | Value: "wix.json", 205 | Usage: "Path to the wix manifest file", 206 | }, 207 | cli.StringFlag{ 208 | Name: "src, s", 209 | Value: filepath.Join(TPLPATH, "templates"), 210 | Usage: "Directory path to the wix templates files", 211 | }, 212 | cli.StringFlag{ 213 | Name: "out, o", 214 | Value: tmpBuildDir, 215 | Usage: "Directory path to the generated wix cmd file", 216 | }, 217 | cli.StringFlag{ 218 | Name: "arch, a", 219 | Value: "", 220 | Usage: "A target architecture, amd64 or 386 (ia64 is not handled)", 221 | }, 222 | cli.StringFlag{ 223 | Name: "msi, m", 224 | Value: "", 225 | Usage: "Path to write resulting msi file to", 226 | }, 227 | cli.StringFlag{ 228 | Name: "version", 229 | Value: "", 230 | Usage: "The version of your program", 231 | }, 232 | cli.StringFlag{ 233 | Name: "license, l", 234 | Value: "", 235 | Usage: "Path to the license file", 236 | }, 237 | cli.BoolFlag{ 238 | Name: "keep, k", 239 | Usage: "Keep output directory containing build files (useful for debug)", 240 | }, 241 | }, 242 | }, 243 | { 244 | Name: "choco", 245 | Usage: "Generate a chocolatey package of your msi files", 246 | Action: chocoMake, 247 | Flags: []cli.Flag{ 248 | cli.StringFlag{ 249 | Name: "path, p", 250 | Value: "wix.json", 251 | Usage: "Path to the wix manifest file", 252 | }, 253 | cli.StringFlag{ 254 | Name: "src, s", 255 | Value: filepath.Join(TPLPATH, "templates", "choco"), 256 | Usage: "Directory path to the wix templates files", 257 | }, 258 | cli.StringFlag{ 259 | Name: "version", 260 | Value: "", 261 | Usage: "The version of your program", 262 | }, 263 | cli.StringFlag{ 264 | Name: "out, o", 265 | Value: tmpBuildDir, 266 | Usage: "Directory path to the generated chocolatey build file", 267 | }, 268 | cli.StringFlag{ 269 | Name: "input, i", 270 | Value: "", 271 | Usage: "Path to the msi file to package into the chocolatey package", 272 | }, 273 | cli.StringFlag{ 274 | Name: "changelog-cmd, c", 275 | Value: "", 276 | Usage: "A command to generate the content of the changlog in the package", 277 | }, 278 | cli.BoolFlag{ 279 | Name: "keep, k", 280 | Usage: "Keep output directory containing build files (useful for debug)", 281 | }, 282 | }, 283 | }, 284 | } 285 | 286 | app.Run(os.Args) 287 | } 288 | 289 | var verReg = regexp.MustCompile(`\s[0-9]+[.][0-9]+[.][0-9]+`) 290 | 291 | func checkEnv(c *cli.Context) error { 292 | 293 | for _, b := range []string{"heat", "light", "candle"} { 294 | if out, err := util.Exec(b, "-h"); out == "" { 295 | fmt.Printf("!! %v not found: %q\n", b, err) 296 | } else { 297 | match := verReg.FindAllString(out, -1) 298 | if len(match) < 1 { 299 | fmt.Printf("?? %v probably not found\n", b) 300 | } else { 301 | version := strings.TrimSpace(match[0]) 302 | ver, err := semver.NewVersion(version) 303 | if err != nil { 304 | fmt.Printf("?? %v found but its version is not parsable %v\n", b, version) 305 | } else { 306 | min := "3.10.0" 307 | if !ver.GreaterThan(semver.MustParse(min)) { 308 | fmt.Printf("!! %v found %v but %v is required\n", b, version, min) 309 | } else { 310 | fmt.Printf("ok %v found %v\n", b, version) 311 | } 312 | } 313 | } 314 | } 315 | } 316 | if out, err := util.Exec("choco", "-v"); out == "" { 317 | fmt.Printf("!! %v not found: %q\n", "chocolatey", err) 318 | } else { 319 | match := verReg.FindAllString(" "+out, -1) 320 | if len(match) < 1 { 321 | fmt.Printf("?? %v probably not found\n", "chocolatey") 322 | } else { 323 | version := strings.TrimSpace(match[0]) 324 | ver, err := semver.NewVersion(version) 325 | if err != nil { 326 | fmt.Printf("?? %v found but its version is not parsable %v\n", "chocolatey", version) 327 | } else { 328 | min := "0.10.0" 329 | if !ver.GreaterThan(semver.MustParse(min)) { 330 | fmt.Printf("!! %v found %v but >%v is required\n", "chocolatey", version, min) 331 | } else { 332 | fmt.Printf("ok %v found %v\n", "chocolatey", version) 333 | } 334 | } 335 | } 336 | } 337 | return nil 338 | } 339 | 340 | func checkJSON(c *cli.Context) error { 341 | path := c.String("path") 342 | 343 | wixFile := manifest.WixManifest{} 344 | err := wixFile.Load(path) 345 | if err != nil { 346 | return cli.NewExitError(err.Error(), 1) 347 | } 348 | 349 | for _, hook := range wixFile.Hooks { 350 | if _, ok := manifest.HookPhases[hook.When]; !ok { 351 | return cli.NewExitError(`Invalid "when" value in hook: `+hook.When, 1) 352 | } 353 | } 354 | 355 | fmt.Println("The manifest is syntaxically correct !") 356 | 357 | if wixFile.NeedGUID() { 358 | fmt.Println("The manifest needs Guid") 359 | fmt.Println("To update your file automatically run:") 360 | fmt.Println(" go-msi set-guid") 361 | return cli.NewExitError("Incomplete manifest file detected", 1) 362 | } 363 | return nil 364 | } 365 | 366 | func setGUID(c *cli.Context) error { 367 | path := c.String("path") 368 | force := c.Bool("force") 369 | 370 | wixFile := manifest.WixManifest{} 371 | err := wixFile.Load(path) 372 | if err != nil { 373 | return cli.NewExitError(err.Error(), 1) 374 | } 375 | 376 | updated, err := wixFile.SetGuids(force) 377 | if err != nil { 378 | return cli.NewExitError(err.Error(), 1) 379 | } 380 | 381 | if updated { 382 | fmt.Println("The manifest was updated") 383 | } else { 384 | fmt.Println("The manifest was not updated") 385 | } 386 | 387 | err = wixFile.Write(path) 388 | if err != nil { 389 | return cli.NewExitError(err.Error(), 1) 390 | } 391 | fmt.Println("The file is saved on disk") 392 | 393 | return nil 394 | } 395 | 396 | func generateTemplates(c *cli.Context) error { 397 | path := c.String("path") 398 | src := c.String("src") 399 | out := c.String("out") 400 | version := c.String("version") 401 | license := c.String("license") 402 | 403 | wixFile := manifest.WixManifest{} 404 | err := wixFile.Load(path) 405 | if err != nil { 406 | return cli.NewExitError(err.Error(), 1) 407 | } 408 | 409 | if wixFile.NeedGUID() { 410 | fmt.Println("The manifest needs Guid") 411 | fmt.Println("To update your file automatically run:") 412 | fmt.Println(" go-msi set-guid") 413 | return cli.NewExitError("Cannot proceed, manifest file is incomplete", 1) 414 | } 415 | 416 | if c.IsSet("version") { 417 | wixFile.Version = version 418 | } 419 | 420 | if c.IsSet("license") { 421 | wixFile.License = license 422 | } 423 | 424 | err = wixFile.Normalize() 425 | if err != nil { 426 | return cli.NewExitError(err.Error(), 1) 427 | } 428 | 429 | err = wixFile.RewriteFilePaths(out) 430 | if err != nil { 431 | return cli.NewExitError(err.Error(), 1) 432 | } 433 | 434 | templates, err := tpls.Find(src, "*.wxs") 435 | if err != nil { 436 | return cli.NewExitError(err.Error(), 1) 437 | } 438 | if len(templates) == 0 { 439 | return cli.NewExitError("No templates *.wxs found in this directory", 1) 440 | } 441 | 442 | err = os.MkdirAll(out, 0744) 443 | if err != nil { 444 | return cli.NewExitError(err.Error(), 1) 445 | } 446 | 447 | for _, tpl := range templates { 448 | dst := filepath.Join(out, filepath.Base(tpl)) 449 | err = tpls.GenerateTemplate(&wixFile, tpl, dst) 450 | if err != nil { 451 | return cli.NewExitError(err.Error(), 1) 452 | } 453 | } 454 | 455 | fmt.Printf("Generated %d templates\n", len(templates)) 456 | for _, tpl := range templates { 457 | dst := filepath.Join(out, filepath.Base(tpl)) 458 | fmt.Printf("- %s\n", dst) 459 | } 460 | 461 | return nil 462 | } 463 | 464 | func toWindows1252(c *cli.Context) error { 465 | src := c.String("src") 466 | out := c.String("out") 467 | 468 | if src == "" { 469 | return cli.NewExitError("--src argument is required", 1) 470 | } 471 | if out == "" { 472 | return cli.NewExitError("--out argument is required", 1) 473 | } 474 | if _, err := os.Stat(src); os.IsNotExist(err) { 475 | return cli.NewExitError(err.Error(), 1) 476 | } 477 | os.MkdirAll(filepath.Dir(out), 0744) 478 | err := rtf.WriteAsWindows1252(src, out) 479 | if err != nil { 480 | return cli.NewExitError(err.Error(), 1) 481 | } 482 | return nil 483 | } 484 | 485 | func toRtf(c *cli.Context) error { 486 | src := c.String("src") 487 | out := c.String("out") 488 | reencode := c.Bool("reencode") 489 | 490 | if src == "" { 491 | return cli.NewExitError("--src argument is required", 1) 492 | } 493 | if out == "" { 494 | return cli.NewExitError("--out argument is required", 1) 495 | } 496 | if _, err := os.Stat(src); os.IsNotExist(err) { 497 | return cli.NewExitError(err.Error(), 1) 498 | } 499 | 500 | os.MkdirAll(filepath.Dir(out), 0744) 501 | 502 | err := rtf.WriteAsRtf(src, out, reencode) 503 | if err != nil { 504 | return cli.NewExitError(err.Error(), 1) 505 | } 506 | 507 | return nil 508 | } 509 | 510 | func generateWixCommands(c *cli.Context) error { 511 | path := c.String("path") 512 | src := c.String("src") 513 | out := c.String("out") 514 | msi := c.String("msi") 515 | arch := c.String("arch") 516 | 517 | if msi == "" { 518 | return cli.NewExitError("--msi parameter must be set", 1) 519 | } 520 | 521 | templates, err := tpls.Find(src, "*.wxs") 522 | if err != nil { 523 | return cli.NewExitError(err.Error(), 1) 524 | } 525 | if len(templates) == 0 { 526 | return cli.NewExitError("No templates *.wxs found in this directory", 1) 527 | } 528 | 529 | builtTemplates := make([]string, len(templates)) 530 | for i, tpl := range templates { 531 | builtTemplates[i] = filepath.Join(out, filepath.Base(tpl)) 532 | } 533 | 534 | wixFile := manifest.WixManifest{} 535 | err = wixFile.Load(path) 536 | if err != nil { 537 | return cli.NewExitError(err.Error(), 1) 538 | } 539 | 540 | if wixFile.NeedGUID() { 541 | fmt.Println("The manifest needs Guid") 542 | fmt.Println("To update your file automatically run:") 543 | fmt.Println(" go-msi set-guid") 544 | return cli.NewExitError("Cannot proceed, manifest file is incomplete", 1) 545 | } 546 | 547 | err = wixFile.Normalize() 548 | if err != nil { 549 | return cli.NewExitError(err.Error(), 1) 550 | } 551 | 552 | err = wixFile.RewriteFilePaths(out) 553 | if err != nil { 554 | return cli.NewExitError(err.Error(), 1) 555 | } 556 | 557 | msi, err = filepath.Abs(msi) 558 | if err != nil { 559 | return cli.NewExitError(err.Error(), 1) 560 | } 561 | msi, err = filepath.Rel(out, msi) 562 | if err != nil { 563 | return cli.NewExitError(err.Error(), 1) 564 | } 565 | 566 | cmdStr := wix.GenerateCmd(&wixFile, builtTemplates, msi, arch) 567 | 568 | targetFile := filepath.Join(out, "build.bat") 569 | err = ioutil.WriteFile(targetFile, []byte(cmdStr), 0644) 570 | if err != nil { 571 | return cli.NewExitError(err.Error(), 1) 572 | } 573 | 574 | return nil 575 | } 576 | 577 | func runWixCommands(c *cli.Context) error { 578 | out := c.String("out") 579 | 580 | bin, err := exec.LookPath("cmd.exe") 581 | if err != nil { 582 | return cli.NewExitError(err.Error(), 1) 583 | } 584 | args := []string{"/C", "build.bat"} 585 | oCmd := exec.Command(bin, args...) 586 | oCmd.Dir = out 587 | oCmd.Stdout = os.Stdout 588 | oCmd.Stderr = os.Stderr 589 | err = oCmd.Run() 590 | if err != nil { 591 | return cli.NewExitError(err.Error(), 1) 592 | } 593 | 594 | return nil 595 | } 596 | 597 | func quickMake(c *cli.Context) error { 598 | path := c.String("path") 599 | src := c.String("src") 600 | out := c.String("out") 601 | version := c.String("version") 602 | license := c.String("license") 603 | msi := c.String("msi") 604 | arch := c.String("arch") 605 | keep := c.Bool("keep") 606 | 607 | if msi == "" { 608 | return cli.NewExitError("--msi parameter must be set", 1) 609 | } 610 | 611 | wixFile := manifest.WixManifest{} 612 | if err := wixFile.Load(path); err != nil { 613 | return cli.NewExitError(err.Error(), 1) 614 | } 615 | 616 | if wixFile.NeedGUID() { 617 | if _, err := wixFile.SetGuids(false); err != nil { 618 | return cli.NewExitError(err.Error(), 1) 619 | } 620 | } 621 | 622 | // if err := os.RemoveAll(out); err != nil { 623 | // return cli.NewExitError(err.Error(), 1) 624 | // } 625 | if err := os.MkdirAll(out, 0744); err != nil { 626 | return cli.NewExitError(err.Error(), 1) 627 | } 628 | 629 | if c.IsSet("version") { 630 | wixFile.Version = version 631 | } 632 | 633 | if c.IsSet("license") { 634 | wixFile.License = license 635 | } 636 | 637 | if err := wixFile.Normalize(); err != nil { 638 | return cli.NewExitError(err.Error(), 1) 639 | } 640 | 641 | if err := wixFile.RewriteFilePaths(out); err != nil { 642 | return cli.NewExitError(err.Error(), 1) 643 | } 644 | 645 | if wixFile.License != "" { 646 | if !rtf.IsRtf(wixFile.License) { 647 | target := filepath.Join(out, filepath.Base(wixFile.License)+".rtf") 648 | err := rtf.WriteAsRtf(wixFile.License, target, true) 649 | if err != nil { 650 | return cli.NewExitError(err.Error(), 1) 651 | } 652 | wixFile.License, err = filepath.Rel(out, target) 653 | if err != nil { 654 | return cli.NewExitError(err.Error(), 1) 655 | } 656 | } 657 | } 658 | 659 | templates, err := tpls.Find(src, "*.wxs") 660 | if err != nil { 661 | return cli.NewExitError(err.Error(), 1) 662 | } 663 | if len(templates) == 0 { 664 | return cli.NewExitError("No templates *.wxs found in this directory", 1) 665 | } 666 | 667 | builtTemplates := make([]string, len(templates)) 668 | for i, tpl := range templates { 669 | dst := filepath.Join(out, filepath.Base(tpl)) 670 | err = tpls.GenerateTemplate(&wixFile, tpl, dst) 671 | builtTemplates[i] = dst 672 | if err != nil { 673 | return cli.NewExitError(err.Error(), 1) 674 | } 675 | } 676 | 677 | msi, err = filepath.Abs(msi) 678 | if err != nil { 679 | return cli.NewExitError(err.Error(), 1) 680 | } 681 | msi, err = filepath.Rel(out, msi) 682 | if err != nil { 683 | return cli.NewExitError(err.Error(), 1) 684 | } 685 | 686 | cmdStr := wix.GenerateCmd(&wixFile, builtTemplates, msi, arch) 687 | 688 | targetFile := filepath.Join(out, "build.bat") 689 | err = ioutil.WriteFile(targetFile, []byte(cmdStr), 0644) 690 | if err != nil { 691 | return cli.NewExitError(err.Error(), 1) 692 | } 693 | 694 | bin, err := exec.LookPath("cmd.exe") 695 | if err != nil { 696 | return cli.NewExitError(err.Error(), 1) 697 | } 698 | args := []string{"/C", "build.bat"} 699 | oCmd := exec.Command(bin, args...) 700 | oCmd.Dir = out 701 | oCmd.Stdout = os.Stdout 702 | oCmd.Stderr = os.Stderr 703 | err = oCmd.Run() 704 | if err != nil { 705 | return cli.NewExitError(err.Error(), 1) 706 | } 707 | 708 | if keep == false { 709 | // err = os.RemoveAll(out) 710 | // if err != nil { 711 | // return cli.NewExitError(err.Error(), 1) 712 | // } 713 | } else { 714 | fmt.Printf("Build files are available in %s\n", out) 715 | } 716 | 717 | fmt.Println("All Done!!") 718 | 719 | return nil 720 | } 721 | 722 | func chocoMake(c *cli.Context) error { 723 | path := c.String("path") 724 | src := c.String("src") 725 | out := c.String("out") 726 | input := c.String("input") 727 | version := c.String("version") 728 | changelogCmd := c.String("changelog-cmd") 729 | keep := c.Bool("keep") 730 | 731 | wixFile := manifest.WixManifest{} 732 | if err := wixFile.Load(path); err != nil { 733 | return cli.NewExitError(err.Error(), 1) 734 | } 735 | 736 | if err := os.RemoveAll(out); err != nil { 737 | return cli.NewExitError(err.Error(), 1) 738 | } 739 | if err := os.MkdirAll(out, 0744); err != nil { 740 | return cli.NewExitError(err.Error(), 1) 741 | } 742 | 743 | if c.IsSet("version") { 744 | wixFile.Version = version 745 | } 746 | 747 | if err := wixFile.Normalize(); err != nil { 748 | return cli.NewExitError(err.Error(), 1) 749 | } 750 | 751 | templates, err := tpls.Find(src, "*") 752 | if err != nil { 753 | return cli.NewExitError(err.Error(), 1) 754 | } 755 | if len(templates) == 0 { 756 | return cli.NewExitError("No templates found in this directory", 1) 757 | } 758 | 759 | out, err = filepath.Abs(out) 760 | if err != nil { 761 | return cli.NewExitError(err.Error(), 1) 762 | } 763 | wixFile.Choco.BuildDir = out 764 | wixFile.Choco.MsiFile = filepath.Base(input) 765 | wixFile.Choco.MsiSum, err = util.ComputeSha256(input) 766 | if err != nil { 767 | return cli.NewExitError(err.Error(), 1) 768 | } 769 | 770 | if changelogCmd != "" { 771 | windows, err2 := stringexec.Command(changelogCmd) 772 | if err2 != nil { 773 | return cli.NewExitError(err.Error(), 1) 774 | } 775 | windows.Stderr = os.Stderr 776 | out, err3 := windows.Output() 777 | if err3 != nil { 778 | return cli.NewExitError(fmt.Sprintf("Failed to execute command to generate the changelog:%q\n%v", changelogCmd, err.Error()), 1) 779 | } 780 | sout := string(out) 781 | souts := strings.Split(sout, "\n") 782 | if len(souts) > 2 { 783 | souts = souts[2:] // why ? command line artifacts ? todo: put an explanation here. 784 | } 785 | sout = strings.Join(souts, "\n") 786 | 787 | wixFile.Choco.ChangeLog = sout 788 | } 789 | 790 | if err = util.CopyFile(filepath.Join(wixFile.Choco.BuildDir, wixFile.Choco.MsiFile), input); err != nil { 791 | return cli.NewExitError(err.Error(), 1) 792 | } 793 | 794 | for _, tpl := range templates { 795 | dst := filepath.Join(out, filepath.Base(tpl)) 796 | err = tpls.GenerateTemplate(&wixFile, tpl, dst) 797 | if err != nil { 798 | return cli.NewExitError(err.Error(), 1) 799 | } 800 | } 801 | 802 | bin, err := exec.LookPath("choco") 803 | if err != nil { 804 | return cli.NewExitError(err.Error(), 1) 805 | } 806 | oCmd := exec.Command(bin, "pack") 807 | oCmd.Dir = out 808 | oCmd.Stdout = os.Stdout 809 | oCmd.Stderr = os.Stderr 810 | err = oCmd.Run() 811 | if err != nil { 812 | return cli.NewExitError(err.Error(), 1) 813 | } 814 | 815 | SrcNupkg := fmt.Sprintf("%s\\%s.%s.nupkg", out, wixFile.Choco.ID, wixFile.VersionOk) 816 | DstNupkg := fmt.Sprintf("%s.%s.nupkg", wixFile.Choco.ID, wixFile.Version) 817 | 818 | if err = util.CopyFile(DstNupkg, SrcNupkg); err != nil { 819 | return cli.NewExitError(err.Error(), 1) 820 | } 821 | 822 | if keep == false { 823 | err = os.RemoveAll(out) 824 | if err != nil { 825 | return cli.NewExitError(err.Error(), 1) 826 | } 827 | } else { 828 | fmt.Printf("Build files are available in %s\n", out) 829 | } 830 | 831 | fmt.Printf("Package copied to %s\n", DstNupkg) 832 | fmt.Println("All Done!!") 833 | 834 | return nil 835 | } 836 | -------------------------------------------------------------------------------- /manifest/index.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "encoding/xml" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "strconv" 12 | "strings" 13 | 14 | "github.com/Masterminds/semver" 15 | "github.com/satori/go.uuid" 16 | ) 17 | 18 | // WixManifest is the struct to decode a wix.json file. 19 | type WixManifest struct { 20 | Product string `json:"product"` 21 | Company string `json:"company"` 22 | Version string `json:"version,omitempty"` 23 | VersionOk string `json:"-"` 24 | License string `json:"license,omitempty"` 25 | UpgradeCode string `json:"upgrade-code"` 26 | Files WixFiles `json:"files,omitempty"` 27 | Directories []string `json:"directories,omitempty"` 28 | RelDirs []string `json:"-"` 29 | Env WixEnvList `json:"env,omitempty"` 30 | Shortcuts WixShortcuts `json:"shortcuts,omitempty"` 31 | Choco ChocoSpec `json:"choco,omitempty"` 32 | Hooks []Hook `json:"hooks,omitempty"` 33 | InstallHooks []Hook `json:"-"` 34 | UninstallHooks []Hook `json:"-"` 35 | } 36 | 37 | // ChocoSpec is the struct to decode the choco key of a wix.json file. 38 | type ChocoSpec struct { 39 | ID string `json:"id,omitempty"` 40 | Title string `json:"title,omitempty"` 41 | Authors string `json:"authors,omitempty"` 42 | Owners string `json:"owners,omitempty"` 43 | Description string `json:"description,omitempty"` 44 | ProjectURL string `json:"project-url,omitempty"` 45 | Tags string `json:"tags,omitempty"` 46 | LicenseURL string `json:"license-url,omitempty"` 47 | IconURL string `json:"icon-url,omitempty"` 48 | RequireLicense bool `json:"require-license,omitempty"` 49 | MsiFile string `json:"-"` 50 | MsiSum string `json:"-"` 51 | BuildDir string `json:"-"` 52 | ChangeLog string `json:"-"` 53 | } 54 | 55 | const ( 56 | whenInstall = "install" 57 | whenUninstall = "uninstall" 58 | ) 59 | 60 | // HookPhases describes known hook phases. 61 | var HookPhases = map[string]bool{ 62 | whenInstall: true, 63 | whenUninstall: true, 64 | } 65 | 66 | // Hook describes a command to run on install / uninstall. 67 | type Hook struct { 68 | Command string `json:"command,omitempty"` 69 | CookedCommand string `json:"-"` 70 | When string `json:"when,omitempty"` 71 | } 72 | 73 | // WixFiles is the struct to decode files key of the wix.json file. 74 | type WixFiles struct { 75 | GUID string `json:"guid"` 76 | Items []string `json:"items"` 77 | } 78 | 79 | // WixEnvList is the struct to decode env key of the wix.json file. 80 | type WixEnvList struct { 81 | GUID string `json:"guid"` 82 | Vars []WixEnv `json:"vars"` 83 | } 84 | 85 | // WixEnv is the struct to decode env value of the wix.json file. 86 | type WixEnv struct { 87 | Name string `json:"name"` 88 | Value string `json:"value"` 89 | Permanent string `json:"permanent"` 90 | System string `json:"system"` 91 | Action string `json:"action"` 92 | Part string `json:"part"` 93 | } 94 | 95 | // WixShortcuts is the struct to decode shortcuts key of the wix.json file. 96 | type WixShortcuts struct { 97 | GUID string `json:"guid,omitempty"` 98 | Items []WixShortcut `json:"items,omitempty"` 99 | } 100 | 101 | // WixShortcut is the struct to decode shortcut value of the wix.json file. 102 | type WixShortcut struct { 103 | Name string `json:"name"` 104 | Description string `json:"description"` 105 | Target string `json:"target"` 106 | WDir string `json:"wdir"` 107 | Arguments string `json:"arguments"` 108 | Icon string `json:"icon"` // a path to the ico file, no space in it. 109 | } 110 | 111 | // Write the manifest to the given file, 112 | // if file is empty, writes to wix.json 113 | func (wixFile *WixManifest) Write(p string) error { 114 | if p == "" { 115 | p = "wix.json" 116 | } 117 | byt, err := json.MarshalIndent(wixFile, "", " ") 118 | if err != nil { 119 | return err 120 | } 121 | err = ioutil.WriteFile(p, byt, 0644) 122 | if err != nil { 123 | return err 124 | } 125 | return nil 126 | } 127 | 128 | // Load the manifest from given file path, 129 | // if the file path is empty, reads from wix.json 130 | func (wixFile *WixManifest) Load(p string) error { 131 | if p == "" { 132 | p = "wix.json" 133 | } 134 | if _, err := os.Stat(p); os.IsNotExist(err) { 135 | return err 136 | } 137 | dat, err := ioutil.ReadFile(p) 138 | if err != nil { 139 | return fmt.Errorf("JSON ReadFile failed with %v", err) 140 | } 141 | err = json.Unmarshal(dat, &wixFile) 142 | if err != nil { 143 | return fmt.Errorf("JSON Unmarshal failed with %v", err) 144 | } 145 | return nil 146 | } 147 | 148 | //SetGuids generates and apply guid values appropriately 149 | func (wixFile *WixManifest) SetGuids(force bool) (bool, error) { 150 | updated := false 151 | if wixFile.UpgradeCode == "" || force { 152 | wixFile.UpgradeCode = uuid.NewV4().String() 153 | updated = true 154 | } 155 | if wixFile.Files.GUID == "" || force { 156 | wixFile.Files.GUID = uuid.NewV4().String() 157 | updated = true 158 | } 159 | if (wixFile.Env.GUID == "" || force) && len(wixFile.Env.Vars) > 0 { 160 | wixFile.Env.GUID = uuid.NewV4().String() 161 | updated = true 162 | } 163 | if (wixFile.Shortcuts.GUID == "" || force) && len(wixFile.Shortcuts.Items) > 0 { 164 | wixFile.Shortcuts.GUID = uuid.NewV4().String() 165 | updated = true 166 | } 167 | return updated, nil 168 | } 169 | 170 | // NeedGUID tells if the manifest json file is missing guid values. 171 | func (wixFile *WixManifest) NeedGUID() bool { 172 | need := false 173 | if wixFile.UpgradeCode == "" { 174 | need = true 175 | } 176 | if wixFile.Files.GUID == "" { 177 | need = true 178 | } 179 | if wixFile.Env.GUID == "" && len(wixFile.Env.Vars) > 0 { 180 | need = true 181 | } 182 | if wixFile.Shortcuts.GUID == "" && len(wixFile.Shortcuts.Items) > 0 { 183 | need = true 184 | } 185 | return need 186 | } 187 | 188 | // RewriteFilePaths Reads Files and Directories of the wix.json file 189 | // and turn their values into a relative path to out 190 | // where out is the path to the wix templates files. 191 | func (wixFile *WixManifest) RewriteFilePaths(out string) error { 192 | var err error 193 | out, err = filepath.Abs(out) 194 | if err != nil { 195 | return err 196 | } 197 | for i, file := range wixFile.Files.Items { 198 | file, err = filepath.Abs(file) 199 | if err != nil { 200 | return err 201 | } 202 | wixFile.Files.Items[i], err = filepath.Rel(out, file) 203 | if err != nil { 204 | return err 205 | } 206 | } 207 | for _, d := range wixFile.Directories { 208 | d, err = filepath.Abs(d) 209 | if err != nil { 210 | return err 211 | } 212 | r, err := filepath.Rel(out, d) 213 | if err != nil { 214 | return err 215 | } 216 | wixFile.RelDirs = append(wixFile.RelDirs, r) 217 | } 218 | for i, s := range wixFile.Shortcuts.Items { 219 | if s.Icon != "" { 220 | file, err := filepath.Abs(s.Icon) 221 | if err != nil { 222 | return err 223 | } 224 | wixFile.Shortcuts.Items[i].Icon, err = filepath.Rel(out, file) 225 | if err != nil { 226 | return err 227 | } 228 | } 229 | } 230 | return nil 231 | } 232 | 233 | // Normalize Appropriately fixes some values within the decoded json 234 | // It applies defaults values on the wix/msi property to 235 | // to generate the msi package. 236 | // It applies defaults values on the choco property to 237 | // generate a nuget package 238 | func (wixFile *WixManifest) Normalize() error { 239 | // Wix version Field of Product element 240 | // does not support semver strings 241 | // it supports only something like x.x.x.x 242 | // So, if the version has metadata/prerelease values, 243 | // lets get ride of those and save the workable version 244 | // into VersionOk field 245 | wixFile.VersionOk = wixFile.Version 246 | v, err := semver.NewVersion(wixFile.Version) 247 | if err != nil { 248 | return fmt.Errorf("Failed to parse version '%v': %v", wixFile.Version, err) 249 | } 250 | okVersion := "" 251 | okVersion += strconv.FormatInt(v.Major(), 10) 252 | okVersion += "." + strconv.FormatInt(v.Minor(), 10) 253 | okVersion += "." + strconv.FormatInt(v.Patch(), 10) 254 | wixFile.VersionOk = okVersion 255 | 256 | // choco fix 257 | if wixFile.Choco.ID == "" { 258 | wixFile.Choco.ID = wixFile.Product 259 | } 260 | if wixFile.Choco.Title == "" { 261 | wixFile.Choco.Title = wixFile.Product 262 | } 263 | if wixFile.Choco.Authors == "" { 264 | wixFile.Choco.Authors = wixFile.Company 265 | } 266 | if wixFile.Choco.Owners == "" { 267 | wixFile.Choco.Owners = wixFile.Company 268 | } 269 | if wixFile.Choco.Description == "" { 270 | wixFile.Choco.Description = wixFile.Product 271 | } 272 | wixFile.Choco.Tags += " admin" // required to pass chocolatey validation.. 273 | 274 | // Escape hook commands and ensure the command name is enclosed in quotes (needed by wix) 275 | for i, hook := range wixFile.Hooks { 276 | cmd := strings.Trim(hook.Command, " ") 277 | if len(cmd) > 0 && cmd[0] != '"' { 278 | words := strings.Split(cmd, " ") 279 | cmd = `"` + words[0] + `"` + cmd[len(words[0]):] 280 | } 281 | buf := &bytes.Buffer{} 282 | if err := xml.EscapeText(buf, []byte(cmd)); err != nil { 283 | return err 284 | } 285 | wixFile.Hooks[i].CookedCommand = buf.String() 286 | } 287 | 288 | // Separate install and uninstall hooks to simplify templating 289 | for _, hook := range wixFile.Hooks { 290 | switch hook.When { 291 | case whenInstall: 292 | wixFile.InstallHooks = append(wixFile.InstallHooks, hook) 293 | case whenUninstall: 294 | wixFile.UninstallHooks = append(wixFile.UninstallHooks, hook) 295 | } 296 | } 297 | 298 | return nil 299 | } 300 | -------------------------------------------------------------------------------- /rpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-msi", 3 | "summary": "Generate msi packages", 4 | "description": "Generate msi packages", 5 | "changelog-cmd": "changelog rpm", 6 | "license": "LICENSE", 7 | "url": "https://github.com/mh-cbon/!name!", 8 | "files": [ 9 | { 10 | "from": "build/!arch!/go-msi", 11 | "to": "%{_bindir}/", 12 | "base": "build/!arch!/", 13 | "type": "" 14 | }, 15 | { 16 | "from": "templates/*/*", 17 | "to": "%{_datarootdir}/!name!/" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /rtf/index.go: -------------------------------------------------------------------------------- 1 | package rtf 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "unicode" 7 | 8 | "golang.org/x/text/encoding/charmap" 9 | "golang.org/x/text/runes" 10 | "golang.org/x/text/transform" 11 | ) 12 | 13 | // WriteAsWindows1252 Reads given src file, encodes to windows1252 14 | // and writes the result to dst 15 | func WriteAsWindows1252(src string, dst string) error { 16 | bSrc, err := ioutil.ReadFile(src) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | bDst := make([]byte, len(bSrc)*2) 22 | replaceNonASCII := runes.Map(func(r rune) rune { 23 | if r > unicode.MaxASCII { 24 | return rune('?') 25 | } 26 | return r 27 | }) 28 | transformer := transform.Chain(replaceNonASCII, charmap.Windows1252.NewEncoder()) 29 | _, _, err = transformer.Transform(bDst, bSrc, true) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | dS := strings.NewReplacer("\r\n", "\r\n", "\n", "\r\n").Replace(string(bDst)) 35 | return ioutil.WriteFile(dst, []byte(dS), 0644) 36 | } 37 | 38 | // WriteAsRtf Reads given src file and encodes it to windows1252, 39 | // formats the content to an RTF file 40 | // and writes the result to dst. 41 | func WriteAsRtf(src string, dst string, reencode bool) error { 42 | 43 | bSrc, err := ioutil.ReadFile(src) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | var bDst []byte 49 | 50 | if reencode { 51 | bDst = make([]byte, len(bSrc)) 52 | replaceNonASCII := runes.Map(func(r rune) rune { 53 | if r > unicode.MaxASCII { 54 | return rune('?') 55 | } 56 | return r 57 | }) 58 | transformer := transform.Chain(replaceNonASCII, charmap.Windows1252.NewEncoder()) 59 | _, _, err := transformer.Transform(bDst, bSrc, true) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | dS := strings.NewReplacer("\r\n", "\r\n", "\n", "\r\n").Replace(string(bDst)) 65 | 66 | bDst = []byte(dS) 67 | 68 | } else { 69 | bDst = bSrc 70 | } 71 | 72 | sDat := strings.NewReplacer("\n", "\n\\line ").Replace(string(bDst)) 73 | sDat = "{\\rtf1\\ansi\r\n" + sDat + "\r\n}" 74 | 75 | return ioutil.WriteFile(dst, []byte(sDat), 0644) 76 | } 77 | 78 | // IsRtf Detects if the given src file is formatted with RTF format. 79 | func IsRtf(src string) bool { 80 | dat, err := ioutil.ReadFile(src) 81 | if err != nil { 82 | return false 83 | } 84 | sDat := string(dat) 85 | if len(sDat) > 4 { 86 | return sDat[0:5] == "{\\rtf" 87 | } 88 | return false 89 | } 90 | -------------------------------------------------------------------------------- /templates/LicenseAgreementDlg_HK.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | CostingComplete = 1 12 | "1"]]> 13 | LicenseAccepted = "1" 14 | 15 | 16 | 1 17 | 18 | 19 | 20 | 21 | {{if gt (.License | len) 0}} 22 | 23 | {{end}} 24 | 25 | 26 | 27 | 1 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /templates/WixUI_HK.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 1 29 | "1"]]> 30 | 31 | 1 32 | 33 | NOT Installed 40 | Installed AND PATCH 41 | 42 | 1 43 | LicenseAccepted = "1" 44 | 45 | 1 46 | 1 47 | NOT WIXUI_DONTVALIDATEPATH 48 | "1"]]> 49 | WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" 50 | 51 | 1 52 | 1 53 | 54 | Installed 55 | 56 | 1 57 | 58 | 1 59 | 1 60 | 1 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /templates/choco/LICENSE.txt: -------------------------------------------------------------------------------- 1 | From: {{.Choco.LicenseURL}} 2 | 3 | LICENSE 4 | 5 | {{if gt (.License | len) 0}} 6 | {{.License | cat}} 7 | {{else if gt (.Choco.LicenseURL | len) 0}} 8 | {{.Choco.LicenseURL | download}} 9 | {{end}} 10 | -------------------------------------------------------------------------------- /templates/choco/VERIFICATION.txt: -------------------------------------------------------------------------------- 1 | VERIFICATION 2 | 3 | To check the checksum of this package, extract the msi file contained into it, 4 | then run 5 | 6 | checksum.exe {{.Choco.MsiFile}} -t=sha256 7 | 8 | The result must match 9 | 10 | {{.Choco.MsiSum | upper}} 11 | -------------------------------------------------------------------------------- /templates/choco/chocolateyInstall.ps1: -------------------------------------------------------------------------------- 1 | $packageName = '{{.Choco.ID}}' 2 | $fileType = 'msi' 3 | $silentArgs = '/quiet'; 4 | $scriptPath = $(Split-Path $MyInvocation.MyCommand.Path); 5 | $fileFullPath = Join-Path $scriptPath '{{.Choco.MsiFile}}'; 6 | 7 | Install-ChocolateyInstallPackage $packageName $fileType $silentArgs $fileFullPath -checksum '{{.Choco.MsiSum}}' -checksumType = 'sha256' 8 | -------------------------------------------------------------------------------- /templates/choco/chocolateyUninstall.ps1: -------------------------------------------------------------------------------- 1 | $packageName = "{{.Choco.ID}}"; 2 | $fileType = 'msi'; 3 | $scriptPath = $(Split-Path $MyInvocation.MyCommand.Path); 4 | $fileFullPath = Join-Path $scriptPath '{{.Choco.MsiFile}}'; 5 | 6 | Uninstall-ChocolateyPackage $packageName $fileType "$fileFullPath /q" 7 | -------------------------------------------------------------------------------- /templates/choco/pkg.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{.Choco.ID}} 5 | {{.Choco.Title}} 6 | {{.VersionOk}} 7 | {{.Choco.Authors}} 8 | {{.Choco.Owners}} 9 | {{.Choco.Description}} 10 | {{if gt (.Choco.ProjectURL | len) 0}} 11 | {{.Choco.ProjectURL}} 12 | {{end}} 13 | {{if gt (.Choco.Tags | len) 0}} 14 | {{.Choco.Tags}} 15 | {{end}} 16 | {{if gt (.Choco.LicenseURL | len) 0}} 17 | {{.Choco.LicenseURL}} 18 | {{end}} 19 | {{if gt (.Choco.IconURL | len) 0}} 20 | {{.Choco.IconURL}} 21 | {{end}} 22 | {{if gt (.Choco.ChangeLog | len) 0}} 23 | {{.Choco.ChangeLog}} 24 | {{end}} 25 | {{if .Choco.RequireLicense}} 26 | true 27 | {{else}} 28 | false 29 | {{end}} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /templates/product.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | NOT NEWERVERSIONDETECTED 29 | 30 | 31 | 32 | 33 | 34 | {{if gt (.Files.Items | len) 0}} 35 | 36 | {{range $i, $e := .Files.Items}} 37 | 38 | {{end}} 39 | 40 | {{end}} 41 | {{if gt (.Directories | len) 0}} 42 | {{range $i, $e := .Directories}} 43 | 44 | {{end}} 45 | {{end}} 46 | 47 | 48 | 49 | {{if gt (.Env.Vars | len) 0}} 50 | 51 | {{range $i, $e := .Env.Vars}} 52 | 59 | {{end}} 60 | 61 | {{end}} 62 | 63 | {{if gt (.Shortcuts.Items | len) 0}} 64 | 65 | 66 | 67 | {{range $i, $e := .Shortcuts.Items}} 68 | 77 | {{if gt ($e.Icon | len) 0}} 78 | 79 | {{end}} 80 | 81 | 85 | {{end}} 86 | 87 | 88 | 89 | 90 | {{end}} 91 | 92 | 93 | 94 | {{range $i, $e := .InstallHooks}} 95 | 96 | 97 | {{end}} 98 | {{range $i, $e := .UninstallHooks}} 99 | 100 | 101 | {{end}} 102 | 103 | 104 | {{range $i, $e := .InstallHooks}} 105 | NOT Installed AND NOT REMOVE 106 | {{end}} 107 | {{range $i, $e := .UninstallHooks}} 108 | REMOVE ~= "ALL" 109 | {{end}} 110 | 111 | 112 | 113 | {{if gt (.Env.Vars | len) 0}} 114 | 115 | {{end}} 116 | {{if gt (.Files.Items | len) 0}} 117 | 118 | {{end}} 119 | {{if gt (.Shortcuts.Items | len) 0}} 120 | 121 | {{end}} 122 | {{range $i, $e := .Directories}} 123 | 124 | {{end}} 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /testing/README.md: -------------------------------------------------------------------------------- 1 | # e2e testing - go-msi 2 | 3 | manual end-to-end testing, fedora box, 4 | 5 | ```sh 6 | sh vagrant-setup.sh 7 | 8 | # to re run as many times as needed to work 9 | sh vagrant-test.sh 10 | 11 | sh vagrant-off.sh 12 | 13 | ``` 14 | 15 | 16 | 17 | It does 18 | 19 | - wake up a windows server 2012 box 20 | - setup GO 21 | - setup wix 22 | - setup chocolatey 23 | - prepare a go package of hello/ 24 | - invoke testing/main.go 25 | - builds hello.go 26 | - builds an msi 27 | - runs msi /i 28 | - realize some checks that the app installed and works properly 29 | - runs msi /x (uninstall) 30 | - realize some checks 31 | - runs chocolatey packaging 32 | - installs the choco package 33 | - realize some checks that the app installed and works properly 34 | - runs choco remove 35 | - realize some checks 36 | - shut down vagrant 37 | - destroy the box 38 | -------------------------------------------------------------------------------- /testing/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | # on fedora-like, you must use the downloadable package of vagrant 7 | # available on hashi corp website. 8 | # The per distrib provided package wont provide winrm support.. 9 | # see https://www.vagrantup.com/downloads.html 10 | 11 | config.vm.define :win2012 do |win| 12 | win.vm.box = "opentable/win-2012r2-standard-amd64-nocm" 13 | # big timeout since windows boot is very slow 14 | win.vm.boot_timeout = 500 15 | win.vm.communicator = :winrm 16 | win.vm.provider "virtualbox" do |vb| 17 | # first setup requires gui to be enabled so scripts can be executed in virtualbox guest screen 18 | vb.gui = false 19 | vb.gui = true 20 | vb.customize ["modifyvm", :id, "--memory", "1024"] 21 | vb.customize ["modifyvm", :id, "--vram", "128"] 22 | vb.customize ["modifyvm", :id, "--cpus", "1"] 23 | vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"] 24 | vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 25 | vb.customize ["guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 10000] 26 | end 27 | 28 | # config.vm.synced_folder ".", "/vagrant", type: "rsync", rsync__exclude: ".git/" 29 | 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /testing/glide.lock: -------------------------------------------------------------------------------- 1 | hash: 7c8e01308dcc94b234a32bc906eb4471d88cac5e33b35ad5017f8ca7514f7197 2 | updated: 2017-03-06T20:54:03.564228552+01:00 3 | imports: 4 | - name: golang.org/x/sys 5 | version: e48874b42435b4347fc52bdee0424a52abc974d7 6 | subpackages: 7 | - windows/svc 8 | testImports: [] 9 | -------------------------------------------------------------------------------- /testing/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/mh-cbon/go-msi/testing 2 | import: 3 | - package: golang.org/x/sys 4 | subpackages: 5 | - windows/svc 6 | -------------------------------------------------------------------------------- /testing/hello/LICENSE: -------------------------------------------------------------------------------- 1 | This is the license. 2 | -------------------------------------------------------------------------------- /testing/hello/README.md: -------------------------------------------------------------------------------- 1 | # demo - go-msi 2 | 3 | A demo program for end-to-end testing. 4 | 5 | Find the test suite method [here](https://github.com/mh-cbon/go-msi/tree/master/testing) 6 | -------------------------------------------------------------------------------- /testing/hello/assets/dir1/file2: -------------------------------------------------------------------------------- 1 | file2 2 | -------------------------------------------------------------------------------- /testing/hello/assets/file1: -------------------------------------------------------------------------------- 1 | file1 2 | -------------------------------------------------------------------------------- /testing/hello/change.log: -------------------------------------------------------------------------------- 1 | 2 | 0.0.1 3 | 4 | * A dummy 5 | changelog 6 | * for testing \ 7 | purpose 8 | 9 | - mh-cbon 10 | 11 | -- mh-cbon ; Sat, 23 Jul 2016 15:34:40 +0200 12 | -------------------------------------------------------------------------------- /testing/hello/glide.lock: -------------------------------------------------------------------------------- 1 | hash: fbbc7e2b50d047de95fa2ac0f70ad85818fbb845822855ef2f6f401d1aff4f59 2 | updated: 2017-03-07T14:20:06.739944837+01:00 3 | imports: 4 | - name: github.com/kardianos/osext 5 | version: 9b883c5eb462dd5cb1b0a7a104fe86bc6b9bd391 6 | - name: github.com/kardianos/service 7 | version: ef06f2f890aa4f20d1ff55da91b29392ba6af3af 8 | - name: golang.org/x/sys 9 | version: e48874b42435b4347fc52bdee0424a52abc974d7 10 | subpackages: 11 | - windows 12 | - windows/registry 13 | - windows/svc 14 | - windows/svc/eventlog 15 | - windows/svc/mgr 16 | testImports: [] 17 | -------------------------------------------------------------------------------- /testing/hello/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/mh-cbon/go-msi/testing/hello 2 | import: 3 | - package: github.com/kardianos/service 4 | - package: golang.org/x/sys 5 | subpackages: 6 | - windows/svc/mgr 7 | -------------------------------------------------------------------------------- /testing/hello/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/kardianos/service" 10 | ) 11 | 12 | var logger service.Logger 13 | 14 | func main() { 15 | svcConfig := &service.Config{ 16 | Name: "HelloSvc", 17 | DisplayName: "The Hello service", 18 | Description: "This is an example Go service.", 19 | } 20 | runAsService(svcConfig, func() { 21 | fmt.Println("Starting....") 22 | http.HandleFunc("/", handleIndex) 23 | log.Fatal(http.ListenAndServe(":8080", nil)) 24 | }) 25 | } 26 | 27 | func handleIndex(w http.ResponseWriter, r *http.Request) { 28 | io.WriteString(w, "hello, world\n") 29 | } 30 | 31 | func runAsService(svcConfig *service.Config, run func()) error { 32 | s, err := service.New(&program{exec: run}, svcConfig) 33 | if err != nil { 34 | return err 35 | } 36 | logger, err = s.Logger(nil) 37 | if err != nil { 38 | return err 39 | } 40 | return s.Run() 41 | } 42 | 43 | type program struct { 44 | exec func() 45 | } 46 | 47 | func (p *program) Start(s service.Service) error { 48 | // Start should not block. Do the actual work async. 49 | go p.exec() 50 | return nil 51 | } 52 | func (p *program) Stop(s service.Service) error { 53 | // Stop should not block. Return with a few seconds. 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /testing/hello/ico.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mh-cbon/go-msi/9625c3dd3939a01bcd967a6ed5818cf14c3ffd76/testing/hello/ico.ico -------------------------------------------------------------------------------- /testing/hello/wix.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": "hello", 3 | "company": "mh-cbon", 4 | "license": "LICENSE", 5 | "upgrade-code": "", 6 | "files": { 7 | "guid": "", 8 | "items": [ 9 | "build/amd64/hello.exe" 10 | ] 11 | }, 12 | "directories": [ 13 | "assets" 14 | ], 15 | "env": { 16 | "guid": "", 17 | "vars": [ 18 | { 19 | "name": "some", 20 | "value": "value", 21 | "permanent": "no", 22 | "system": "no", 23 | "action": "set", 24 | "part": "last" 25 | } 26 | ] 27 | }, 28 | "shortcuts": { 29 | "guid": "", 30 | "items": [ 31 | { 32 | "name": "hello", 33 | "description": "hello web server", 34 | "target": "[INSTALLDIR]\\hello.exe", 35 | "wdir": "INSTALLDIR", 36 | "icon":"ico.ico" 37 | } 38 | ] 39 | }, 40 | "hooks": [ 41 | {"when": "install", "command": "sc.exe create HelloSvc binPath=\"[INSTALLDIR]hello.exe\" type=share start=auto DisplayName=\"Hello!\""}, 42 | {"when": "install", "command": "sc.exe start HelloSvc"}, 43 | {"when": "uninstall", "command": "sc.exe delete HelloSvc"} 44 | ], 45 | "choco": { 46 | "description": "hello program", 47 | "project-url": "https://github.com/mh-cbon/go-msi/tree/master/testing", 48 | "tags": "hello", 49 | "license-url": "https://github.com/mh-cbon/go-msi/blob/master/LICENSE" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testing/main.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "strings" 16 | 17 | "golang.org/x/sys/windows/svc" 18 | "golang.org/x/sys/windows/svc/mgr" 19 | ) 20 | 21 | func main() { 22 | 23 | svcName := "HelloSvc" 24 | 25 | confirm(rmFile("log-install.txt"), "install log removal") 26 | confirm(rmFile("log-uninstall.txt"), "uninstall log removal") 27 | 28 | wd := makeDir("c:/gopath/src/github.com/mh-cbon/go-msi/testing/hello") 29 | mustContains(wd, "hello.go") 30 | mustChdir(wd) 31 | 32 | mustEnvEq("$env:some", "") 33 | 34 | mustMkdirAll("build/amd64") 35 | helloBuild := makeCmd("go", "build", "-o", _p("build/amd64/hello.exe"), "hello.go") 36 | mustExec(helloBuild, "hello build failed %v") 37 | 38 | helloPkgSetup := makeCmd("C:/go-msi/go-msi.exe", "set-guid") 39 | mustExec(helloPkgSetup, "hello packaging setup failed %v") 40 | 41 | helloPkg := makeCmd("C:/go-msi/go-msi.exe", "make", 42 | "--msi", "hello.msi", 43 | "--version", "0.0.1", 44 | "--arch", "amd64", 45 | "--keep", 46 | ) 47 | mustExec(helloPkg, "hello packaging failed %v") 48 | 49 | resultPackage := makeFile("hello.msi") 50 | mustExists(resultPackage, "Package file is missing %v") 51 | 52 | mustNotHaveWindowsService("HelloSvc") 53 | 54 | helloPackageInstall := makeCmd("msiexec", "/i", "hello.msi", "/q", "/log", "log-install.txt") 55 | mustExec(helloPackageInstall, "hello package install failed %v") 56 | readFile("log-install.txt") 57 | mustSucceed(rmFile("log-install.txt"), "rmfile failed %v") 58 | 59 | // mustShowEnv("$env:path") 60 | // mustEnvEq("$env:some", "value") 61 | 62 | readDir("C:/Program Files/hello") 63 | readDir("C:/Program Files/hello/assets") 64 | 65 | mgr, helloSvc := mustHaveWindowsService(svcName) 66 | mustHaveStartedWindowsService(svcName, helloSvc) 67 | 68 | helloEpURL := "http://localhost:8080/" 69 | // helloExecPath := "C:/Program Files/hello/hello.exe" 70 | // mustExecHello(helloExecPath, helloEpURL) 71 | mustQueryHello(helloEpURL) 72 | // mustStopWindowsService(svcName, helloSvc) 73 | mustSucceed(helloSvc.Close(), "Failed to close the service %v") 74 | mgr.Disconnect() 75 | 76 | helloPackageUninstall := makeCmd("msiexec", "/x", "hello.msi", "/q", "/log", "log-uninstall.txt") 77 | mustExec(helloPackageUninstall, "hello package uninstall failed %v") 78 | readFile("log-uninstall.txt") 79 | mustSucceed(rmFile("log-uninstall.txt"), "rmfile failed %v") 80 | 81 | mustNotHaveWindowsService("HelloSvc") 82 | 83 | // mustShowEnv("$env:path") 84 | // mustEnvEq("$env:some", "") 85 | 86 | helloChocoPkg := makeCmd("C:/go-msi/go-msi.exe", "choco", 87 | "--input", "hello.msi", 88 | "--version", "0.0.1", 89 | "-c", _qp("C:/Program Files/changelog/changelog.exe")+" ghrelease --version 0.0.1", 90 | "--keep", 91 | ) 92 | mustExec(helloChocoPkg, "hello choco package make failed %v") 93 | 94 | helloNuPkg := makeFile("hello.0.0.1.nupkg") 95 | mustExists(helloNuPkg, "Chocolatey nupkg file is missing %v") 96 | 97 | mustNotHaveWindowsService("HelloSvc") 98 | 99 | helloChocoInstall := makeCmd("choco", "install", "hello.0.0.1.nupkg", "-y") 100 | mustExec(helloChocoInstall, "hello choco package install failed %v") 101 | 102 | readDir("C:/Program Files/hello") 103 | readDir("C:/Program Files/hello/assets") 104 | 105 | mgr, helloSvc = mustHaveWindowsService(svcName) 106 | mustHaveStartedWindowsService(svcName, helloSvc) 107 | mustSucceed(helloSvc.Close(), "Failed to close the service %v") 108 | mgr.Disconnect() 109 | 110 | // mustShowEnv("$env:path") 111 | // mustEnvEq("$env:some", "value") 112 | 113 | // mustExecHello(helloExecPath, helloEpURL) 114 | mustQueryHello(helloEpURL) 115 | // mustStopWindowsService(svcName, helloSvc) 116 | 117 | helloChocoUninstall := makeCmd("choco", "uninstall", "hello", "-v", "-d", "-y", "--force") 118 | mustExec(helloChocoUninstall, "hello choco package uninstall failed %v") 119 | readFile("C:\\ProgramData\\chocolatey\\logs\\chocolatey.log") 120 | 121 | mustNotHaveWindowsService("HelloSvc") 122 | 123 | // mustShowEnv("$env:path") 124 | // mustEnvEq("$env:some", "") 125 | 126 | } 127 | 128 | func mustHaveWindowsService(n string) (*mgr.Mgr, *mgr.Service) { 129 | mgr, err := mgr.Connect() 130 | mustSucceed(err, "Failed to connect to the service manager %v") 131 | s, err := mgr.OpenService(n) 132 | mustSucceed(err, "Failed to open the service %v") 133 | if s == nil { 134 | mustSucceed(err, "Failed to find the service %v") 135 | } 136 | log.Printf("SUCCESS: Service %q exists\n", n) 137 | return mgr, s 138 | } 139 | 140 | func mustNotHaveWindowsService(n string) bool { 141 | mgr, err := mgr.Connect() 142 | mustSucceed(err, "Failed to connect to the service manager %v") 143 | defer mgr.Disconnect() 144 | s, err := mgr.OpenService(n) 145 | mustNotSucceed(err, "Must fail to open the service %v") 146 | if s == nil { 147 | mustNotSucceed(err, "Must fail to find the service %v") 148 | } else { 149 | defer s.Close() 150 | } 151 | log.Printf("SUCCESS: Service %q does not exist\n", n) 152 | return s == nil 153 | } 154 | 155 | func mustHaveStartedWindowsService(n string, s *mgr.Service) { 156 | status, err := s.Query() 157 | mustSucceed(err, "Failed to query the service status %v") 158 | if status.State != svc.Running { 159 | mustSucceed(fmt.Errorf("Service not started %v", n)) 160 | } 161 | log.Printf("SUCCESS: Service %q was started\n", n) 162 | } 163 | 164 | func mustStopWindowsService(n string, s *mgr.Service) { 165 | status, err := s.Control(svc.Stop) 166 | mustSucceed(err, "Failed to control the service status %v") 167 | if status.State != svc.Stopped { 168 | mustSucceed(fmt.Errorf("Service not stopped %v", n)) 169 | } 170 | log.Printf("SUCCESS: Service %q was stopped\n", n) 171 | } 172 | 173 | func mustNotSucceed(err error, format ...string) { 174 | if err == nil { 175 | if len(format) > 0 { 176 | err = fmt.Errorf(format[0], err) 177 | } 178 | log.Fatal(err) 179 | } 180 | } 181 | 182 | func mustQueryHello(u string) { 183 | res := getURL(u) 184 | mustExec(res, "HTTP request failed %v") 185 | mustEqStdout(res, "hello, world\n", "Invalid HTTP response got=%q, want=%q") 186 | log.Printf("SUCCESS: Hello service query %q succeed\n", u) 187 | } 188 | 189 | func mustExecHello(p string, u string) { 190 | helloPackageExec := makeCmd(p) 191 | mustStart(helloPackageExec, "hello command failed %v") 192 | mustQueryHello(u) 193 | mustKill(helloPackageExec, "hello was not killed properly %v") 194 | log.Printf("SUCCESS: Hello program exec %q and query %q succeed\n", p, u) 195 | } 196 | 197 | func _qp(s string) string { 198 | return _q(_p(s)) 199 | } 200 | 201 | func _q(s string) string { 202 | return "\"" + s + "\"" 203 | } 204 | 205 | func _p(s string) string { 206 | return filepath.Clean(s) 207 | } 208 | 209 | func confirm(err error, message string) { 210 | if err == nil { 211 | log.Printf("DONE: %v\n", message) 212 | } else { 213 | log.Printf("NOT-DONE: (%v) %v", err, message) 214 | } 215 | } 216 | func mustSucceed(err error, format ...string) { 217 | if err != nil { 218 | if len(format) > 0 { 219 | err = fmt.Errorf(format[0], err) 220 | } 221 | log.Fatal(err) 222 | } 223 | } 224 | func mustSucceedDetailed(err error, e interface{}, format ...string) { 225 | if x, ok := e.(stdouter); ok { 226 | fmt.Printf("%T:%v\n", x, x.Stdout()) 227 | } 228 | if x, ok := e.(stderrer); ok { 229 | fmt.Printf("%T:%v\n", x, x.Stderr()) 230 | } 231 | if err != nil { 232 | if len(format) > 0 { 233 | err = fmt.Errorf(format[0], err) 234 | } else { 235 | err = fmt.Errorf("%v", err) 236 | } 237 | log.Fatal(err) 238 | } else { 239 | 240 | } 241 | } 242 | func mustFail(err error, format ...string) { 243 | if err == nil { 244 | msg := "Expected to fail" 245 | if len(format) > 0 { 246 | msg = format[0] 247 | } 248 | log.Fatal(msg) 249 | } 250 | } 251 | func mustShowEnv(e string) { 252 | psShowEnv := makeCmd("PowerShell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", e) 253 | mustExec(psShowEnv, "powershell command failed %v") 254 | log.Printf("showEnv ok %v %q", e, psShowEnv.Stdout()) 255 | } 256 | func maybeShowEnv(e string) *cmdExec { 257 | psShowEnv := makeCmd("PowerShell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", e) 258 | warnExec(psShowEnv, "powershell command failed %v") 259 | log.Printf("showEnv ok %v %q", e, psShowEnv.Stdout()) 260 | return psShowEnv 261 | } 262 | func mustLookPath(search string) string { 263 | path, err := exec.LookPath(search) 264 | mustSucceed(err, fmt.Sprintf("lookPath failed %q\n%%v", search)) 265 | path = filepath.Clean(path) 266 | log.Printf("lookPath ok %v=%q", search, path) 267 | return path 268 | } 269 | func mustChdir(s fmt.Stringer) { 270 | path := filepath.Clean(s.String()) 271 | mustSucceed(os.Chdir(path), fmt.Sprintf("chDir failed %q\n%%v", path)) 272 | log.Printf("chdir ok %v", path) 273 | } 274 | func mustEnvEq(env string, expect string, format ...string) { 275 | c := maybeShowEnv(env) 276 | got := c.Stdout() 277 | if len(got) > 0 { 278 | got = got[0 : len(got)-2] 279 | } 280 | f := fmt.Sprintf("Env %q is not equal to want=%q, got=%q", env, expect, got) 281 | mustSucceed(isTrue(got == expect, f)) 282 | log.Printf("mustEnvEq ok %v=%q", env, expect) 283 | } 284 | func mustContains(path fmt.Stringer, file string) { 285 | s := mustLs(path) 286 | _, ex := s[file] 287 | f := fmt.Sprintf("File %q not found in %q", file, path) 288 | mustSucceed(isTrue(ex, f)) 289 | log.Printf("mustContains ok %v %v", path, file) 290 | } 291 | func mustMkdirAll(path string, perm ...os.FileMode) { 292 | if len(perm) == 0 { 293 | perm = []os.FileMode{os.ModePerm} 294 | } 295 | path = filepath.Clean(path) 296 | mustSucceed(os.MkdirAll(path, perm[0]), fmt.Sprintf("mkdirAll failed %q\n%%v", path)) 297 | log.Printf("mkdirAll ok %v %v", path, perm) 298 | } 299 | func mustLs(s fmt.Stringer) map[string]os.FileInfo { 300 | ret := make(map[string]os.FileInfo) 301 | path := filepath.Clean(s.String()) 302 | files, err := ioutil.ReadDir(path) 303 | mustSucceed(err, fmt.Sprintf("readdir failed %q, err=%%v", s)) 304 | for _, f := range files { 305 | ret[f.Name()] = f 306 | } 307 | return ret 308 | } 309 | 310 | type starter interface { 311 | Start() error 312 | } 313 | 314 | func mustStart(e starter, format ...string) { 315 | if len(format) < 1 { 316 | format[0] = "Start err: %v" 317 | } 318 | mustSucceed(e.Start(), format[0]) 319 | } 320 | 321 | type waiter interface { 322 | Wait() error 323 | } 324 | 325 | func mustWait(e waiter, format ...string) { 326 | if len(format) < 1 { 327 | format[0] = "Wait err: %v" 328 | } 329 | mustSucceed(e.Wait(), format[0]) 330 | } 331 | 332 | type execer interface { 333 | Exec() error 334 | } 335 | 336 | func mustExec(e execer, format ...string) { 337 | if len(format) < 1 { 338 | format[0] = "Exec err: %v" 339 | } 340 | mustSucceedDetailed(e.Exec(), e, format[0]) 341 | log.Printf("mustExec success %v", e) 342 | } 343 | 344 | func warnExec(e execer, format ...string) { 345 | if err := e.Exec(); err != nil { 346 | if len(format) < 1 { 347 | format[0] = "Exec err: %v" 348 | } 349 | log.Printf(format[0], err) 350 | } 351 | } 352 | 353 | type killer interface { 354 | Kill() error 355 | } 356 | 357 | func mustKill(e killer, format ...string) { 358 | if len(format) < 1 { 359 | format[0] = "Kill err: %v" 360 | } 361 | mustSucceed(e.Kill(), format[0]) 362 | } 363 | 364 | type exister interface { 365 | exists() bool 366 | } 367 | 368 | func mustExists(e exister, format ...string) { 369 | if len(format) < 1 { 370 | format[0] = fmt.Sprintf("mustExists err: %T does not exist %q, got %%v", e, e) 371 | } 372 | mustSucceed(isTrue(e.exists(), format[0])) 373 | } 374 | 375 | func isTrue(b bool, format ...string) error { 376 | if b == false { 377 | if len(format) < 1 { 378 | format[0] = "mustTrue got %v" 379 | } 380 | return fmt.Errorf(format[0], b) 381 | } 382 | return nil 383 | } 384 | 385 | type stderrer interface { 386 | Stderr() string 387 | } 388 | 389 | type stdouter interface { 390 | Stdout() string 391 | } 392 | 393 | func mustEqStdout(e stdouter, expected string, format ...string) { 394 | got := e.Stdout() 395 | if len(format) < 1 { 396 | format[0] = fmt.Sprintf("mustEqStdout failed: output does not match, got=%q, want=%q", got, expected) 397 | } 398 | mustSucceed(isTrue(got == expected), format[0]) 399 | } 400 | 401 | func makeFile(f string) *file { 402 | return &file{f} 403 | } 404 | 405 | type file struct { 406 | path string 407 | } 408 | 409 | func (f *file) exists() bool { 410 | if _, err := os.Stat(f.path); os.IsNotExist(err) { 411 | return false 412 | } 413 | return true 414 | } 415 | func (f *file) String() string { 416 | return f.path 417 | } 418 | 419 | func makeDir(f string) *dir { 420 | return &dir{f} 421 | } 422 | 423 | type dir struct { 424 | path string 425 | } 426 | 427 | func (d *dir) exists() bool { 428 | if _, err := os.Stat(d.path); os.IsNotExist(err) { 429 | return false 430 | } 431 | return true 432 | } 433 | func (d *dir) String() string { 434 | return d.path 435 | } 436 | 437 | func getURL(url string) *httpRequest { 438 | return &httpRequest{url: url} 439 | } 440 | 441 | type httpRequest struct { 442 | url string 443 | err error 444 | body string 445 | statusCode int 446 | headers map[string][]string 447 | } 448 | 449 | func (f *httpRequest) String() string { 450 | return f.url 451 | } 452 | 453 | func (f *httpRequest) Stdout() string { 454 | return f.body 455 | } 456 | func (f *httpRequest) Header(name string) []string { 457 | return f.headers[name] 458 | } 459 | func (f *httpRequest) RespondeCode() int { 460 | return f.statusCode 461 | } 462 | func (f *httpRequest) ExitOk() bool { 463 | return f.statusCode == 200 464 | } 465 | func (f *httpRequest) Exec() error { 466 | response, err := http.Get(f.url) 467 | f.headers = response.Header 468 | f.statusCode = response.StatusCode 469 | f.err = err 470 | if f.err == nil { 471 | var b bytes.Buffer 472 | defer response.Body.Close() 473 | _, f.err = io.Copy(&b, response.Body) 474 | if f.err == nil { 475 | f.body = b.String() 476 | } 477 | } 478 | return f.err 479 | } 480 | 481 | type cmdExec struct { 482 | *exec.Cmd 483 | bin string 484 | args []string 485 | startErr error 486 | hasStarted bool 487 | waitErr error 488 | hasWaited bool 489 | stdout *bytes.Buffer 490 | stderr *bytes.Buffer 491 | } 492 | 493 | func (e *cmdExec) String() string { 494 | return e.bin + " " + strings.Join(e.args, " ") 495 | } 496 | 497 | func (e *cmdExec) SetArgs(args []string) error { 498 | if e.hasStarted { 499 | return fmt.Errorf("Cannot set arguments on command already started") 500 | } 501 | if e.hasWaited { 502 | return fmt.Errorf("Cannot set arguments on command already waited") 503 | } 504 | e.args = args 505 | return nil 506 | } 507 | 508 | func (e *cmdExec) Start() error { 509 | if !e.hasStarted { 510 | e.startErr = e.Cmd.Start() 511 | } 512 | e.hasStarted = true 513 | return e.startErr 514 | } 515 | func (e *cmdExec) Wait() error { 516 | if !e.hasWaited { 517 | e.waitErr = e.Cmd.Wait() 518 | } 519 | e.hasWaited = true 520 | return e.waitErr 521 | } 522 | func (e *cmdExec) Exec() error { 523 | if err := e.Start(); err != nil { 524 | return err 525 | } 526 | return e.Wait() 527 | } 528 | func (e *cmdExec) Kill() error { 529 | return e.Process.Kill() 530 | } 531 | func (e *cmdExec) Stdout() string { 532 | if e.hasStarted == false { 533 | log.Fatal("Process must have run") 534 | } 535 | return e.stdout.String() 536 | } 537 | func (e *cmdExec) Stderr() string { 538 | if e.hasStarted == false { 539 | log.Fatal("Process must have run") 540 | } 541 | return e.stderr.String() 542 | } 543 | func (e *cmdExec) ExitOk() bool { 544 | if e.hasWaited == false { 545 | log.Fatal("Process must have run") 546 | } 547 | return e.ProcessState.Exited() && e.ProcessState.Success() 548 | } 549 | 550 | func makeCmd(w string, a ...string) *cmdExec { 551 | log.Printf("makeCmd: %v %v\n", w, a) 552 | cmd := exec.Command(mustLookPath(w), a...) 553 | var stdout bytes.Buffer 554 | var stderr bytes.Buffer 555 | // cmd.Stdout = &stdout 556 | // cmd.Stderr = &stderr 557 | cmd.Stdout = os.Stderr 558 | cmd.Stderr = os.Stderr 559 | return &cmdExec{Cmd: cmd, stdout: &stdout, stderr: &stderr} 560 | } 561 | 562 | func readDir(s string) { 563 | s = filepath.Clean(s) 564 | files, err := ioutil.ReadDir(s) 565 | mustSucceed(err, fmt.Sprintf("readdir failed %q, err=%%v", s)) 566 | log.Printf("Content of directory %q\n", s) 567 | for _, f := range files { 568 | log.Printf(" %v\n", f.Name()) 569 | } 570 | } 571 | 572 | func readFile(s string) { 573 | s = filepath.Clean(s) 574 | fd, err := os.Open(s) 575 | mustSucceed(err, fmt.Sprintf("readfile failed %q, err=%%v", s)) 576 | defer fd.Close() 577 | if fd != nil { 578 | io.Copy(fd, os.Stdout) 579 | } 580 | } 581 | 582 | func rmFile(s string) error { 583 | s = filepath.Clean(s) 584 | return os.Remove(s) 585 | } 586 | -------------------------------------------------------------------------------- /testing/test.bat: -------------------------------------------------------------------------------- 1 | 2 | cd C:\\gopath\\src\\github.com\\mh-cbon\\go-msi\\testing\\hello 3 | 4 | C:\\go-msi\\go-msi.exe make --msi hello.msi --version 0.0.1 --arch amd64 5 | msiexec /i hello.msi /q 6 | 7 | mkdir wixtemplates 8 | C:\\go-msi\\go-msi.exe generate-templates --out wixtemplates --version 0.0.1 9 | -------------------------------------------------------------------------------- /testing/vagrant-off.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | set -e 3 | 4 | vagrant halt 5 | vagrant destroy -f win2012 6 | -------------------------------------------------------------------------------- /testing/vagrant-setup.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | set -e 3 | 4 | # sudo dnf install http://download.virtualbox.org/virtualbox/5.1.18/VirtualBox-5.1-5.1.18_114002_fedora25-1.x86_64.rpm -y 5 | # sudo dnf install kernel-devel -y 6 | # sudo /sbin/vboxconfig # sometime needed. 7 | 8 | # prepare and load vagrant 9 | # vagrant plugin install winrm-fs # seems useless since vagrant 1.9.7 10 | # vagrant plugin install winrm 11 | vagrant up --provider=virtualbox 12 | 13 | # prepare hello program like appveyor 14 | vagrant winrm -c "mkdir c:\\gopath\\src\\github.com\\mh-cbon\\go-msi\\testing\\hello" 15 | vagrant winrm -c 'Copy-Item C:\\vagrant\\hello\\* -destination c:\\gopath\\src\\github.com\\mh-cbon\\go-msi\\testing\\hello\\ -recurse -Force' 16 | 17 | # setup go 18 | VERSION=`curl https://golang.org/VERSION?m=text` 19 | wget https://storage.googleapis.com/golang/$VERSION.windows-amd64.msi 20 | vagrant winrm -c "COPY C:\\vagrant\\$VERSION.windows-amd64.msi C:\\go.msi" 21 | vagrant winrm -c "msiexec.exe /i C:\\go.msi /quiet" 22 | vagrant winrm -c "setx GOPATH C:\\gopath\\" 23 | vagrant winrm -c "ls env:GOPATH" 24 | rm $VERSION.windows-amd64.msi 25 | 26 | # setup changelog 27 | wget https://github.com/mh-cbon/changelog/releases/download/0.0.25/changelog-amd64.msi 28 | vagrant winrm -c "COPY C:\\vagrant\\changelog-amd64.msi C:\\changelog-amd64.msi" 29 | vagrant winrm -c 'cmd.exe /c "msiexec.exe /i C:\\changelog-amd64.msi /quiet"' 30 | rm changelog-amd64.msi 31 | 32 | # setup go-msi 33 | GOOS=windows GOARCH=amd64 go build -o go-msi.exe ../main.go 34 | vagrant winrm -c "mkdir C:\\go-msi\\templates" 35 | cp -r ../templates . 36 | vagrant winrm -c 'Copy-Item C:\\vagrant\\templates\\* -destination C:\\go-msi\\templates\\ -recurse -Force' 37 | rm -fr templates 38 | vagrant winrm -c 'COPY C:\\vagrant\\go-msi.exe C:\\go-msi\\' 39 | rm -fr go-msi.exe 40 | 41 | # quick test/setup 42 | # GOOS=windows GOARCH=amd64 go build -o go-msi.exe ../main.go 43 | # vagrant winrm -c 'COPY C:\\vagrant\\go-msi.exe C:\\go-msi\\' 44 | # rm -fr go-msi.exe 45 | # vagrant winrm -c 'C:\\go-msi\\go-msi.exe check-env' 46 | 47 | 48 | # setup wix 49 | wget -O wix310-binaries.zip http://wixtoolset.org/downloads/v3.10.3.3007/wix310-binaries.zip 50 | unzip wix310-binaries.zip -d wix310 51 | vagrant winrm -c "xcopy /E /I C:\vagrant\wix310 C:\wix310" 52 | vagrant winrm -c "setx PATH \"%PATH%;C:\\wix310\"" 53 | rm -fr wix310 wix310*zip 54 | 55 | # setup chocolatey 56 | vagrant winrm -c 'iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex' 57 | vagrant winrm -c 'choco source add -n=mh-cbon -s="https://api.bintray.com/nuget/mh-cbon/choco"' 58 | vagrant winrm -c 'choco install changelog go-msi emd gump gh-api-cli -y' 59 | vagrant winrm -c 'changelog -v' 60 | vagrant winrm -c 'go-msi -v' 61 | vagrant winrm -c 'emd -version' 62 | vagrant winrm -c 'gh-api-cli -v' 63 | -------------------------------------------------------------------------------- /testing/vagrant-test.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | set -e 3 | 4 | 5 | vagrant winrm -c "mkdir c:\\gopath\\src\\github.com\\mh-cbon\\go-msi\\testing\\hello" 6 | vagrant winrm -c 'Copy-Item C:\\vagrant\\hello\\* -destination c:\\gopath\\src\\github.com\\mh-cbon\\go-msi\\testing\\hello\\ -recurse -Force' 7 | 8 | cp -r ../templates . && \ 9 | vagrant winrm -c 'Copy-Item C:\\vagrant\\templates\\* -destination C:\\go-msi\\templates\\ -recurse -Force' && \ 10 | rm -fr templates 11 | 12 | GOOS=windows go build -o test.exe main.go \ 13 | && echo "----" \ 14 | && vagrant winrm -c "C:\\vagrant\\test.exe" 15 | 16 | 17 | # vagrant winrm -c "ls env:some" || echo "ok, expected not found" 18 | 19 | # vagrant winrm -c "\$env:path" 20 | 21 | # vagrant winrm -c "Get-Content -Path \"C:\\ProgramData\\chocolatey\\logs\\chocolatey.log\" -Tail 100" 22 | 23 | # vagrant winrm -c "choco install c:\gopath\src\github.com\mh-cbon\go-msi\testing\hello\hello.0.0.1.nupkg -y" 24 | 25 | # vagrant winrm -c "choco uninstall hello -y" 26 | 27 | # vagrant winrm -c ". C:\Windows\System32\msiexec.exe /i c:\gopath\src\github.com\mh-cbon\go-msi\testing\hello\hello.msi /quiet" 28 | 29 | # vagrant winrm -c ". C:\Windows\System32\msiexec.exe /uninstall c:\gopath\src\github.com\mh-cbon\go-msi\testing\hello\hello.msi /quiet" 30 | -------------------------------------------------------------------------------- /tpls/index.go: -------------------------------------------------------------------------------- 1 | package tpls 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "text/template" 13 | 14 | "github.com/mattn/go-zglob" 15 | "github.com/mh-cbon/go-msi/manifest" 16 | ) 17 | 18 | var funcMap = template.FuncMap{ 19 | "dec": func(i int) int { 20 | return i - 1 21 | }, 22 | "cat": func(filename string) string { 23 | out, err := ioutil.ReadFile(filename) 24 | if err != nil { 25 | panic(fmt.Errorf("failed to read file %q", filename)) 26 | } 27 | return string(out) 28 | }, 29 | "download": func(url string) string { 30 | response, err := http.Get(url) 31 | if err != nil { 32 | panic(fmt.Errorf("failed to download url %q", url)) 33 | } 34 | defer response.Body.Close() 35 | var b bytes.Buffer 36 | if _, err := io.Copy(&b, response.Body); err != nil { 37 | panic(fmt.Errorf("failed to download url %q", url)) 38 | } 39 | return b.String() 40 | }, 41 | "upper": strings.ToUpper, 42 | } 43 | 44 | // Find all wxs fies in given directory 45 | func Find(srcDir string, pattern string) ([]string, error) { 46 | glob := filepath.Join(srcDir, pattern) 47 | return zglob.Glob(glob) 48 | } 49 | 50 | // GenerateTemplate generates given src template to out file using given manifest 51 | func GenerateTemplate(wixFile *manifest.WixManifest, src string, out string) error { 52 | tpl, err := template.New("").Funcs(funcMap).ParseFiles(src) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | fileWriter, err := os.Create(out) 58 | if err != nil { 59 | return err 60 | } 61 | defer fileWriter.Close() 62 | err = tpl.ExecuteTemplate(fileWriter, filepath.Base(src), wixFile) 63 | if err != nil { 64 | return err 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /unice-recipe.md: -------------------------------------------------------------------------------- 1 | # unix friendly recipe to create msi package 2 | 3 | This is an HOWDOI build an msi package from a non windows machine. 4 | 5 | It s not very difficult, but its time consuming, especially the first time. 6 | 7 | ### Requirements 8 | 9 | - vagrant, get the source from the website, not from your distro or brewer 10 | - virtualbox, afaik the box i use requires it 11 | - at least 20GB space 12 | - some time ahead 13 | 14 | ### Machine setup 15 | 16 | Start by installing vagrant using the sources available [here](https://www.vagrantup.com/downloads.html) 17 | 18 | Install virtualbox from [here](https://www.virtualbox.org/wiki/Linux_Downloads) (I can t remember if the distro packages are fine, let me know if you try it) 19 | 20 | Install the vagrant plugins 21 | 22 | ```sh 23 | vagrant plugin install winrm 24 | vagrant plugin install vagrant-winrm 25 | vagrant plugin install vagrant-vbguest 26 | vagrant plugin install vagrant-share 27 | ``` 28 | 29 | Initialize a new windows vagrant box on root of your directory with a content similar to [this one](https://github.com/mh-cbon/go-msi/blob/master/Vagrantfile) 30 | 31 | A that point you should be ready to `up` the machine, 32 | 33 | ```sh 34 | vagrant up 35 | ``` 36 | 37 | And get yourself a coffee. At first init it will start to download the vagrant image which is about 14GB. 38 | 39 | Once the machine is up, ensure `winrm` works correctly 40 | 41 | ```sh 42 | vagrant winrm -c "dir C:\\vagrant" 43 | ``` 44 | 45 | The command should display your project files. 46 | 47 | 48 | From your local computer, download [wix](http://wixtoolset.org/releases/v3-10-3-3007/) msi package, 49 | 50 | ```sh 51 | wget -O wix310-binaries.zip http://wixtoolset.org/downloads/v3.10.3.3007/wix310-binaries.zip 52 | # or 53 | curl -O http://wixtoolset.org/downloads/v3.10.3.3007/wix310-binaries.zip 54 | 55 | unzip wix310-binaries.zip -d wix310 56 | ``` 57 | 58 | Copy `wix310` folder, clean your copy 59 | 60 | ```sh 61 | vagrant winrm -c "xcopy /E /I C:\vagrant\wix310 C:\wix310" 62 | rm -fr wix310 63 | rm -fr wix310-binaries.zip 64 | ``` 65 | 66 | Register `wix` bin path to `PATH` 67 | 68 | ```sh 69 | vagrant winrm -c "setx PATH \"%PATH%;C:\\wix310\"" 70 | ``` 71 | 72 | Confirm its up to date by running this command 73 | 74 | ```sh 75 | vagrant winrm -c "heat.exe -v" 76 | ``` 77 | 78 | From your local computer, download [go-msi](https://github.com/mh-cbon/go-msi/releases) msi package, 79 | 80 | ```sh 81 | wget -O go-msi.msi https://github.com/mh-cbon/go-msi/releases/download/0.0.22/go-msi-amd64.msi 82 | # or 83 | curl -O https://github.com/mh-cbon/go-msi/releases/download/0.0.22/go-msi-amd64.msi 84 | ``` 85 | 86 | Trigger `go-msi` setup on the remote windows machine, 87 | 88 | ```sh 89 | vagrant winrm -c "msiexec.exe /i C:\\vagrant\\go-msi-amd64.msi /quiet" 90 | ``` 91 | 92 | __At that point, the machine is ready__ 93 | 94 | Those steps are to reproduce every time you `destroy` the machine. 95 | 96 | If you only `halt` the machine, you can jump to the next section. 97 | 98 | ### Generate the package 99 | 100 | To generate the package, you can run 101 | 102 | ```sh 103 | vagrant winrm -c "cd C:\\vagrant; go-msi make -m go-msi.msi --version 0.0.1 --arch amd64" 104 | ``` 105 | 106 | To test install your packages, 107 | 108 | ```sh 109 | vagrant winrm -c "msiexec.exe /i C:\\vagrant\\go-msi-amd64.msi /quiet" 110 | ``` 111 | 112 | To uninstall your package, 113 | 114 | ```sh 115 | vagrant winrm -c "msiexec.exe /uninstall C:\\vagrant\\go-msi-amd64.msi /quiet" 116 | ``` 117 | 118 | ### Generate a chocolatey package 119 | 120 | Install chocolatey, 121 | 122 | ```sh 123 | vagrant winrm -c 'iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex' 124 | ``` 125 | 126 | Generate the choco package, 127 | 128 | ```sh 129 | vagrant winrm -c 'cmd.exe /c "cd C:\\vagrant\\ && go-msi.exe choco --input go-msi-amd64.msi --version 0.0.1"' 130 | ``` 131 | 132 | Test install the choco package, 133 | 134 | ```sh 135 | vagrant winrm -c 'cmd.exe /c "cd C:\\vagrant\\ && choco install go-msi.0.0.1.nupkg -y' 136 | ``` 137 | 138 | Test uninstall the choco package, 139 | 140 | ```sh 141 | vagrant winrm -c 'cmd.exe /c "cd C:\\vagrant\\ && choco uninstall go-msi -y' 142 | ``` 143 | 144 | Push the choco package, 145 | 146 | ```sh 147 | vagrant winrm -c "cmd.exe /c \"cd C:\\vagrant\\ && choco push -k=\"'xxx'\" go-msi.0.0.1.nupkg\"" 148 | ``` 149 | 150 | Then `halt` the machine. 151 | 152 | The resulting `msi` package will be placed into your project root. 153 | 154 | ### That's it 155 | 156 | I hope it works for you too, 157 | 158 | ~~ Happy Coding 159 | -------------------------------------------------------------------------------- /util/index.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "io" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | ) 11 | 12 | // GetBinPath Find path of the current binary file on the file system 13 | func GetBinPath() (string, error) { 14 | var err error 15 | wd := "" 16 | if filepath.Base(os.Args[0]) == "main" { // go run ... 17 | wd, err = os.Getwd() 18 | } else { 19 | bin, err2 := exec.LookPath(os.Args[0]) 20 | if err2 == nil { 21 | wd = filepath.Dir(bin) 22 | } 23 | } 24 | return wd, err 25 | } 26 | 27 | //CopyFile copy file src to dst. 28 | func CopyFile(dst, src string) error { 29 | s, err := os.Open(src) 30 | if err != nil { 31 | return err 32 | } 33 | defer s.Close() 34 | d, err := os.Create(dst) 35 | if err != nil { 36 | return err 37 | } 38 | if _, err := io.Copy(d, s); err != nil { 39 | d.Close() 40 | return err 41 | } 42 | return d.Close() 43 | } 44 | 45 | //ComputeSha256 computes the sha256 value of a file content. 46 | func ComputeSha256(filepath string) (string, error) { 47 | hasher := sha256.New() 48 | f, err := os.Open(filepath) 49 | if err != nil { 50 | return "", err 51 | } 52 | defer f.Close() 53 | if _, err := io.Copy(hasher, f); err != nil { 54 | return "", err 55 | } 56 | return hex.EncodeToString(hasher.Sum(nil)), nil 57 | } 58 | 59 | //Exec computes the sha256 value of a file content. 60 | func Exec(w string, args ...string) (string, error) { 61 | cmd := exec.Command(w, args...) 62 | out, err := cmd.CombinedOutput() 63 | return string(out), err 64 | } 65 | -------------------------------------------------------------------------------- /wix.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": "go-msi", 3 | "company": "mh-cbon", 4 | "license": "LICENSE", 5 | "upgrade-code": "8615055C-D8E0-404C-93BE-441C503BA6F0", 6 | "files": { 7 | "guid": "378896D8-6749-4821-870A-44CBBB791D0C", 8 | "items": [ 9 | "go-msi.exe" 10 | ] 11 | }, 12 | "directories": [ 13 | "templates" 14 | ], 15 | "env": { 16 | "guid": "0CB88C7F-85A7-4986-B6CE-1CAD5C17EA0E", 17 | "vars": [ 18 | { 19 | "name": "PATH", 20 | "value": "[INSTALLDIR]", 21 | "permanent": "no", 22 | "system": "no", 23 | "action": "set", 24 | "part": "last" 25 | } 26 | ] 27 | }, 28 | "shortcuts": { 29 | "guid": "6DAFD205-3D2D-43D7-BF78-33BFE8746D2A", 30 | "items": [ 31 | { 32 | "name": "go-msi", 33 | "description": "Easy msi pakage for Go", 34 | "target": "[INSTALLDIR]\\go-msi.exe", 35 | "wdir": "INSTALLDIR", 36 | "arguments": "" 37 | } 38 | ] 39 | }, 40 | "choco": { 41 | "description": "Easy way to generate msi package for a Go project", 42 | "project-url": "https://github.com/mh-cbon/go-msi", 43 | "tags": "generate go msi nuget", 44 | "license-url": "https://github.com/mh-cbon/go-msi/blob/master/LICENSE" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /wix/index.go: -------------------------------------------------------------------------------- 1 | package wix 2 | 3 | import ( 4 | "path/filepath" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/mh-cbon/go-msi/manifest" 9 | ) 10 | 11 | var eol = "\r\n" 12 | 13 | // GenerateCmd generates required command lines to produce an msi package, 14 | func GenerateCmd(wixFile *manifest.WixManifest, templates []string, msiOutFile string, arch string) string { 15 | 16 | cmd := "" 17 | 18 | for i, dir := range wixFile.RelDirs { 19 | sI := strconv.Itoa(i) 20 | cmd += "heat dir " + dir + " -nologo -cg AppFiles" + sI 21 | cmd += " -gg -g1 -srd -sfrag -template fragment -dr APPDIR" + sI 22 | cmd += " -var var.SourceDir" + sI 23 | cmd += " -out AppFiles" + sI + ".wxs" 24 | cmd += eol 25 | } 26 | cmd += "candle" 27 | if arch != "" { 28 | if arch == "386" { 29 | arch = "x86" 30 | } else if arch == "amd64" { 31 | arch = "x64" 32 | } 33 | cmd += " -arch " + arch 34 | } 35 | for i, dir := range wixFile.RelDirs { 36 | sI := strconv.Itoa(i) 37 | cmd += " -dSourceDir" + sI + "=" + dir 38 | } 39 | for i := range wixFile.Directories { 40 | sI := strconv.Itoa(i) 41 | cmd += " AppFiles" + sI + ".wxs" 42 | } 43 | for _, tpl := range templates { 44 | cmd += " " + filepath.Base(tpl) 45 | } 46 | cmd += eol 47 | cmd += "light -ext WixUIExtension -ext WixUtilExtension -sacl -spdb " 48 | cmd += " -out " + msiOutFile 49 | for i := range wixFile.Directories { 50 | sI := strconv.Itoa(i) 51 | cmd += " AppFiles" + sI + ".wixobj" 52 | } 53 | for _, tpl := range templates { 54 | cmd += " " + strings.Replace(filepath.Base(tpl), ".wxs", ".wixobj", -1) 55 | } 56 | cmd += eol 57 | 58 | return cmd 59 | } 60 | --------------------------------------------------------------------------------