version: 2.1

orbs:
  macos: circleci/macos@2.5.1
  
jobs:
  openscad-mxe-64bit:
    working_directory: ~/workspace
    docker:
      - image: openscad/mxe-x86_64-gui:latest
    resource_class: large
    steps:
      - checkout
      - run:
          name: Build OpenSCAD Windows Application (64bit)
          no_output_timeout: 18000
          command: |
              export NUMCPU=4
              export LC_ALL=C.UTF-8
              export MXEDIR=/mxe
              export PKG_CONFIG_PATH=/mxe/usr/x86_64-w64-mingw32.static.posix/lib/pkgconfig
              ln -sf /usr/bin/python{3,}
              if [ x"${CIRCLE_BRANCH}" = xmaster ]; then export SUFFIX=""; else export SUFFIX="_$(echo ${CIRCLE_BRANCH} | sed -e 's,pull/,PR,; s,/head,h,; s,/merge,m,; s,/,.,g')"; fi
              export OPENSCAD_VERSION="$(date +%Y.%m.%d).ci${CIRCLE_BUILD_NUM}"
              
              ./scripts/release-common.sh snapshot mingw64 -v "$OPENSCAD_VERSION"
              mkdir -p /tmp/out
              for f in mingw*/*.zip mingw*/*.exe; do N=$(basename "$f" | sed -e "s/\\(-x86-[36][24]\\)/\\1${SUFFIX}/;"); cp -iv "$f" /tmp/out/"$N"; done
              if [ -n "${CODE_SIGNING_KEY}" ]; then
                  cd /tmp/out
                  echo $CODE_SIGNING_DATA | base64 -d | gzip -d | gpg --import --allow-secret-key-import --pinentry-mode loopback --passphrase-file <(echo $CODE_SIGNING_PW)
                  export GPG_ARGS="--batch --yes --passphrase-fd 0 --pinentry-mode loopback --detach-sign --armor"
                  for a in *.exe *.zip; do echo "$CODE_SIGNING_PW" | gpg -u "$CODE_SIGNING_KEY" $GPG_ARGS "$a"; sha256sum "$a" | tee "${a}.sha256"; sha512sum "$a" | tee "${a}.sha512"; done
                  rm -rf ~/.gnupg
              else
                  echo "Skipping code signing."
              fi
      - store_artifacts:
          path: /tmp/out
          destination: 64-bit
  openscad-appimage-64bit:
    working_directory: ~/workspace
    docker:
      - image: openscad/appimage-x86_64-base:latest
    resource_class: large
    steps:
      - checkout
      - run:
          name: Build OpenSCAD AppImage (64bit)
          no_output_timeout: 18000
          command: |
              set +e
              . /opt/qt5*/bin/qt5*-env.sh
              set -e
              export OPENSCAD_COMMIT=$(git log -1 --pretty=format:%h)
              export OPENSCAD_VERSION="$(date +%Y.%m.%d).ai${CIRCLE_BUILD_NUM}"
              if [ x"${CIRCLE_BRANCH}" = xmaster ]; then export SUFFIX=""; else export SUFFIX="_$(echo ${CIRCLE_BRANCH} | sed -e 's,pull/,PR,; s,/head,h,; s,/merge,m,; s,/,.,g')"; fi
              git submodule update --init --recursive
              mkdir build && cd build
              cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DEXPERIMENTAL=ON -DSNAPSHOT=ON -DOPENSCAD_VERSION="$OPENSCAD_VERSION" -DOPENSCAD_COMMIT="$OPENSCAD_COMMIT"
              make -j4
              make install DESTDIR=../AppDir
              cd ..
              export PATH=/appimage/usr/bin:"$PATH"
              export EXTRA_QT_PLUGINS=svg
              VERSION="${OPENSCAD_VERSION}${SUFFIX}" linuxdeploy --plugin qt --output appimage --appdir AppDir
              mkdir -p /tmp/out
              cp -iv OpenSCAD-*.AppImage /tmp/out
              if [ -n "${CODE_SIGNING_KEY}" ]; then
                  cd /tmp/out
                  echo $CODE_SIGNING_DATA | base64 -d | gzip -d | gpg --import --allow-secret-key-import --pinentry-mode loopback --passphrase-file <(echo $CODE_SIGNING_PW)
                  export GPG_ARGS="--batch --yes --passphrase-fd 0 --pinentry-mode loopback --detach-sign --armor"
                  for a in *.AppImage; do echo "$CODE_SIGNING_PW" | gpg -u "$CODE_SIGNING_KEY" $GPG_ARGS "$a"; sha256sum "$a" | tee "${a}.sha256"; sha512sum "$a" | tee "${a}.sha512"; done
                  rm -rf ~/.gnupg
              else
                  echo "Skipping code signing."
              fi
      - store_artifacts:
          path: /tmp/out
          destination: 64-bit
  openscad-wasm:
    parameters:
      wasm-type:
        type: string
    working_directory: ~/workspace
    docker:
      - image: openscad/wasm-base:latest
    resource_class: large
    steps:
      - checkout
      - run:
          name: Install latest node.js
          command: |
              npm i -g n
              n latest
      - when:
          condition:
            equal: [ web, << parameters.wasm-type >> ]
          steps:
            - run:
                name: Install Puppeteer
                command: |
                    apt-get update
                    apt-get install -y \
                        libatk1.0-0 libatk-bridge2.0-0 libdrm2 libxcomposite1 libxdamage1 \
                        libxfixes3 libxrandr2 libgbm1 libxkbcommon0 libpango-1.0-0 libcairo2
                    export PATH="/usr/local/bin:$PATH"
                    npm init -y
                    npm i puppeteer
                    npx puppeteer browsers install chrome
      - run:
          name: Build OpenSCAD WASM (<< parameters.wasm-type >>)
          no_output_timeout: 18000
          command: |
              export OPENSCAD_COMMIT=$(git log -1 --pretty=format:%h)
              export OPENSCAD_VERSION="$(date +%Y.%m.%d).wasm${CIRCLE_BUILD_NUM}"
              if [ x"${CIRCLE_BRANCH}" = xmaster ]; then export SUFFIX=""; else export SUFFIX="_$(echo ${CIRCLE_BRANCH} | sed -e 's,pull/,PR,; s,/head,h,; s,/merge,m,; s,/,.,g')"; fi
              git submodule update --init --recursive
              emcmake cmake -B ../build . \
                  -DSNAPSHOT=ON \
                  -DEXPERIMENTAL=ON \
                  -DCMAKE_BUILD_TYPE=Release \
                  -DWASM_BUILD_TYPE=<< parameters.wasm-type >> \
                  -DOPENSCAD_COMMIT="$OPENSCAD_COMMIT" \
                  -DOPENSCAD_VERSION="$OPENSCAD_VERSION"
              cd ../build
              make -j4 VERBOSE=1
              mkdir -p /tmp/out
              zip -9r "/tmp/out/OpenSCAD-${OPENSCAD_VERSION}${SUFFIX}-WebAssembly-<< parameters.wasm-type >>.zip" openscad.*
              if [ -n "${CODE_SIGNING_KEY}" ]; then
                  cd /tmp/out
                  echo $CODE_SIGNING_DATA | base64 -d | gzip -d | gpg --import --allow-secret-key-import --pinentry-mode loopback --passphrase-file <(echo $CODE_SIGNING_PW)
                  export GPG_ARGS="--batch --yes --passphrase-fd 0 --pinentry-mode loopback --detach-sign --armor"
                  for a in *.zip; do echo "$CODE_SIGNING_PW" | gpg -u "$CODE_SIGNING_KEY" $GPG_ARGS "$a"; sha256sum "$a" | tee "${a}.sha256"; sha512sum "$a" | tee "${a}.sha512"; done
                  rm -rf ~/.gnupg
              else
                  echo "Skipping code signing."
              fi
      - run:
          name: Verify OpenSCAD WASM build
          command: |
              # node in the path (from emsdk) is too old to run the wasm build, use the one installed by n.
              export PATH="/usr/local/bin:$PATH"
              ./tests/wasm-check.sh << parameters.wasm-type >>
      - store_artifacts:
          path: /tmp/out
          destination: wasm
  openscad-macos:
    resource_class: macos.m1.medium.gen1
    macos:
      xcode: 15.3.0
    environment:
      OPENSCAD_LIBRARIES: /Users/distiller/libraries/install
      PKG_CONFIG_PATH: /Users/distiller/libraries/install/lib/pkgconfig
      DYLD_LIBRARY_PATH: /Users/distiller/libraries/install/lib
      DYLD_FRAMEWORK_PATH: /Users/distiller/libraries/install/lib
    steps:
      - macos/install-rosetta
      - checkout
      - run: git submodule update --init --recursive
      - run:
          name: System Info
          command: |
            system_profiler SPHardwareDataType SPSoftwareDataType SPStorageDataType SPDeveloperToolsDataType
      - run:
          name: Install Homebrew packages
          command: |
            brew update
            brew install automake libtool cmake pkg-config wget meson python-packaging
      - restore_cache:
          keys:
            # CircleCI library caching. If we ever need invalidate the cache (e.g. to remove any old files 
            # from a library cache), the safest, change the MACOS_CACHE_VERSION variable to a random value at
            # https://app.circleci.com/settings/project/github/openscad/openscad/environment-variables.
            - macos-libraries-{{ .Environment.MACOS_CACHE_VERSION }}-{{ checksum "scripts/macosx-build-dependencies.sh" }}-{{ checksum ".circleci/config.yml" }}
            # Fetch the most recently saved cache
            - macos-libraries-{{ .Environment.MACOS_CACHE_VERSION }}-
      - run:
          name: Build Dependencies
          command: |
            # Pick up our own Qt
            export PATH=$OPENSCAD_LIBRARIES/bin:$PATH
            # Build universal binaries and limit to 30 minutes
            ./scripts/macosx-build-dependencies.sh -l 30 -x -a -d
      - run:
          name: Package Dependencies as an artifact
          command: |
            mkdir -p /tmp/out
            tar cz -C "$OPENSCAD_LIBRARIES" -f /tmp/out/libraries.tar.bz2 .
            shasum -a 512 /tmp/out/libraries.tar.bz2 > /tmp/out/libraries.tar.bz2.sha512
      - save_cache:
          # Make sure to create a new cache entry, needed for incremental library builds and also
          # preventing full cache drop after 15 days.
          key: macos-libraries-{{ .Environment.MACOS_CACHE_VERSION }}-{{ checksum "scripts/macosx-build-dependencies.sh" }}-{{ checksum ".circleci/config.yml" }}-{{ .BuildNum }}
          paths:
            - /Users/distiller/libraries/install
      - run:
          name: Build OpenSCAD
          command: |
            export NUMCPU=$(($(sysctl -n hw.ncpu) * 3 / 2))
            export VERSIONDATE=$(date "+%Y.%m.%d")
            time ./scripts/release-common.sh -d $VERSIONDATE snapshot
            echo "export VERSIONDATE=$VERSIONDATE" >> $BASH_ENV
      - run:
          name: Verify OpenSCAD binary
          command: |
            ./scripts/macosx-sanity-check.py build/OpenSCAD.app/Contents/MacOS/OpenSCAD
      - run:
          name: Sign and Notarize OpenSCAD binary
          command: |
            security -v create-keychain -p "" OpenSCAD.keychain
            security -v list-keychains -s OpenSCAD.keychain
            security -v unlock-keychain -p "" OpenSCAD.keychain
            security -v set-keychain-settings -lt 7200 OpenSCAD.keychain
            echo "$APPLE_CODE_SIGNING_KEY" | base64 -D -o OpenSCAD.p12
            security -v import OpenSCAD.p12 -k OpenSCAD.keychain -f pkcs12 -P "$APPLE_CODE_SIGNING_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
            security -v set-key-partition-list -S apple-tool:,apple: -s -k "" OpenSCAD.keychain
            codesign --options=runtime --keychain OpenSCAD.keychain -s "Marius Kintel" --force --deep build/OpenSCAD.app

            hdiutil create -volname "OpenSCAD" -srcfolder build/OpenSCAD.app -ov -format UDZO "OpenSCAD-$VERSIONDATE.dmg"  
            codesign --keychain OpenSCAD.keychain -s "Marius Kintel" --force OpenSCAD-$VERSIONDATE.dmg

            xcrun notarytool submit OpenSCAD-$VERSIONDATE.dmg --wait --apple-id $APPLE_NOTARIZATION_ACCOUNT --team-id $APPLE_NOTARIZATION_TEAM --password $APPLE_NOTARIZATION_PASSWORD
            # TODO: If notarization fails, we could, download and dump the explanation here.
            xcrun stapler staple OpenSCAD-$VERSIONDATE.dmg

            security -v delete-keychain OpenSCAD.keychain
            rm OpenSCAD.p12
            cp -R OpenSCAD-$VERSIONDATE.dmg /tmp/out/
            shasum -a 256 OpenSCAD-$VERSIONDATE.dmg > OpenSCAD-$VERSIONDATE.dmg.sha256
            shasum -a 512 OpenSCAD-$VERSIONDATE.dmg > OpenSCAD-$VERSIONDATE.dmg.sha512
            cp -v OpenSCAD-$VERSIONDATE.dmg* /tmp/out/
      - store_artifacts:
          path: /tmp/out
workflows:
  version: 2
  "CircleCI master build":
    jobs:
      - openscad-mxe-64bit:
          context: secret-context
          filters:
              branches:
                  only:
                      - master
      - openscad-appimage-64bit:
          context: secret-context
          filters:
              branches:
                  only:
                      - master
      - openscad-wasm:
          matrix:
            parameters:
              wasm-type: [web, node]
          context: secret-context
          filters:
              branches:
                  only:
                      - master
      - openscad-macos:
          context: secret-context
          filters:
              branches:
                  only:
                      - master
  "CircleCI branch build":
    jobs:
      - openscad-mxe-64bit:
          filters:
              branches:
                  ignore:
                      - master
                      - coverity_scan
                      - /^(?i:continuous)$/
      - openscad-appimage-64bit:
          filters:
              branches:
                  ignore:
                      - master
                      - coverity_scan
                      - /^(?i:continuous)$/
      - openscad-wasm:
          matrix:
            parameters:
              wasm-type: [web, node]
          filters:
              branches:
                  ignore:
                      - master
                      - coverity_scan
                      - /^(?i:continuous)$/
      - openscad-macos:
          context: secret-context
          filters:
              branches:
                  only:
                      - /.*-macos/
  scheduled:
    triggers:
      - schedule:
          cron: "30 6 * * 0,1,5"
          filters:
              branches:
                  only:
                      - master
    jobs:
      - openscad-macos:
          context: secret-context
