# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-certificates.md
name: Check Certificates

# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
on:
  push:
    paths:
      - '.github/workflows/check-certificates.ya?ml'
  pull_request:
    paths:
      - '.github/workflows/check-certificates.ya?ml'
  schedule:
    # Run every 10 hours.
    - cron: '0 */10 * * *'
  workflow_dispatch:
  repository_dispatch:

env:
  # Begin notifications when there are less than this many days remaining before expiration.
  EXPIRATION_WARNING_PERIOD: 30

jobs:
  check-certificates:
    name: ${{ matrix.certificate.identifier }}
    # Only run when the workflow will have access to the certificate secrets.
    if: >
      (github.event_name != 'pull_request' && github.repository == 'arduino/arduino-ide') ||
      (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'arduino/arduino-ide')
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false

      matrix:
        certificate:
          # Additional certificate definitions can be added to this list.
          - identifier: macOS signing certificate # Text used to identify certificate in notifications.
            certificate-secret: APPLE_SIGNING_CERTIFICATE_P12 # Name of the secret that contains the certificate.
            password-secret: KEYCHAIN_PASSWORD # Name of the secret that contains the certificate password.
          - identifier: Windows signing certificate
            certificate-secret: WINDOWS_SIGNING_CERTIFICATE_PFX
            password-secret: WINDOWS_SIGNING_CERTIFICATE_PASSWORD

    steps:
      - name: Set certificate path environment variable
        run: |
          # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
          echo "CERTIFICATE_PATH=${{ runner.temp }}/certificate.p12" >> "$GITHUB_ENV"

      - name: Decode certificate
        env:
          CERTIFICATE: ${{ secrets[matrix.certificate.certificate-secret] }}
        run: |
          echo "${{ env.CERTIFICATE }}" | base64 --decode > "${{ env.CERTIFICATE_PATH }}"

      - name: Verify certificate
        env:
          CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
        run: |
          (
            openssl pkcs12 \
              -in "${{ env.CERTIFICATE_PATH }}" \
              -legacy \
              -noout \
              -passin env:CERTIFICATE_PASSWORD
          ) || (
            echo "::error::Verification of ${{ matrix.certificate.identifier }} failed!!!"
            exit 1
          )

      - name: Slack notification of certificate verification failure
        if: failure()
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          SLACK_MESSAGE: |
            :warning::warning::warning::warning:
            WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} verification failed!!!
            :warning::warning::warning::warning:
          SLACK_COLOR: danger
          MSG_MINIMAL: true
        uses: rtCamp/action-slack-notify@v2

      - name: Get days remaining before certificate expiration date
        env:
          CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
        id: get-days-before-expiration
        run: |
          EXPIRATION_DATE="$(
            (
              openssl pkcs12 \
                -in "${{ env.CERTIFICATE_PATH }}" \
                -clcerts \
                -legacy \
                -nodes \
                -passin env:CERTIFICATE_PASSWORD
            ) | (
              openssl x509 \
                -noout \
                -enddate
            ) | (
              grep \
                --max-count=1 \
                --only-matching \
                --perl-regexp \
                'notAfter=(\K.*)'
            )
          )"

          DAYS_BEFORE_EXPIRATION="$((($(date --utc --date="$EXPIRATION_DATE" +%s) - $(date --utc +%s)) / 60 / 60 / 24))"

          # Display the expiration information in the log.
          echo "Certificate expiration date: $EXPIRATION_DATE"
          echo "Days remaining before expiration: $DAYS_BEFORE_EXPIRATION"

          echo "days=$DAYS_BEFORE_EXPIRATION" >> $GITHUB_OUTPUT

      - name: Check if expiration notification period has been reached
        id: check-expiration
        run: |
          if [[ ${{ steps.get-days-before-expiration.outputs.days }} -lt ${{ env.EXPIRATION_WARNING_PERIOD }} ]]; then
            echo "::error::${{ matrix.certificate.identifier }} will expire in ${{ steps.get-days-before-expiration.outputs.days }} days!!!"
            exit 1
          fi

      - name: Slack notification of pending certificate expiration
        # Don't send spurious expiration notification if verification fails.
        if: failure() && steps.check-expiration.outcome == 'failure'
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          SLACK_MESSAGE: |
            :warning::warning::warning::warning:
            WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} will expire in ${{ steps.get-days-before-expiration.outputs.days }} days!!!
            :warning::warning::warning::warning:
          SLACK_COLOR: danger
          MSG_MINIMAL: true
        uses: rtCamp/action-slack-notify@v2
