diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..37ee028 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +### Motivation + + + +### Change description + + + +### Additional Notes + + + +### Reviewer checklist + +- [ ] PR addresses a single concern. +- [ ] PR title and description are properly filled. +- [ ] Changes will be merged in `main`. +- [ ] Changes are covered by tests. +- [ ] Logging is meaningful in case of troubleshooting. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..90b6be2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,287 @@ +name: Release + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+*" + +env: + GO_VERSION: "1.25.1" + PROJECT_NAME: "arduino-flasher-cli" + DIST_DIR: build + +jobs: + build: + strategy: + matrix: + runon: [ubuntu-24.04] + os: [linux, darwin] + arch: [amd64, arm64] + include: + - runon: windows-2025 + os: windows + arch: amd64 + runs-on: ${{ matrix.runon }} + outputs: + release: ${{ steps.set-version.outputs.RELEASE_NAME }} + defaults: + run: + shell: bash + steps: + - name: Tag version + run: | + VERSION=${GITHUB_REF##*/} + echo "VERSION=${VERSION}" >> $GITHUB_ENV + echo "RELEASE_NAME=${{ env.PROJECT_NAME }}-${VERSION}-${{ matrix.os }}-${{ matrix.arch }}" >> $GITHUB_ENV + env: + GITHUB_REF: ${{ github.ref }} + + - name: Set Windows version + id: set-version + run: | + echo "RELEASE_NAME=${{ env.RELEASE_NAME }}" >> $GITHUB_OUTPUT + if: matrix.os == 'windows' + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Taskfile + uses: arduino/setup-task@v2 + with: + version: "3.x" + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Binary + run: | + task build + env: + GOARCH: ${{ matrix.arch }} + GOOS: ${{ matrix.os }} + GH_TOKEN: ${{ secrets.ARDUINOBOT_TOKEN }} + + - name: Prepare Build Artifacts (!windows) + working-directory: ./${{ env.DIST_DIR }} + run: tar -czf ${{ env.RELEASE_NAME }}.tar.gz arduino-flasher-cli -C ../ LICENSE + if: matrix.os != 'windows' + + - name: Prepare Build Artifacts (windows) + working-directory: ./${{ env.DIST_DIR }} + run: 7z a -tzip ${{ env.RELEASE_NAME }}.zip arduino-flasher-cli.exe ../LICENSE + if: matrix.os == 'windows' + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-${{ matrix.os }}-${{ matrix.arch }} + path: | + ${{ env.DIST_DIR }}/${{ env.RELEASE_NAME }}.tar.gz + ${{ env.DIST_DIR }}/${{ env.RELEASE_NAME }}.zip + if-no-files-found: error + + sign-windows-executable: + runs-on: windows-sign-pc + needs: build + + defaults: + run: + shell: bash + + env: + RELEASE_NAME: ${{ needs.build.outputs.release }} + INSTALLER_CERT_WINDOWS_CER: "/tmp/cert.cer" + # We are hardcoding the path for signtool because is not present on the windows PATH env var by default. + # Keep in mind that this path could change when upgrading to a new runner version + SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x86/signtool.exe" + SEVENZ_PATH: "C:/ProgramData/chocolatey/tools/7z.exe" + + steps: + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + name: ${{ env.PROJECT_NAME }}-windows-amd64 + + - name: Save Win signing certificate to file + run: echo "${{ secrets.INSTALLER_CERT_WINDOWS_CER }}" | base64 --decode > ${{ env.INSTALLER_CERT_WINDOWS_CER}} + + - name: Extract build + run: | + ${{ env.SEVENZ_PATH }} x ${{ env.RELEASE_NAME }}.zip -aoa + rm ${{ env.RELEASE_NAME }}.zip + + - name: Sign executable + env: + CERT_PASSWORD: ${{ secrets.INSTALLER_CERT_WINDOWS_PASSWORD }} + CONTAINER_NAME: ${{ secrets.INSTALLER_CERT_WINDOWS_CONTAINER }} + # https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing-with-safenet-etoken + run: | + "${{ env.SIGNTOOL_PATH }}" sign -d "Arduino Flasher CLI" -f ${{ env.INSTALLER_CERT_WINDOWS_CER}} -csp "eToken Base Cryptographic Provider" -k "[{{${{ env.CERT_PASSWORD }}}}]=${{ env.CONTAINER_NAME }}" -fd sha256 -tr http://timestamp.digicert.com -td SHA256 -v "arduino-flasher-cli.exe" + + - name: Prepare Build Artifacts + run: | + ${{ env.SEVENZ_PATH }} a -tzip ${{ env.RELEASE_NAME }}.zip arduino-flasher-cli.exe LICENSE + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PROJECT_NAME }}-windows-amd64 + path: ${{ env.RELEASE_NAME }}.zip + if-no-files-found: error + overwrite: true + + # This step is needed because the self hosted runner does not delete files automatically + - name: Cleanup + run: rm ${{ env.RELEASE_NAME }}.zip LICENSE arduino-flasher-cli.exe + + notarize-macos: + name: Notarize macOS + runs-on: macos-15 + needs: build + permissions: + contents: read + + env: + GON_CONFIG_PATH: gon.config.hcl + + strategy: + matrix: + build: [darwin-amd64, darwin-arm64] + steps: + - name: Set environment variables + run: | + VERSION="${GITHUB_REF##*/}" + echo "PACKAGE_FILENAME=${{ env.PROJECT_NAME }}-${VERSION}-${{ matrix.build }}.tar.gz" >>$GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + name: ${{ env.PROJECT_NAME }}-${{ matrix.build }} + path: ${{ env.DIST_DIR }} + + - name: Extract build + working-directory: ${{ env.DIST_DIR }} + run: | + tar -xvf ${{ env.PACKAGE_FILENAME }} + + - name: Import Code-Signing Certificates + env: + KEYCHAIN: "sign.keychain" + INSTALLER_CERT_MAC_PATH: "/tmp/ArduinoCerts2020.p12" + # Arbitrary password for a keychain that exists only for the duration of the job, so not secret + KEYCHAIN_PASSWORD: keychainpassword + run: | + echo "${{ secrets.INSTALLER_CERT_MAC_P12 }}" | base64 --decode >"${{ env.INSTALLER_CERT_MAC_PATH }}" + + security create-keychain \ + -p "${{ env.KEYCHAIN_PASSWORD }}" \ + "${{ env.KEYCHAIN }}" + + security default-keychain \ + -s "${{ env.KEYCHAIN }}" + + security unlock-keychain \ + -p "${{ env.KEYCHAIN_PASSWORD }}" \ + "${{ env.KEYCHAIN }}" + + security import \ + "${{ env.INSTALLER_CERT_MAC_PATH }}" \ + -k "${{ env.KEYCHAIN }}" \ + -f pkcs12 \ + -A \ + -T "/usr/bin/codesign" \ + -P "${{ secrets.INSTALLER_CERT_MAC_PASSWORD }}" + + security set-key-partition-list \ + -S apple-tool:,apple: \ + -s \ + -k "${{ env.KEYCHAIN_PASSWORD }}" \ + "${{ env.KEYCHAIN }}" + + - name: Install gon for code signing and app notarization + run: | + wget \ + -q https://github.com/Bearer/gon/releases/download/v0.0.27/gon_macos.zip + + unzip \ + gon_macos.zip \ + -d /usr/local/bin + + - name: Write gon config to file + # gon does not allow env variables in config file (https://github.com/mitchellh/gon/issues/20) + run: | + cat >"${{ env.GON_CONFIG_PATH }}" \ + <