From 452daad14b8f551646e7458d0e9e3972267d832f Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sat, 5 Nov 2022 15:40:53 +0100 Subject: [PATCH] build: Add GitHub actions build for Windows (#8627) This is sort of a proof of concept, but since our current Windows builder is down this might solve the problem. It includes a change for easier code signing (taking the certificate in a secret/env var rather than existing already on disk), but otherwise mirrors precisely what we already do in the build server. --- .github/workflows/build-syncthing.yaml | 117 +++++++++++++++++++++++++ build.go | 30 ++++++- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build-syncthing.yaml diff --git a/.github/workflows/build-syncthing.yaml b/.github/workflows/build-syncthing.yaml new file mode 100644 index 00000000000..837bfe77efb --- /dev/null +++ b/.github/workflows/build-syncthing.yaml @@ -0,0 +1,117 @@ +name: Build Syncthing + +on: + pull_request: + push: + +env: + # The go version to use for builds. + GO_VERSION: "1.19.3" + + # Optimize compatibility on the slow archictures. + GO386: softfloat + GOARM: "5" + GOMIPS: softfloat + + # Avoid hilarious amounts of obscuring log output when running tests. + LOGGER_DISCARD: "1" + +# A note on actions and third party code... The actions under actions/ (like +# `uses: actions/checkout`) are maintained by GitHub, and we need to trust +# GitHub to maintain their code and infrastructure or we're in deep shit in +# general. The same doesn't necessarily apply to other actions authors, so +# some care needs to be taken when adding steps, especially in the paths +# that lead up to code being packaged and signed. + +jobs: + + # + # Windows, quick build and test, runs always + # + + build-windows: + name: Build and test on Windows + runs-on: windows-latest + steps: + - name: Set git to use LF + # Without this, the checkout will happen with CRLF line endings, + # which is fine for the source code but messes up tests that depend + # on data on disk being as expected. Ideally, those tests should be + # fixed, but not today. + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v3 + + - uses: actions/setup-go@v3 + # `cache: true` gives us automatic caching of modules and build + # cache, speeding up builds. The cache key is dependent on the Go + # version and our go.sum contents. + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Build and test + run: | + go run build.go + go run build.go test + + # + # Windows, build signed packages + # + + package-windows: + name: Create packages for Windows + runs-on: windows-latest + # We only run this job for release pushes. + if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release') + # This is also enforced by the environment which contains the secrets. + environment: signing + needs: + - build-windows + steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v3 + # `fetch-depth: 0` because we want to check out the entire repo + # including tags and branches, not just the latest commit which + # lacks version info. + with: + fetch-depth: 0 + + - uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - uses: actions/cache@v3 + with: + path: | + ~\AppData\Local\go-build + ~\go\pkg\mod + key: ${{ runner.os }}-go-${{ env.GOVERSION }}-package-${{ hashFiles('**/go.sum') }} + + - name: Install dependencies + run: | + go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@v1.4.0 + + - name: Create packages + run: | + go run build.go -goarch amd64 zip + go run build.go -goarch arm zip + go run build.go -goarch arm64 zip + go run build.go -goarch 386 zip + env: + CODESIGN_SIGNTOOL: ${{ secrets.CODESIGN_SIGNTOOL }} + CODESIGN_CERTIFICATE_BASE64: ${{ secrets.CODESIGN_CERTIFICATE_BASE64 }} + CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }} + CODESIGN_TIMESTAMP_SERVER: ${{ secrets.CODESIGN_TIMESTAMP_SERVER }} + + - name: Archive artifacts + uses: actions/upload-artifact@v3 + with: + name: packages + path: syncthing-windows-*.zip diff --git a/build.go b/build.go index b37a19e586f..872fffa7c11 100644 --- a/build.go +++ b/build.go @@ -15,6 +15,7 @@ import ( "bytes" "compress/flate" "compress/gzip" + "encoding/base64" "encoding/json" "errors" "flag" @@ -1383,6 +1384,33 @@ func windowsCodesign(file string) { args := []string{"sign", "/fd", algo} if f := os.Getenv("CODESIGN_CERTIFICATE_FILE"); f != "" { args = append(args, "/f", f) + } else if b := os.Getenv("CODESIGN_CERTIFICATE_BASE64"); b != "" { + // Decode the PFX certificate from base64. + bs, err := base64.RawStdEncoding.DecodeString(b) + if err != nil { + log.Println("Codesign: signing failed: decoding base64:", err) + return + } + + // Write it to a temporary file + f, err := os.CreateTemp("", "codesign-*.pfx") + if err != nil { + log.Println("Codesign: signing failed: creating temp file:", err) + return + } + _ = f.Chmod(0600) // best effort remove other users' access + defer os.Remove(f.Name()) + if _, err := f.Write(bs); err != nil { + log.Println("Codesign: signing failed: writing temp file:", err) + return + } + if err := f.Close(); err != nil { + log.Println("Codesign: signing failed: closing temp file:", err) + return + } + + // Use that when signing + args = append(args, "/f", f.Name()) } if p := os.Getenv("CODESIGN_CERTIFICATE_PASSWORD"); p != "" { args = append(args, "/p", p) @@ -1402,7 +1430,7 @@ func windowsCodesign(file string) { bs, err := runError(st, args...) if err != nil { - log.Println("Codesign: signing failed:", string(bs)) + log.Printf("Codesign: signing failed: %v: %s", err, string(bs)) return } log.Println("Codesign: successfully signed", file, "using", algo)