version: 2.1
workflows:
  macos:
    jobs:
      - build_for_macos:
          context: scratch-desktop-and-link
  windows:
    jobs:
      - build_for_windows:
          context: scratch-desktop-and-link
orbs:
  windows: circleci/windows@2.4.0
aliases:
  # condition to indicate whether or not we should sign this build
  - &should_sign
    or:
      - equal: [ develop, << pipeline.git.branch >> ]
      - equal: [ main, << pipeline.git.branch >> ]
      - equal: [ master, << pipeline.git.branch >> ]
  # clear large environment variables from the "scratch-desktop-and-link" context
  # this helps when a program (like NPM) encounters errors with a large environment
  - &clear_context
      CSC_MACOS: ""
      MAC_PROVISION_PROFILE: ""
      SDM_CERT: ""
      SDM_CERT_CA_BUNDLE: ""
      SDM_CERT_KEY: ""
      WIN_CSC_LINK: ""
jobs:
  build_for_macos:
    macos:
      # CircleCI's Xcode 11.1.0 image is the last of their images to be based on macOS 10.14
      # I've had trouble building for earlier versions of macOS on Catalina but it's unclear whether that was due to
      # Catalina or the version of Xcode. We should investigate this further.
      xcode: 11.1.0
    steps:
      - checkout
      - npm_install:
          npmCacheDir: ~/.npm
      - run:
          name: Test
          command: npm run test
      - when:
          condition:
            *should_sign
          steps:
            - run:
                name: Import CI context
                command: |
                  set -e
                  function decodeToFile () {
                    if [ -z "$1" ]; then
                      echo "Missing or invalid filename"
                      return 1
                    fi
                    if [ -z "$2" ]; then
                      echo "Missing environment variable contents for file: $1"
                      return 2
                    fi
                    echo "$2" | base64 --decode > "$1"
                  }
                  decodeToFile embedded.provisionprofile "${MAC_PROVISION_PROFILE}"
                  decodeToFile mas-dev.provisionprofile "${MAC_DEV_PROVISION_PROFILE}"
                  decodeToFile macos-certs-scratch-foundation.p12.gz "${CSC_MACOS_GZ}"
                  decodeToFile apple-dev-cert.p12 "${MAC_DEV_CERT}"
                  gunzip macos-certs-scratch-foundation.p12.gz
                  security -v create-keychain -p circleci circleci.keychain
                  security -v default-keychain -s circleci.keychain
                  security -v import macos-certs-scratch-foundation.p12 -k circleci.keychain -P "${CSC_MACOS_PASSWORD}" -T /usr/bin/codesign -T /usr/bin/productbuild
                  security -v import apple-dev-cert.p12 -k circleci.keychain -P "${MAC_DEV_CERT_PASSWORD}" -T /usr/bin/codesign -T /usr/bin/productbuild
                  security -v unlock-keychain -p circleci circleci.keychain
                  # "set-key-partition-list" prints extensive not-so-useful output and adding "-q" (even multiple times) doesn't suppress it.
                  # The "grep -v" at the end of this line suppresses all of that so any errors or warnings might be more visible.
                  security -v set-key-partition-list -S apple-tool:,apple:,codesign: -s -k circleci circleci.keychain | grep -v '^    0x'
                  security -v set-keychain-settings -lut 600 circleci.keychain
                  security -v find-identity circleci.keychain
                  rm macos-certs-scratch-foundation.p12 apple-dev-cert.p12
      - restore_cache:
          # Caching Homebrew's files (see the save_cache step below) means that Homebrew doesn't have to update as
          # much. The Homebrew update can take several minutes without this, but with the cache it tends to take less
          # than a minute most of the time. The cache will expire periodically and be replaced by a more up-to-date
          # cache, which should effectively cap the amount of updating that Homebrew needs to do on top of the cache.
          name: Restore Homebrew cache
          key: homebrew-cache-v1
      - run:
          name: Work around electron-userland/electron-builder#4964
          command: |
            brew install go go-bindata
            git -C ~ clone https://github.com/develar/app-builder.git
            git -C ~/app-builder checkout b85740334fec875f5dd8dcd22eb1f729599109db
            make --directory=~/app-builder build
            ln -sfv ~/app-builder/dist/app-builder_darwin_amd64/app-builder ./node_modules/app-builder-bin/mac/
      - run:
          name: Upgrade to Node 14
          command: brew install node@14
      - save_cache:
          name: Save Homebrew cache
          paths:
            - ~/Library/Caches/Homebrew
            - /usr/local/Homebrew
          key: homebrew-cache-v1
      - build
      - run:
          name: Move DMG to artifacts directory
          command: |
            mkdir -p Artifacts/
            mv -v dist/Scratch*.dmg Artifacts/
      - when:
          condition:
            *should_sign
          steps:
            - run:
                name: Zip MAS-Dev to artifacts directory
                # If you use `zip` for this it will throw away some metadata (resource forks?) and
                # the app will crash on startup with "EXC_CRASH (Code Signature Invalid)".
                # To preserve that metadata, use `ditto` instead.
                # See also: https://stackoverflow.com/a/22370486
                command: |
                  NPM_APP_VERSION="`node -pe "require('./package.json').version"`"
                  cd dist/mas-dev
                  ditto -v -c -k --sequesterRsrc --keepParent --zlibCompressionLevel 9 \
                    Scratch*.app ../../Artifacts/mas-dev-${NPM_APP_VERSION}.zip
            - run:
                name: Move PKG to artifacts directory
                command: |
                  mv -v dist/mas/Scratch*.pkg Artifacts/
      - store_artifacts:
          path: Artifacts/
  build_for_windows:
    executor: windows/default
    steps:
      - run:
          # work around https://github.com/appveyor/ci/issues/2420 which seems to affect CircleCI too
          # see also https://circleci.com/docs/2.0/env-vars/#using-parameters-and-bash-environment
          name: Work around git-sh-setup issue
          shell: bash
          command: |
            echo 'Adding libexec/git-core to PATH...'
            echo 'For more details see https://github.com/appveyor/ci/issues/2420'
            echo 'export PATH="$PATH:/c/Program Files/Git/mingw64/libexec/git-core"' >> $BASH_ENV
      - run:
          # nvm for Windows doesn't accept partial version numbers, so specify exact :(
          name: Upgrade to Node 14.17.0
          command: |
            nvm install 14.17.0
            nvm use 14.17.0
      - checkout
      - npm_install:
          npmCacheDir: "C:/Users/circleci/AppData/Roaming/npm-cache"
      - run:
          name: Test
          command: npm run test
          environment: *clear_context
      - when:
          condition:
            *should_sign
          steps:
            - run:
                name: Import CI context
                shell: bash
                command: |
                  set -e
                  function decodeToFile () {
                    if [ -z "$1" ]; then
                      echo "Missing or invalid filename"
                      return 1
                    fi
                    if [ -z "$2" ]; then
                      echo "Missing environment variable contents for file: $1"
                      return 2
                    fi
                    echo "$2" | base64 --decode > "$1"
                  }
                  decodeToFile ~/codesign.pfx "${WIN_CSC_LINK}"
            - run:
                # In theory this should be unnecessary: the electron-builder documentation says that WIN_CSC_LINK can
                # be a base64-encoded certificate, which is what's in the CI context. In practice that leads to an
                # signtool.exe finding the certificate but not the key, for reasons I haven't been able to understand.
                # Also, because of the non-standard user configuration on CircleCI's Windows VM, attempting to import
                # a certificate into the user's certificate store ("Cert:/LocalUser/My") will fail. Instead, this code
                # imports the PFX into the machine certificate store. That usually requires Administrator permissions,
                # but on CircleCI's setup it works just fine. See also: https://github.com/ShabadOS/desktop/issues/265
                # and https://github.com/ShabadOS/desktop/pull/266
                name: Add CSC to machine store
                shell: powershell
                command: |
                  $securePassword = (ConvertTo-SecureString -String $env:WIN_CSC_KEY_PASSWORD -AsPlainText -Force)
                  Import-PfxCertificate -FilePath ~/codesign.pfx -Password $securePassword -CertStoreLocation "Cert:/LocalMachine/My"
      - build
      - run:
          name: Move Windows build products to artifacts directory
          shell: bash
          command: |
            mkdir -p Artifacts/
            mv dist/{Scratch*.appx,Scratch*.exe} Artifacts/
      - store_artifacts:
          path: Artifacts/
commands:
  npm_install:
    description: Run 'npm install' with caching
    parameters:
      npmCacheDir:
        type: string
        description: NPM cache directory (`npm config cache get`) - usually either ~/.npm or %AppData%/npm-cache
    steps:
      - restore_cache:
          keys:
            - npm-cache-{{ arch }}-{{ checksum "package-lock.json" }}
      - run:
          name: Install node_modules
          shell: bash # harmless on macOS, required on Windows to work around git-sh-setup issue
          # --prefer-offline "will make npm skip any conditional requests (304 checks) for stale cache data, and only
          #   hit the network if something is missing from the cache"
          command: npm ci --prefer-offline
          environment: *clear_context
      - save_cache:
          paths:
            - << parameters.npmCacheDir >>
          key: npm-cache-{{ arch }}-{{ checksum "package-lock.json" }}
  build:
    steps:
      - when:
          condition:
            *should_sign
          steps:
            - run:
                command: npm run dist
                no_output_timeout: 30m # macOS notarization can take longer than the default 10 minutes
                environment:
                  # blank big variables to avoid crash on Windows
                  <<: *clear_context
                  # let Windows know where to get the PFX (ignored on non-Windows builds)
                  WIN_CSC_LINK: ~/codesign.pfx
                  # blank CIRCLE_BUILD_NUM to work around electron-userland/electron-builder#5016
                  CIRCLE_BUILD_NUM: ""
      - unless:
          condition:
            *should_sign
          steps:
            - run:
                command: npm run distDev
                environment: *clear_context