# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/publish-go-nightly-task.md
name: Publish Nightly Build

env:
  # As defined by the Taskfile's PROJECT_NAME variable
  PROJECT_NAME: arduino-cli
  # As defined by the Taskfile's DIST_DIR variable
  DIST_DIR: dist
  # The project's folder on Arduino's download server for uploading builds
  AWS_PLUGIN_TARGET: /arduino-cli/
  AWS_REGION: "us-east-1"
  ARTIFACT_NAME: dist

# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
on:
  schedule:
    # run every day at 1AM
    - cron: "0 1 * * *"
  workflow_dispatch:
  repository_dispatch:

permissions:
  id-token: write # This is required for requesting the JWT
  contents: read # This is required for actions/checkout

jobs:
  create-nightly-artifacts:
    outputs:
      version: ${{ steps.get-version.outputs.version }}
    runs-on: ubuntu-latest

    strategy:
      matrix:
        os:
          - Windows_32bit
          - Windows_64bit
          - Linux_32bit
          - Linux_64bit
          - Linux_ARMv6
          - Linux_ARMv7
          - Linux_ARM64
          - macOS_64bit
          - macOS_ARM64
          - jsonschema

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Task
        uses: arduino/setup-task@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          version: 3.x

      - name: Build
        env:
          NIGHTLY: true
        run: task dist:${{ matrix.os }}

      - name: Output Version
        id: get-version
        env:
          NIGHTLY: true
        run: echo "version=$(task general:get-version)" >> $GITHUB_OUTPUT

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          if-no-files-found: error
          name: ${{ env.ARTIFACT_NAME }}-${{ matrix.os }}
          path: ${{ env.DIST_DIR }}

  notarize-macos:
    name: Notarize ${{ matrix.artifact.name }}
    runs-on: macos-latest
    needs: create-nightly-artifacts

    env:
      GON_CONFIG_PATH: gon.config.hcl

    strategy:
      matrix:
        artifact:
          - artifact-suffix: macOS_64bit
            name: darwin_amd64
            path: "macOS_64bit.tar.gz"
          - artifact-suffix: macOS_ARM64
            name: darwin_arm64
            path: "macOS_ARM64.tar.gz"

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.ARTIFACT_NAME }}-${{ matrix.artifact.artifact-suffix }}
          path: ${{ env.DIST_DIR }}

      - name: Remove non-notarized artifact
        uses: geekyeggo/delete-artifact@v5
        with:
          name: ${{ env.ARTIFACT_NAME }}-${{ matrix.artifact.artifact-suffix }}

      - name: Import Code-Signing Certificates
        env:
          KEYCHAIN: "sign.keychain"
          INSTALLER_CERT_MAC_PATH: "/tmp/ArduinoCerts2020.p12"
          KEYCHAIN_PASSWORD: keychainpassword # Arbitrary password for a keychain that exists only for the duration of the job, so not secret
        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 }}" <<EOF
          # See: https://github.com/Bearer/gon#configuration-file
          source = ["dist/arduino-cli_osx_${{ matrix.artifact.name }}/arduino-cli"]
          bundle_id = "cc.arduino.arduino-cli"

          sign {
            application_identity = "Developer ID Application: ARDUINO SA (7KT7ZWMCJT)"
          }

          # Ask Gon for zip output to force notarization process to take place.
          # The CI will ignore the zip output, using the signed binary only.
          zip {
            output_path = "unused.zip"
          }
          EOF

      - name: Sign and notarize binary
        env:
          AC_USERNAME: ${{ secrets.AC_USERNAME }}
          AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
          AC_PROVIDER: ${{ secrets.AC_PROVIDER }}
        run: |
          gon "${{ env.GON_CONFIG_PATH }}"

      - name: Re-package binary
        working-directory: ${{ env.DIST_DIR }}
        # Repackage the signed binary replaced in place by Gon (ignoring the output zip file)
        run: |
          # GitHub's upload/download-artifact actions don't preserve file permissions,
          # so we need to add execution permission back until the action is made to do this.
          chmod +x "${{ env.PROJECT_NAME }}_osx_${{ matrix.artifact.name }}/${{ env.PROJECT_NAME }}"
          VERSION=${{ needs.create-nightly-artifacts.outputs.version }}
          PACKAGE_FILENAME=(${{ env.PROJECT_NAME }}_${VERSION}_${{ matrix.artifact.path }})
          tar -czvf "$PACKAGE_FILENAME" \
          -C "${{ env.PROJECT_NAME }}_osx_${{ matrix.artifact.name }}/" "${{ env.PROJECT_NAME }}" \
          -C ../../ LICENSE.txt
          echo "PACKAGE_FILENAME=$PACKAGE_FILENAME" >> $GITHUB_ENV

      - name: Upload notarized artifact
        uses: actions/upload-artifact@v4
        with:
          if-no-files-found: error
          name: ${{ env.ARTIFACT_NAME }}-${{ matrix.artifact.artifact-suffix }}
          path: ${{ env.DIST_DIR }}/${{ env.PACKAGE_FILENAME }}

  create-windows-installer:
    runs-on: windows-sign-pc
    needs: create-nightly-artifacts

    defaults:
      run:
        shell: bash

    env:
      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"

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          pattern: ${{ env.ARTIFACT_NAME }}-*
          merge-multiple: true
          path: ${{ env.DIST_DIR }}

      - name: Prepare PATH
        uses: microsoft/setup-msbuild@v2

      - name: Build MSI
        id: buildmsi
        run: |
          VERSION=${{ needs.create-nightly-artifacts.outputs.version }}
          WIX_VERSION="0.0.0" # use 0.0.0 as version for the installer since wix is picky about other alternatives, this only affects nightly builds (error CNDL0242)
          PACKAGE_FILENAME="${{ env.PROJECT_NAME }}_${VERSION}_Windows_64bit"
          SOURCE_DIR="${GITHUB_WORKSPACE}/${{ env.DIST_DIR }}/${{ env.PROJECT_NAME }}_windows_amd64/"
          MSBuild.exe ./installer/cli.wixproj -p:SourceDir="$SOURCE_DIR" -p:OutputPath="${GITHUB_WORKSPACE}/${{ env.DIST_DIR }}" -p:OutputName="$PACKAGE_FILENAME" -p:ProductVersion="$WIX_VERSION"

      - name: Save Win signing certificate to file
        run: echo "${{ secrets.INSTALLER_CERT_WINDOWS_CER }}" | base64 --decode > ${{ env.INSTALLER_CERT_WINDOWS_CER}}

      - name: Sign MSI
        env:
          MSI_FILE: ${{ steps.buildmsi.outputs.msi }} # this comes from .installer/cli.wixproj
          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 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 "${{ env.MSI_FILE }}"

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        env:
          MSI_FILE: ${{ steps.buildmsi.outputs.msi }}
        with:
          if-no-files-found: error
          name: ${{ env.ARTIFACT_NAME }}-windows-installer
          path: ${{ env.MSI_FILE }}

  publish-nightly:
    runs-on: ubuntu-latest
    environment: production
    needs:
      - create-nightly-artifacts
      - notarize-macos
      - create-windows-installer
    permissions:
      contents: write
      id-token: write # This is required for requesting the JWT

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          pattern: ${{ env.ARTIFACT_NAME }}-*
          merge-multiple: true
          path: ${{ env.DIST_DIR }}

      - name: Install Task
        uses: arduino/setup-task@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          version: 3.x

      - name: Collect proto files
        env:
          NIGHTLY: true
        run: task protoc:collect

      - name: Create checksum file
        working-directory: ${{ env.DIST_DIR}}
        run: |
          VERSION=${{ needs.create-nightly-artifacts.outputs.version }}
          sha256sum ${{ env.PROJECT_NAME }}_${VERSION}* > ${VERSION}-checksums.txt

      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
          role-session-name: "github_${{ env.PROJECT_NAME }}"
          aws-region: ${{ env.AWS_REGION }}

      - name: Upload release files on Arduino downloads servers
        run: aws s3 sync ${{ env.DIST_DIR }} s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.AWS_PLUGIN_TARGET }}nightly

  report:
    runs-on: ubuntu-latest
    needs: publish-nightly
    if: failure() # Run if publish-nightly or any of its job dependencies failed

    steps:
      - name: Report failure
        uses: masci/datadog@v1
        with:
          api-key: ${{ secrets.DD_API_KEY }}
          events: |
            - title: "${{ env.PROJECT_NAME }} nightly build failed"
              text: "Nightly build workflow has failed"
              alert_type: "error"
              host: ${{ github.repository }}
              tags:
                - "project:${{ env.PROJECT_NAME }}"
                - "workflow:${{ github.workflow }}"
