name: Build macOS

on:
  workflow_call:
    inputs:
      codesign_and_notarize:
        description: 'Sign and notarize code'
        required: false
        default: true
        type: boolean
      edgar_ref:
        description: 'Arelle/EDGAR branch, tag or SHA to checkout (blank for default)'
        required: false
        type: string
      macos_version:
        default: '15'
        description: 'MacOS version to use (15, 14, 13, latest, etc.)'
        required: false
        type: string
      python_version:
        default: '3.13.8'
        description: 'Python version to use'
        required: false
        type: string
      python_architecture:
        default: 'arm64'
        description: 'Python architecture to use (x64, arm64)'
        required: false
        type: string
      xule_ref:
        description: 'xbrlus/xule branch, tag or SHA to checkout (blank for default)'
        required: false
        type: string
      create_dmg_attempt_limit:
        description: 'Number of attempts to create the DMG (workaround for macos-13 runner bug)'
        required: false
        default: 10
        type: number
    outputs:
      artifact_versioned_name:
        description: 'The file name of the DMG including the version, e.g. "arelle-macos-1.0.0.dmg".'
        value: ${{ jobs.build-macos.outputs.artifact_versioned_name }}
      uploaded_artifact_name:
        description: 'The name of the GitHub Actions artifact for the DMG.'
        value: ${{ jobs.build-macos.outputs.uploaded_artifact_name }}
  workflow_dispatch:
    inputs:
      codesign_and_notarize:
        description: 'Sign and notarize code'
        required: false
        default: true
        type: boolean
      edgar_ref:
        description: 'Arelle/EDGAR branch, tag or SHA to checkout (blank for default)'
        required: false
        type: string
      macos_version:
        default: '15'
        description: 'MacOS version to use (15, 14, 13, latest, etc.)'
        required: true
        type: string
      python_version:
        default: '3.13.8'
        description: 'Python version to use'
        required: true
        type: string
      python_architecture:
        default: 'arm64'
        description: 'Python architecture to use (x64, arm64)'
        required: true
        type: string
      xule_ref:
        description: 'xbrlus/xule branch, tag or SHA to checkout (blank for default)'
        required: false
        type: string
      create_dmg_attempt_limit:
        description: 'Number of attempts to create the DMG (workaround for macos-13 runner bug)'
        required: true
        default: 10
        type: number

permissions: {}

jobs:
  build-macos:
    environment: release
    runs-on: macos-${{ inputs.macos_version }}
    outputs:
      artifact_versioned_name: ${{ steps.define-artifact-names.outputs.artifact_versioned_name }}
      uploaded_artifact_name: ${{ steps.define-artifact-names.outputs.uploaded_artifact_name }}

    steps:
      - name: Checkout arelle
        uses: actions/checkout@v5.0.0
        with:
          fetch-depth: 0
      - name: Install Python
        uses: actions/setup-python@v6.0.0
        with:
          cache: 'pip'
          check-latest: true
          python-version: ${{ inputs.python_version }}
          architecture: ${{ inputs.python_architecture }}
      - run: |
          python -m pip install --upgrade pip setuptools wheel
          pip install -r requirements-build.txt
      - uses: actions/checkout@v5.0.0
        with:
          repository: Arelle/EDGAR
          path: arelle/plugin/EDGAR
          ref: ${{ inputs.edgar_ref }}
      - run: rm -rf arelle/plugin/EDGAR/.git
      - uses: actions/checkout@v5.0.0
        with:
          repository: xbrlus/xule
          path: tmp
          ref: ${{ inputs.xule_ref }}
      - run: |
          mv tmp/plugin/validate/* arelle/plugin/validate/
          rm -rf tmp/plugin/validate
          mv tmp/plugin/* arelle/plugin/
          rm -rf tmp
      - name: Capture build version
        run: echo "BUILD_VERSION=$(python -W ignore distro.py --version)" >> $GITHUB_ENV
      - name: Define artifact names
        id: define-artifact-names
        run: |
          echo "artifact_versioned_name=arelle-macos-${{ inputs.python_architecture }}-${{ env.BUILD_VERSION }}.dmg" >> $GITHUB_OUTPUT
          echo "uploaded_artifact_name=macos ${{ inputs.python_architecture }} distribution" >> $GITHUB_OUTPUT
          echo "DMG_BUILD_ARTIFACT_PATH=dist_dmg/arelle-macos-${{ inputs.python_architecture }}-${{ env.BUILD_VERSION }}.dmg" >> $GITHUB_ENV
      - name: Install certificate
        if: ${{ inputs.codesign_and_notarize }}
        env:
          MAC_BUILD_CERTIFICATE_BASE64: ${{ secrets.MAC_BUILD_CERTIFICATE_BASE64 }}
          MAC_BUILD_CERTIFICATE_NAME: 'Developer ID Application: Workiva Inc. (5ZF66U48UD)'
          MAC_BUILD_CERTIFICATE_PASSWORD: ${{ secrets.MAC_BUILD_CERTIFICATE_PASSWORD }}
          MAC_BUILD_KEYCHAIN_PASSWORD: 'LOCAL_KEYCHAIN_PASS'
        run: |
          # Decode and write base64 p12 certificate to file.
          echo $MAC_BUILD_CERTIFICATE_BASE64 | base64 --decode > certificate.p12

          # Create local keychain
          security create-keychain -p "$MAC_BUILD_KEYCHAIN_PASSWORD" build.keychain -v
          security set-keychain-settings -lut 21600 build.keychain
          security default-keychain -s build.keychain

          # Import certificate into keychain
          security unlock-keychain -p "$MAC_BUILD_KEYCHAIN_PASSWORD" build.keychain
          security import certificate.p12 -k build.keychain -T /usr/bin/codesign -P "$MAC_BUILD_CERTIFICATE_PASSWORD"
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MAC_BUILD_KEYCHAIN_PASSWORD" build.keychain

          # Confirm certificate
          security find-identity
          security find-certificate -a -p | openssl x509 -subject -noout
          echo MAC_BUILD_CERTIFICATE_NAME="$MAC_BUILD_CERTIFICATE_NAME"
          echo "CODESIGN_IDENTITY=${{ env.MAC_BUILD_CERTIFICATE_NAME }}" >> $GITHUB_ENV
      - name: Build app
        run: ./scripts/buildMacDist.sh
      - name: Remove git directories
        run: find build/Arelle.app/Contents -name .git -exec rm -fR {} \;
      - name: Notarize app
        if: ${{ inputs.codesign_and_notarize }}
        env:
          USERNAME: ${{ secrets.MAC_BUILD_NOTARIZATION_USERNAME }}
          PASSWORD: ${{ secrets.MAC_BUILD_NOTARIZATION_PASSWORD }}
          TEAM_ID: '5ZF66U48UD'
        run: |
          ditto -c -k --keepParent "build/Arelle.app" "notarization.zip"
          xcrun notarytool submit "notarization.zip" -v --apple-id "$USERNAME" --password "$PASSWORD" --team-id "$TEAM_ID" --wait --timeout 30m --output-format json
          xcrun stapler staple -v "build/Arelle.app"
      - name: Build DMG
        run: |
          pwd
          mkdir dist_dmg
          SIZE=`du -ms | awk '{print int($1 + 20 + 0.5)}'`
          ls build
          # Retry used to workaround macos-13 runner bug. Can be removed when bug is fixed or we update to macos-14:
          # https://github.com/actions/runner-images/issues/7522
          for i in {1..${{ inputs.create_dmg_attempt_limit }}}; do
            if hdiutil create -ov -srcfolder build/Arelle.app -volname Arelle -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M dist_dmg/arelle_tmp.dmg; then
              echo "Successfully created DMG on attempt $i"
              break
            elif [[ $i -eq ${{ inputs.create_dmg_attempt_limit }} ]]; then
              echo "Failed to create DMG after $i attempts"
              exit 1
            else
              echo "Failed to create DMG on attempt $i, retrying"
            fi
          done
          echo "Created DMG: arelle_tmp.dmg"
          DEVICE=$(hdiutil attach -readwrite -noverify dist_dmg/arelle_tmp.dmg | egrep '^/dev/' | sed 1q | awk '{print $1}')
          sleep 2
          pushd /Volumes/Arelle
          ln -s /Applications
          popd
          mkdir /Volumes/Arelle/.background
          cp arelle/images/dmg_background.png /Volumes/Arelle/.background/
          echo '
             tell application "Finder"
               tell disk "Arelle"
                     open
                     set current view of container window to icon view
                     set toolbar visible of container window to false
                     set statusbar visible of container window to false
                     set the bounds of container window to {400, 100, 920, 440}
                     set viewOptions to the icon view options of container window
                     set arrangement of viewOptions to not arranged
                     set icon size of viewOptions to 72
                     set background picture of viewOptions to file ".background:dmg_background.png"
                     set position of item ".background" of container window to {999,999}
                     set position of item ".DS_Store" of container window to {999,1099}
                     set position of item ".fseventsd" of container window to {999,1299}
                     set position of item "Arelle.app" of container window to {150, 70}
                     set position of item "Applications" of container window to {260, 240}
                     close
                     open
                     update without registering applications
                     delay 2
               end tell
             end tell
          ' | osascript
          sync
          hdiutil detach "${DEVICE}"
          hdiutil convert dist_dmg/arelle_tmp.dmg -format UDZO -imagekey zlib-level=9 -o ${{ env.DMG_BUILD_ARTIFACT_PATH }}
      - name: Upload build artifact
        uses: actions/upload-artifact@v4.6.2
        with:
          name: ${{ steps.define-artifact-names.outputs.uploaded_artifact_name }}
          if-no-files-found: error
          path: ${{ env.DMG_BUILD_ARTIFACT_PATH }}
      - name: "[Test] Test build artifact"
        run : |
          pip install -r requirements-test.txt
          pytest -s --disable-warnings --all-frozen-builds --download-cache --offline --arelle="build/Arelle.app/Contents/MacOS/arelleCmdLine" tests/integration_tests/scripts/test_scripts.py
      - name: "[Test] Upload test artifacts"
        if: always()
        uses: actions/upload-artifact@v4.6.2
        with:
          name: 'macos ${{ inputs.python_architecture }} test logs'
          if-no-files-found: error
          include-hidden-files: true
          path: '.test/**/*.logfile.xml'
