diff --git a/.buildkite/commands/install_node_dependencies.sh b/.buildkite/commands/install_node_dependencies.sh new file mode 100755 index 000000000..5c0b79c96 --- /dev/null +++ b/.buildkite/commands/install_node_dependencies.sh @@ -0,0 +1,6 @@ +#!/bin/bash -eu + +echo "--- :npm: Install Node dependencies" +# --legacy-peer-deps is necessary because of react-monaco-editor. +# See README for more details +npm ci --legacy-peer-deps diff --git a/.buildkite/commands/package_windows.ps1 b/.buildkite/commands/package_windows.ps1 new file mode 100644 index 000000000..406ba2812 --- /dev/null +++ b/.buildkite/commands/package_windows.ps1 @@ -0,0 +1,53 @@ +# Stop script execution when a non-terminating error occurs +$ErrorActionPreference = "Stop" + +& "prepare_windows_host_for_node.ps1" + +# First try to get the env var from the process environment +$windowsCertPassword = [System.Environment]::GetEnvironmentVariable('WINDOWS_CODE_SIGNING_CERT_PASSWORD', [System.EnvironmentVariableTarget]::Process) +If ([string]::IsNullOrEmpty($windowsCertPassword)) { + # If it fails, try from the machine-wide environment + $windowsCertPassword = [System.Environment]::GetEnvironmentVariable('WINDOWS_CODE_SIGNING_CERT_PASSWORD', [System.EnvironmentVariableTarget]::Machine) +} + +If ([string]::IsNullOrEmpty($windowsCertPassword)) { + Write-Host "[!] WINDOWS_CODE_SIGNING_CERT_PASSWORD is not set in either process or machine environments." + Exit 1 +} else { + [System.Environment]::SetEnvironmentVariable('CSC_KEY_PASSWORD', $windowsCertPassword, [System.EnvironmentVariableTarget]::Machine) + Write-Host "Environment variable CSC_KEY_PASSWORD set to the value of WINDOWS_CODE_SIGNING_CERT_PASSWORD." +} + +Write-Host "--- :windows: Configure Windows code signing" +# The pfx path comes from the prepare script above. +# TODO: Move the set instruction in the script at the plugin level? +$certPath = (Convert-Path .\certificate.pfx) +If (Test-Path $certPath) { + [System.Environment]::SetEnvironmentVariable('CSC_LINK', $certPath, [System.EnvironmentVariableTarget]::Machine) + Write-Host "Environment variable CSC_LINK set to $certPath" +} else { + Write-Host "[!] certificate.pfx file does not exist." + Exit 1 +} + +# Workaround for CI not finding the certificate. +# See failure such as +# https://buildkite.com/automattic/simplenote-electron/builds/71#01900b28-9508-4bfe-bc80-63464afeaa3e/292-567 +Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\Root -Password (ConvertTo-SecureString -String $env:WINDOWS_CODE_SIGNING_CERT_PASSWORD -AsPlainText -Force) + +Write-Host "--- :windows: Installing make" +choco install make + +Write-Host "--- :npm: Installing dependencies" +npm ci --legacy-peer-deps + +Write-Host "--- :lock_with_ink_pen: Decrypting secrets" +make decrypt_conf + +Write-Host "--- :node: Building app" +make build + +Write-Host "--- :windows: Packaging for Windows" +make package-win32 SKIP_BUILD=true + +If ($LastExitCode -ne 0) { Exit $LastExitCode } diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 000000000..57222b3e5 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,106 @@ +env: + BUILDKITE_PLUGINS_ALWAYS_CLONE_FRESH: 1 + # Uncomment this to get more logs during the electron-builder calls. + # Useful for debugging, but very noisy + # DEBUG: electron-builder + +steps: + - label: Lint + key: lint + env: + NODE_ENV: test + plugins: + - $NVM_PLUGIN + command: | + .buildkite/commands/install_node_dependencies.sh + echo "--- :eslint: Lint" + make lint + + - label: Test + key: test + env: + NODE_ENV: test + plugins: + - $NVM_PLUGIN + command: | + .buildkite/commands/install_node_dependencies.sh + echo "--- :jest: Test" + npm test + + # Notice that we build the app in each platform because it takes ~1-2 minutes + # to do so which is comparable to a dedicated build step, archiving the + # artifacts, uploading them, then downloading them in each package step. + # + # Also notice that we package for the different platforms on every build. + # It's up to the elector-builder configuration to decided whether to upload + # the artifacts to a GitHub release. + # + # See: + # - Makefile PUBLISH value + # - https://www.electron.build/configuration/publish + + - label: Package on macOS + key: package-macos + agents: + queue: mac + env: + IMAGE_ID: $IMAGE_ID + CSC_FOR_PULL_REQUEST: true + plugins: + - $CI_TOOLKIT_PLUGIN + - $NVM_PLUGIN + command: | + .buildkite/commands/install_node_dependencies.sh + echo "--- Fetch code signing identity" + install_gems + bundle exec fastlane configure_code_signing + echo "--- Decrypt secrets" + make decrypt_conf + bundle exec fastlane run configure_apply + echo "--- Build" + make build + echo "--- Package" + make package-osx SKIP_BUILD=true + artifact_paths: + - release/*.dmg + - release/*.dmg.blockmap + - release/latest*.yml + + - label: Package on Windows + key: package-windows + agents: + queue: windows + plugins: + - $CI_TOOLKIT_PLUGIN + command: .buildkite/commands/package_windows.ps1 + env: + CSC_FOR_PULL_REQUEST: true + artifact_paths: + - release\*.exe + # Notice that this will not signed. + # electron-builder logs "AppX is not signed reason=Windows Store only build" + # The behavior occurs in CircleCI, too. See: + # https://app.circleci.com/pipelines/github/Automattic/simplenote-electron/3150/workflows/9970dee9-bc25-432a-a659-38ed5d4d1c36/jobs/25900?invite=true#step-106-66244_55 + - release\*.appx + - release\*.blockmap + - release\*.yml + + - label: Package on Linux + key: package-linux + plugins: + - $CI_TOOLKIT_PLUGIN + - $NVM_PLUGIN + command: | + .buildkite/commands/install_node_dependencies.sh + echo "--- Decrypt secrets" + make decrypt_conf + echo "--- Build" + make build + echo "--- Package" + make package-linux SKIP_BUILD=true + env: + CSC_FOR_PULL_REQUEST: true + artifact_paths: + - release/*.deb + - release/*.tar.gz + - release/*.yml diff --git a/.buildkite/shared-pipeline-vars b/.buildkite/shared-pipeline-vars new file mode 100755 index 000000000..f4fcb493b --- /dev/null +++ b/.buildkite/shared-pipeline-vars @@ -0,0 +1,13 @@ +#!/bin/sh + +# This file is `source`'d before calling `buildkite-agent pipeline upload`, and can be used +# to set up some variables that will be interpolated in the `.yml` pipeline before uploading it. + +# The ~> modifier is not currently used, but we check for it just in case +XCODE_VERSION=$(sed -E -n 's/^(~> )?(.*)/xcode-\2/p' .xcode-version) +CI_TOOLKIT_PLUGIN_VERSION='mokagio/windows-utils' +NVM_PLUGIN_VERSION='0.3.0' + +export IMAGE_ID="$XCODE_VERSION" +export CI_TOOLKIT_PLUGIN="automattic/a8c-ci-toolkit#$CI_TOOLKIT_PLUGIN_VERSION" +export NVM_PLUGIN="automattic/nvm#$NVM_PLUGIN_VERSION" diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 000000000..8515e56f3 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,4 @@ +--- +BUNDLE_PATH: "vendor/bundle" +BUNDLE_JOBS: "3" +BUNDLE_RETRY: "3" diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c76bee6cd..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,300 +0,0 @@ -version: 2.1 - -orbs: - win: circleci/windows@2.2.0 - -references: - decrypt: &decrypt - run: - name: Decrypt assets - command: | - openssl aes-256-cbc -md md5 -d -in ./resources/certificates/win.p12.enc -out ./resources/certificates/win.p12 -k ${SECRETS_ENCRYPTION_KEY_2024} - openssl aes-256-cbc -md md5 -d -in ./resources/certificates/mac.p12.enc -out ./resources/certificates/mac.p12 -k ${SECRETS_ENCRYPTION_KEY} - openssl aes-256-cbc -d -in ./resources/secrets/config.json.enc -out ./config.json -pbkdf2 -k ${SECRETS_ENCRYPTION_KEY} - job_filters: &job_filters - branches: - ignore: - - webapp - - webapp-develop - - webapp-staging - tags: - only: /.*/ - restore_nvm: &restore_nvm - restore_cache: - name: Restoring NVM cache - keys: - - v1-nvm-0-33-11-{{ arch }}-{{ checksum ".nvmrc" }} - - v1-nvm-0-33-11-{{ arch }} - setup_nvm: &setup_nvm - run: - name: Install nvm and node version - command: | - set +e - set +u - set +x - NODE_VERSION=$(cat .nvmrc) - export NVM_DIR="${HOME}/.nvm" - mkdir -p "$NVM_DIR" - if [ ! -f "${NVM_DIR}/nvm.sh" ]; then - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash - fi - [ -s "${NVM_DIR}/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install "$NODE_VERSION" - nvm alias default "$NODE_VERSION" - nvm use "$NODE_VERSION" - save_nvm: &save_nvm - save_cache: - name: Saving NVM cache - key: v1-nvm-0-33-11-{{ arch }}-{{ checksum ".nvmrc" }} - paths: - - ~/.nvm - npm_install: &npm_install - run: - name: Npm install - command: | - source $HOME/.nvm/nvm.sh - nvm use - npm install --force - npm_restore_cache: &npm_restore_cache - restore_cache: - name: Restore npm cache - keys: - - v2-npm-{{ arch }}-{{ checksum "package-lock.json" }} - npm_save_cache: &npm_save_cache - save_cache: - name: Save npm cache - key: v2-npm-{{ arch }}-{{ checksum "package-lock.json" }} - paths: - - ~/.npm - app_cache_paths: &app_cache_paths - - dist - - desktop - - resources - - config.json - install_linux_deps: &install_linux_deps - run: - name: Install linux dev dependencies - command: | - sudo dpkg --add-architecture i386 - sudo apt update - sudo apt -y install libxkbfile-dev libxkbfile-dev:i386 libxext-dev libx11-dev libx11-dev:i386 libxss-dev gcc-multilib g++-multilib rpm - -jobs: - build: - docker: - - image: cimg/node:18.19.1 - shell: /bin/bash --login - working_directory: ~/simplenote - steps: - - *install_linux_deps - - checkout - - *decrypt - - *restore_nvm - - *setup_nvm - - *save_nvm - - *npm_restore_cache - - *npm_install - - run: make build - - persist_to_workspace: - root: ~/simplenote - paths: *app_cache_paths - test: - docker: - - image: cimg/node:18.19.1 - shell: /bin/bash --login - working_directory: ~/simplenote - steps: - - *install_linux_deps - - checkout - - *decrypt - - *restore_nvm - - *setup_nvm - - *save_nvm - - *npm_restore_cache - - *npm_install - - run: NODE_ENV=test npm run build - - run: npm test - - run: npm run lint - - linux: - docker: - - image: cimg/node:18.19.1 - working_directory: ~/simplenote - steps: - - checkout - - attach_workspace: - at: ~/simplenote - - *restore_nvm - - *setup_nvm - - *save_nvm - - *npm_restore_cache - - *install_linux_deps - - *npm_install - - *npm_save_cache - - run: make package-linux SKIP_BUILD=true - - run: - name: Release cleanup - command: | - set +e - rm -rf release/github - rm -rf release/linux-unpacked - rm release/builder-debug.yml - - persist_to_workspace: - root: ~/simplenote - paths: - - release - - mac: - macos: - xcode: '12.5.1' - shell: /bin/bash --login - working_directory: /Users/distiller/simplenote - steps: - - checkout - - attach_workspace: - at: /Users/distiller/simplenote - - *restore_nvm - - *setup_nvm - - *save_nvm - - *npm_restore_cache - - *npm_install - - *npm_save_cache - - run: - name: Build Mac - environment: - CSC_LINK: resources/certificates/mac.p12 - CSC_FOR_PULL_REQUEST: true - command: | - make package-osx SKIP_BUILD=true - - run: - name: Release cleanup - command: | - set +e - rm -rf release/github - rm -rf release/mac - rm release/builder-debug.yml - - persist_to_workspace: - root: /Users/distiller/simplenote - paths: - - release - - windows: - executor: - name: win/default - working_directory: C:\Users\circleci\simplenote-electron - environment: - CSC_FOR_PULL_REQUEST: true - steps: - - checkout - - attach_workspace: - at: C:\Users\circleci\simplenote-electron - - run: - name: Install make - command: choco install make - - run: - name: Install node version - command: | - $NODE_VERSION = Get-Content .\.nvmrc - nvm install $NODE_VERSION - nvm use $NODE_VERSION - - run: - name: Npm install - command: | - npm install --force - - run: - name: Build windows - command: | - # Workaround for Sign Tool "private key filter" bug in Circle's Windows image. - # Ref: https://travis-ci.community/t/codesigning-on-windows/ - # - # Fix: Import .p12 into the local certificate store. Sign Tool will use - # package.json's `certificateSubjectName` to find the imported cert. - Import-PfxCertificate -FilePath C:\Users\circleci\simplenote-electron\resources\certificates\win.p12 -CertStoreLocation Cert:\LocalMachine\Root -Password (ConvertTo-SecureString -String $env:WIN_CSC_KEY_PASSWORD -AsPlainText -Force) - npx electron-builder --win -p onTag - - run: - name: Release cleanup - command: | - rm release/builder-debug.yml - - persist_to_workspace: - root: C:\Users\circleci\simplenote-electron - paths: - - release\*.exe - - release\*.appx - - release\*.yml - windowsstore: - executor: - name: win/default - working_directory: C:\Users\circleci\simplenote-electron - environment: - CSC_LINK: '' - WIN_CSC_LINK: '' - steps: - - checkout - - attach_workspace: - at: C:\Users\circleci\simplenote-electron - - run: - name: Install make - command: choco install make - - run: - name: Install node version - command: | - $NODE_VERSION = Get-Content .\.nvmrc - nvm install $NODE_VERSION - nvm use $NODE_VERSION - - run: - name: Npm install - command: | - npm install --force - - run: - name: Build windows - command: | - npx electron-builder --win --config=./electron-builder-appx.json -p onTag - - persist_to_workspace: - root: C:\Users\circleci\simplenote-electron - paths: - - release\*.appx - artifacts: - docker: - - image: buildpack-deps:trusty - working_directory: simplenote - steps: - - attach_workspace: - at: /tmp/simplenote - - store_artifacts: - path: /tmp/simplenote/release - -workflows: - simplenote: - jobs: - - build: - filters: *job_filters - - test: - filters: *job_filters - - linux: - requires: - - build - filters: *job_filters - - mac: - requires: - - build - filters: *job_filters - - windows: - requires: - - build - filters: *job_filters - - windowsstore: - requires: - - build - filters: - branches: - only: - - none - tags: - only: /.*/ - - artifacts: - requires: - - linux - - mac - - windows - - windowsstore - filters: *job_filters diff --git a/.configure b/.configure new file mode 100644 index 000000000..cf84aa23d --- /dev/null +++ b/.configure @@ -0,0 +1,15 @@ +{ + "project_name": "simplenote-electron", + "branch": "trunk", + "pinned_hash": "bbd0130868d58b2e59c909433df83f4ae6721b76", + "files_to_copy": [ + { + "file": "iOS/app_store_connect_fastlane_api_key.p8", + "destination": "~/.configure/simplenote-electron/secrets/app_store_connect_api_key.p8", + "encrypt": true + } + ], + "file_dependencies": [ + + ] +} \ No newline at end of file diff --git a/.configure-files/app_store_connect_fastlane_api_key.p8.enc b/.configure-files/app_store_connect_fastlane_api_key.p8.enc new file mode 100644 index 000000000..b346a16de Binary files /dev/null and b/.configure-files/app_store_connect_fastlane_api_key.p8.enc differ diff --git a/.editorconfig b/.editorconfig index 997aacfa1..00995a493 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,10 @@ insert_final_newline = true indent_style = space indent_size = 2 +[Makefile] +indent_style = tab +tab_width = 2 + [package.json] indent_style = space indent_size = 2 diff --git a/.gitignore b/.gitignore index 02e8b13d4..5e5e97e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ desktop-build/ dist/ logs/ release/ +vendor/ *.pvk *.p12 *.spc @@ -12,3 +13,5 @@ release/ .DS_Store dev-app-update.yml lib/state/data/test_account.json +# Ignore all files in the .configure-files folder apart from the encoded ones +!.configure-files/*.enc diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..97a0057b2 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,29 @@ +AllCops: + NewCops: enable + +Metrics/BlockLength: + Exclude: &fastlane + - fastlane/Fastfile + +Metrics/MethodLength: + Max: 30 + Exclude: *fastlane + +Layout/LineLength: + Max: 180 + Exclude: *fastlane + +Layout/EmptyLines: + Exclude: *fastlane + +Style/AsciiComments: + Exclude: *fastlane + +Metrics/AbcSize: + Exclude: *fastlane + +Metrics/CyclomaticComplexity: + Exclude: *fastlane + +Metrics/PerceivedComplexity: + Exclude: *fastlane diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..be94e6f53 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.2 diff --git a/.xcode-version b/.xcode-version new file mode 100644 index 000000000..232a7fc1a --- /dev/null +++ b/.xcode-version @@ -0,0 +1 @@ +15.4 diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..3e08fef07 --- /dev/null +++ b/Gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# Normally, we'd let fastlane-plugin-wpmreleasetoolkit transitively fetch Fastlane. +# But there's a bug in the version it resolves by default that we need to fix for while we wait for the plugin to update the resolved version. +# +# 2.219.0 includes a fix for a bug introduced in 2.218.0 +# See https://github.com/fastlane/fastlane/issues/21762#issuecomment-1875208663 +gem 'fastlane', '~> 2.219' +# This comment avoids typing to switch to a development version for testing. +# +# gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', ref: '' +gem 'fastlane-plugin-wpmreleasetoolkit', '~> 11.0' +# TODO: Remove once Dangermattic is set up +gem 'rubocop', '~> 1.61' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..49f38b2ab --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,304 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + activesupport (7.1.3.3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + artifactory (3.0.17) + ast (2.4.2) + atomos (0.1.3) + aws-eventstream (1.3.0) + aws-partitions (1.937.0) + aws-sdk-core (3.196.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.8) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.82.0) + aws-sdk-core (~> 3, >= 3.193.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.151.0) + aws-sdk-core (~> 3, >= 3.194.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + bigdecimal (3.1.8) + buildkit (1.6.0) + sawyer (>= 0.6) + chroma (0.2.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + concurrent-ruby (1.3.1) + connection_pool (2.4.1) + declarative (0.0.20) + diffy (3.4.2) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + drb (2.2.1) + emoji_regex (3.2.3) + excon (0.110.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.3.1) + fastlane (2.220.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored (~> 1.2) + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (>= 0.1.1, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-plugin-wpmreleasetoolkit (11.0.1) + activesupport (>= 6.1.7.1) + buildkit (~> 1.5) + chroma (= 0.2.0) + diffy (~> 3.3) + fastlane (~> 2.213) + git (~> 1.3) + google-cloud-storage (~> 1.31) + java-properties (~> 0.3.0) + nokogiri (~> 1.11, < 1.17) + octokit (~> 6.1) + parallel (~> 1.14) + plist (~> 3.1) + progress_bar (~> 1.3) + rake (>= 12.3, < 14.0) + rake-compiler (~> 1.0) + xcodeproj (~> 1.22) + gh_inspector (1.1.3) + git (1.19.1) + addressable (~> 2.8) + rchardet (~> 1.8) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) + httpclient (2.8.3) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + java-properties (0.3.0) + jmespath (1.6.2) + json (2.7.2) + jwt (2.8.1) + base64 + language_server-protocol (3.17.0.3) + mini_magick (4.12.0) + mini_mime (1.1.5) + minitest (5.23.1) + multi_json (1.15.0) + multipart-post (2.4.1) + mutex_m (0.2.0) + nanaimo (0.3.0) + naturally (2.2.1) + nkf (0.2.0) + nokogiri (1.16.5-arm64-darwin) + racc (~> 1.4) + octokit (6.1.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + options (2.3.2) + optparse (0.5.0) + os (1.1.4) + parallel (1.24.0) + parser (3.3.2.0) + ast (~> 2.4.1) + racc + plist (3.7.1) + progress_bar (1.3.4) + highline (>= 1.6) + options (~> 2.3.0) + public_suffix (5.0.5) + racc (1.8.0) + rainbow (3.1.1) + rake (13.2.1) + rake-compiler (1.2.7) + rake + rchardet (1.8.0) + regexp_parser (2.9.2) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.8) + strscan (>= 3.0.9) + rouge (2.0.7) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + security (0.1.5) + signet (0.19.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + strscan (3.1.0) + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uber (0.1.0) + unicode-display_width (2.5.0) + word_wrap (1.0.0) + xcodeproj (1.24.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + arm64-darwin-22 + +DEPENDENCIES + fastlane (~> 2.219) + fastlane-plugin-wpmreleasetoolkit (~> 11.0) + rubocop (~> 1.61) + +BUNDLED WITH + 2.4.21 diff --git a/Makefile b/Makefile index f7cea4c44..f20bc9f37 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ endif THIS_MAKEFILE_PATH := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) THIS_DIR := $(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) +CONF_FILE_ENCRYPTED=./resources/secrets/config.json.enc +CONF_FILE=./config-local.json + NPM ?= $(NODE) $(shell which npm) NPM_BIN = $(shell npm bin) @@ -46,8 +49,16 @@ DEV_SERVER ?= false # electron-builder publish option # options: always|onTag|onTagOrDraft|never -PUBLISH ?= onTag - +# +# Unfortunately, electron-builder does not recognize Buildkite as a provider, so we need to manually check that. +# See https://github.com/electron-userland/electron-builder/blob/14942b70a5da79a5e36e330f64de66ec501b4ac6/packages/electron-publish/src/publisher.ts#L139 +# +# Notice the use of ?= to still be able to override at call time +ifeq ($(strip $(BUILDKITE_TAG)),) + PUBLISH ?= never +else + PUBLISH ?= always +endif # Main targets .PHONY: start @@ -91,11 +102,23 @@ build-app: .PHONY: build-if-not-exists build-if-not-exists: config.json - @if [ -f $(SIMPLENOTE_JS) ]; then true; else make build; fi + @if [ -f $(SIMPLENOTE_JS) ]; then \ + echo "File $(SIMPLENOTE_JS) exists, skipping build."; \ + true; \ + else \ + echo "File $(SIMPLENOTE_JS) does not exist, running build."; \ + make build; \ + fi .PHONY: build-if-changed build-if-changed: build-if-not-exists - @if [ $(SIMPLENOTE_CHANGES_STD) -eq 0 ]; then true; else make build; fi; + @if [ $(SIMPLENOTE_CHANGES_STD) -eq 0 ]; then \ + echo "No changes detected, skipping build."; \ + true; \ + else \ + echo "Changes detected, running build."; \ + make build; \ + fi # Build binaries only @@ -118,12 +141,13 @@ package: build-if-changed .PHONY: package-win32 package-win32: + @echo Packaging .exe... + @npx electron-builder --win -p $(PUBLISH) ifeq ($(IS_WINDOWS),true) - @echo Building .appx as well + @echo Packaging .appx as well... @npx electron-builder --win -p $(PUBLISH) --config=./electron-builder-appx.json else - @echo Skipping .appx as we are not on a Windows host - @npx electron-builder --win -p $(PUBLISH) + @echo Skipping packaging .appx because we are not running on a Windows machine. endif .PHONY: package-osx @@ -140,18 +164,18 @@ package-linux: build-if-changed install: node_modules node_modules/%: - @npm install $(notdir $@) + @npm install $(notdir $@) --legacy-peer-deps node_modules: package.json - @npm prune - @npm install + @npm prune --legacy-peer-deps + @npm install --legacy-peer-deps @touch node_modules # Checks config.json: -ifeq (,$(wildcard $(THIS_DIR)$/config.json)) - $(error config.json not found. Required file, see docs) +ifeq (,$(wildcard $(THIS_DIR)$/$CONF_FILE)) + $(error $(CONF_FILE) not found. Required file, see docs) endif @@ -178,16 +202,28 @@ lint-js: # encrypted config file .PHONY: _pwd_prompt decrypt_conf encrypt_conf -CONF_FILE=./resources/secrets/config.json.enc - # 'private' task for echoing instructions _pwd_prompt: +ifeq ($(strip $(CI)),) @echo "Check the secret store for Simplenote!" +else + @echo "Use input disabled because running in CI (CI env var set)" +endif -# to create config +OPENSSL_CMD=openssl aes-256-cbc -pbkdf2 +DECRYPT_CMD=${OPENSSL_CMD} -d -in ${CONF_FILE_ENCRYPTED} -out ${CONF_FILE} +# to create config for local development decrypt_conf: _pwd_prompt - openssl aes-256-cbc -d -in ${CONF_FILE} -out ./config-local.json -pbkdf2 +ifeq ($(strip $(CI)),) + ${DECRYPT_CMD} +else +ifeq ($(strip $(SECRETS_ENCRYPTION_KEY)),) + $(error Could not decode $(CONF_FILE) because SECRETS_ENCRYPTION_KEY is missing from environment.) +else + @${DECRYPT_CMD} -k ${SECRETS_ENCRYPTION_KEY} +endif +endif -# for updating config +# for updating the stored config with the local values encrypt_conf: _pwd_prompt - openssl aes-256-cbc -e -in config-local.json -out ${CONF_FILE} -pbkdf2 + ${OPENSSL_CMD} -e -in ${CONF_FILE} -out ${CONF_FILE_ENCRYPTED} diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 2e714252d..1d3124271 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,11 @@ # Changelog +## [v2.22.1] + +### Fixes + +- The final release build of v2.22.0 was not correctly packaged, causing a crash on startup. Affected users will need to manually download this version, which properly includes a necessary configuration file during the build process [#3232](https://github.com/Automattic/simplenote-electron/pull/3232) + ## [v2.22.0] ### Fixes @@ -10,7 +16,7 @@ ### Other Changes -- Updated dependencies, build pipeline and documentation [#3183](https://github.com/Automattic/simplenote-electron/pull/3183), [#3097](https://github.com/Automattic/simplenote-electron/pull/3097), [#3194](https://github.com/Automattic/simplenote-electron/pull/3194), [#3195](https://github.com/Automattic/simplenote-electron/pull/3195) +- Updated dependencies, build pipeline and documentation [#3183](https://github.com/Automattic/simplenote-electron/pull/3183), [#3097](https://github.com/Automattic/simplenote-electron/pull/3097), [#3194](https://github.com/Automattic/simplenote-electron/pull/3194), [#3195](https://github.com/Automattic/simplenote-electron/pull/3195), [#3218](https://github.com/Automattic/simplenote-electron/pull/3218), [#3223](https://github.com/Automattic/simplenote-electron/pull/3223) ## [v2.21.0] diff --git a/after_sign_hook.js b/after_sign_hook.js index 0df03ea61..96a0961b6 100644 --- a/after_sign_hook.js +++ b/after_sign_hook.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const dotenv = require('dotenv'); module.exports = async function (params) { // Only notarize the app on Mac OS only. @@ -7,9 +8,49 @@ module.exports = async function (params) { return; } - if (!process.env.CIRCLE_TAG || process.env.CIRCLE_TAG.length === 0) { - console.log('Not on a tag. Skipping notarization'); // eslint-disable-line no-console - return; + const appStoreConnectKeyPath = path.join( + process.env.HOME, + '.configure', + 'simplenote-electron', + 'secrets', + 'app_store_connect_api_key.p8' + ); + + const envPath = path.join( + process.env.HOME, + '.a8c-apps/simplenote-electron.env' + ); + if (fs.existsSync(envPath)) { + dotenv.config({ path: envPath }); + } else { + // eslint-disable-next-line no-console + console.log( + `No env file found at ${envPath}. Looking for required env vars individually...` + ); + let errors = []; + if (process.env.APP_STORE_CONNECT_API_KEY_KEY_ID === undefined) { + errors.push( + 'APP_STORE_CONNECT_API_KEY_KEY_ID value not found in env. Please set it.' + ); + } + if (process.env.APP_STORE_CONNECT_API_KEY_ISSUER_ID === undefined) { + errors.push( + 'APP_STORE_CONNECT_API_KEY_ISSUER_ID value not found in env. Please set it.' + ); + } + if (fs.existsSync(appStoreConnectKeyPath) === false) { + errors.push( + `Key file not found at ${appStoreConnectKeyPath}. Please add it.` + ); + } + + if (errors.length > 0) { + throw new Error( + `Could not begin signing macOS build. Errors: ${errors.join('\n')}` + ); + } else { + console.log('All required env vars found. Moving on...'); + } } // Same appId in electron-builder. @@ -29,16 +70,15 @@ module.exports = async function (params) { console.log(`Notarizing ${appId} found at ${appPath}`); // eslint-disable-line no-console try { - const electron_notarize = require('electron-notarize'); + const electron_notarize = require('@electron/notarize'); await electron_notarize.notarize({ - appBundleId: appId, appPath: appPath, - appleId: process.env.NOTARIZATION_ID, - appleIdPassword: process.env.NOTARIZATION_PWD, - ascProvider: 'AutomatticInc', + appleApiKey: appStoreConnectKeyPath, + appleApiKeyId: process.env.APP_STORE_CONNECT_API_KEY_KEY_ID, + appleApiIssuer: process.env.APP_STORE_CONNECT_API_KEY_ISSUER_ID, }); } catch (error) { - console.error(error); // eslint-disable-line no-console + throw new Error(`Notarization failed with error:\n${error}`); } console.log(`Done notarizing ${appId}`); // eslint-disable-line no-console diff --git a/fastlane/.gitignore b/fastlane/.gitignore new file mode 100644 index 000000000..8035ae185 --- /dev/null +++ b/fastlane/.gitignore @@ -0,0 +1,2 @@ +report.xml +README.md diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..91184a4c0 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'dotenv' +require 'json' + +fastlane_version '2.0' + +default_platform :mac + +PROJECT_ROOT_FOLDER = File.dirname(File.expand_path(__dir__)) + +ENV_FILE_NAME = 'simplenote-electron.env' +USER_ENV_FILE_PATH = File.join(Dir.home, '.a8c-apps', ENV_FILE_NAME) + +# TODO: This is duplicated with `after_sign_hook.js`. It would be nice to have a single source of truth. +TEAM_ID = 'PZYM8XX95Q' + +CODE_SIGNING_STORAGE_OPTIONS = { + storage_mode: 's3', + s3_bucket: 'a8c-fastlane-match', + s3_region: 'us-east-2' +}.freeze + +# Required for sync_code_signing to authenticate with S3. +CODE_SIGNING_ENV_VARS = %w[ + MATCH_S3_ACCESS_KEY + MATCH_S3_SECRET_ACCESS_KEY + MATCH_PASSWORD +].freeze + +APP_BUNDLE_IDENTIFIER = JSON.load_file!(File.join(PROJECT_ROOT_FOLDER, 'electron-builder.json'))['appId'] + +# Required for app_store_connect_api_key to generate the key information to pass down the call chain. +ASC_API_KEY_ENV_VARS = %w[ + APP_STORE_CONNECT_API_KEY_KEY_ID + APP_STORE_CONNECT_API_KEY_ISSUER_ID + APP_STORE_CONNECT_API_KEY_KEY +].freeze + +import 'lib/helpers.rb' + +before_all do + # This is necessary for 'match' to work correctly in CI. When running + # locally, it has no effect so it's safe to run it before all lanes. + setup_ci + + Dotenv.load(USER_ENV_FILE_PATH) +end + +platform :mac do + desc 'Download and configure code signing certificates and provisioning profiles .' + lane :configure_code_signing do |options| + require_env_vars!(*ASC_API_KEY_ENV_VARS, *CODE_SIGNING_ENV_VARS) + + sync_code_signing( + type: 'developer_id', + platform: 'macos', + team_id: TEAM_ID, + api_key: app_store_connect_api_key, + app_identifier: APP_BUNDLE_IDENTIFIER, + readonly: options.fetch(:readonly, true), + **CODE_SIGNING_STORAGE_OPTIONS + ) + end +end diff --git a/fastlane/example.env b/fastlane/example.env new file mode 100644 index 000000000..ae2de1462 --- /dev/null +++ b/fastlane/example.env @@ -0,0 +1,9 @@ +# You don't need to fill in these values for Fastlane to run. +# +# However, if a lane requires some of these environment values and they are not set here, it will fail. +APP_STORE_CONNECT_API_KEY_KEY_ID= +APP_STORE_CONNECT_API_KEY_ISSUER_ID= +APP_STORE_CONNECT_API_KEY_KEY= +MATCH_PASSWORD= +MATCH_S3_ACCESS_KEY= +MATCH_S3_SECRET_ACCESS_KEY= diff --git a/fastlane/lib/helpers.rb b/fastlane/lib/helpers.rb new file mode 100644 index 000000000..71fb58a5b --- /dev/null +++ b/fastlane/lib/helpers.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Use this to ensure all env vars a lane requires are set. +# +# The best place to call this is at the start of a lane, to fail early. +def require_env_vars!(*keys) + keys.each { |key| get_required_env!(key) } +end + +# Use this instead of getting values from `ENV` directly. It will throw an error if the requested value is missing. +def get_required_env!(key, env_file_path: USER_ENV_FILE_PATH) + unless ENV.key?(key) + message = "Environment variable '#{key}' is not set." + + if is_ci + UI.user_error!(message) + elsif File.exist?(env_file_path) + UI.user_error!("#{message} Consider adding it to #{env_file_path}.") + else + env_file_example_path = 'fastlane/example.env' + env_file_dir = File.dirname(env_file_path) + env_file_name = File.basename(env_file_path) + + UI.user_error! <<~MSG + #{env_file_name} not found in #{env_file_dir}! + + Please copy #{env_file_example_path} to #{env_file_path} and fill in the values for the automation you require. + + mkdir -p #{env_file_dir} && cp #{env_file_example_path} #{env_file_path} + MSG + end + end + + value = ENV.fetch(key) + + UI.user_error!("Env var for key #{key} is set but empty. Please set a value for #{key}.") if value.to_s.empty? + + value +end + +def prompt_user_for_app_store_connect_credentials + require 'credentials_manager' + + # If Fastlane cannot instantiate a user, it will ask the caller for the email. + # Once we have it, we can set it as `FASTLANE_USER` in the environment (which has lifecycle limited to this call) so that the next commands will already have access to it. + # Note: if the user is already available to `AccountManager`, setting it in the env is redundant, but Fastlane doesn't provide a way to check it so we have to do it anyway. + ENV['FASTLANE_USER'] = CredentialsManager::AccountManager.new.user +end diff --git a/get-config.js b/get-config.js deleted file mode 100644 index 0bd0c3497..000000000 --- a/get-config.js +++ /dev/null @@ -1,39 +0,0 @@ -function readLocalConfig() { - try { - const config = require('./config-local'); - if (typeof config === 'function') { - throw new Error('Invalid config file. Config must be JSON.'); - } - return config; - } catch { - return false; - } -} - -function readConfig() { - try { - const config = require('./config'); - if (typeof config === 'function') { - throw new Error('Invalid config file. Config must be JSON.'); - } - return config; - } catch (e) { - // eslint-disable-next-line no-console - console.error( - 'Could not read in the required configuration file.\n' + - 'This file should exist as `config.json` inside the project root directory.\n' + - 'Please consult the project README.md for further information.\n' - ); - - throw e; - } -} - -function getConfig() { - var config = readLocalConfig() || readConfig(); - var pkg = require('./package.json'); - config.version = pkg.version; - return config; -} - -module.exports = getConfig; diff --git a/lib/auth/index.tsx b/lib/auth/index.tsx index 0d463ad9d..7f348004a 100644 --- a/lib/auth/index.tsx +++ b/lib/auth/index.tsx @@ -2,7 +2,6 @@ import React, { Component, Fragment } from 'react'; import classNames from 'classnames'; import cryptoRandomString from '../utils/crypto-random-string'; import { get } from 'lodash'; -import getConfig from '../../get-config'; import MailIcon from '../icons/mail'; import SimplenoteLogo from '../icons/simplenote'; import Spinner from '../components/spinner'; @@ -48,7 +47,6 @@ export class Auth extends Component { render() { // Don't render this component when running on the web - const config = getConfig(); if (config.is_app_engine) { return null; } @@ -423,7 +421,6 @@ export class Auth extends Component { }; onWPLogin = () => { - const config = getConfig(); const redirectUrl = encodeURIComponent(config.wpcc_redirect_url); this.authState = `app-${cryptoRandomString(20)}`; const authUrl = `https://public-api.wordpress.com/oauth2/authorize?client_id=${config.wpcc_client_id}&redirect_uri=${redirectUrl}&response_type=code&scope=global&state=${this.authState}`; diff --git a/lib/boot-with-auth.tsx b/lib/boot-with-auth.tsx index a3ee31d72..23fe62227 100644 --- a/lib/boot-with-auth.tsx +++ b/lib/boot-with-auth.tsx @@ -6,7 +6,6 @@ import React from 'react'; import App from './app'; import { ErrorBoundaryWithAnalytics } from './error-boundary'; import Modal from 'react-modal'; -import getConfig from '../get-config'; import { makeStore } from './state'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; @@ -16,8 +15,6 @@ import '../scss/style.scss'; import isDevConfig from './utils/is-dev-config'; -const config = getConfig(); - export const bootWithToken = ( logout: () => any, token: string, diff --git a/lib/boot-without-auth.tsx b/lib/boot-without-auth.tsx index 9da0ecddf..e355e8f15 100644 --- a/lib/boot-without-auth.tsx +++ b/lib/boot-without-auth.tsx @@ -9,8 +9,6 @@ import classNames from 'classnames'; import AboutDialog from './dialogs/about'; import ErrorBoundary from './error-boundary'; -import getConfig from '../get-config'; - import '../scss/style.scss'; type Props = { @@ -36,7 +34,6 @@ type User = { access_token?: string; }; -const config = getConfig(); const auth = new SimperiumAuth(config.app_id, config.app_key); class AppWithoutAuth extends Component { diff --git a/lib/boot.ts b/lib/boot.ts index cd6be293c..9a959c08a 100644 --- a/lib/boot.ts +++ b/lib/boot.ts @@ -5,13 +5,10 @@ import 'setimmediate'; import { parse } from 'cookie'; -import getConfig from '../get-config'; import { boot as bootWithoutAuth } from './boot-without-auth'; import { boot as bootLoggingOut } from './logging-out'; import { isElectron } from './utils/platform'; -const config = getConfig(); - const clearStorage = (): Promise => new Promise((resolveStorage) => { localStorage.removeItem('access_token'); diff --git a/lib/global.d.ts b/lib/global.d.ts index 20c8948a8..377985449 100644 --- a/lib/global.d.ts +++ b/lib/global.d.ts @@ -5,6 +5,16 @@ import * as S from './state'; declare global { const __TEST__: boolean; + const config: { + app_engine_url: string; + app_id: string; + app_key: string; + development: boolean; + is_app_engine: string; + version: string; + wpcc_client_id: string; + wpcc_redirect_url: string; + }; interface Window { __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose; diff --git a/lib/state/analytics/middleware.ts b/lib/state/analytics/middleware.ts index cb23e6e31..e5cb155cc 100644 --- a/lib/state/analytics/middleware.ts +++ b/lib/state/analytics/middleware.ts @@ -1,11 +1,8 @@ import { debounce } from 'lodash'; import analytics from '../../analytics'; -import getConfig from '../../../get-config'; import isDevConfig from '../../utils/is-dev-config'; -const config = getConfig(); - import type * as A from '../action-types'; import type * as S from '../'; import type * as T from '../../types'; diff --git a/lib/state/simperium/middleware.ts b/lib/state/simperium/middleware.ts index f04494ce5..51ae1b166 100644 --- a/lib/state/simperium/middleware.ts +++ b/lib/state/simperium/middleware.ts @@ -2,7 +2,6 @@ import { default as createClient } from 'simperium'; import debugFactory from 'debug'; import actions from '../actions'; -import getConfig from '../../../get-config'; import { BucketQueue } from './functions/bucket-queue'; import { InMemoryBucket } from './functions/in-memory-bucket'; import { InMemoryGhost } from './functions/in-memory-ghost'; @@ -37,7 +36,7 @@ export const initSimperium = (store) => { const { dispatch, getState } = store; - const client = createClient(getConfig().app_id, token, { + const client = createClient(config.app_id, token, { objectStoreProvider: (bucket) => { switch (bucket.name) { case 'account': @@ -465,7 +464,7 @@ export const initSimperium = return result; } - case 'REALLY_LOGOUT': + case 'REALLY_LOG_OUT': stopSyncing(); localStorage.setItem('simplenote_logout', Math.random().toString()); client.end(); diff --git a/package-lock.json b/package-lock.json index daeb8f945..d29833154 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "simplenote", - "version": "2.22.0-beta1", + "version": "2.22.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "simplenote", - "version": "2.22.0-beta1", + "version": "2.22.1", "license": "GPL-2.0", "dependencies": { "@automattic/color-studio": "2.6.0", @@ -21,7 +21,7 @@ "date-fns": "3.6.0", "electron-fetch": "1.9.1", "electron-progressbar": "2.2.1", - "electron-updater": "6.1.8", + "electron-updater": "6.3.0", "electron-window-state": "5.0.3", "file-saver": "2.0.5", "focus-trap-react": "10.2.3", @@ -79,7 +79,7 @@ "@babel/preset-env": "7.24.5", "@babel/preset-react": "7.24.1", "@babel/preset-typescript": "7.24.1", - "@electron/notarize": "2.3.0", + "@electron/notarize": "2.3.2", "@testing-library/react": "12.1.5", "@types/cookie": "0.6.0", "@types/debug": "4.1.12", @@ -2474,9 +2474,9 @@ } }, "node_modules/@electron/notarize": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.3.0.tgz", - "integrity": "sha512-EiTBU0BwE7HZZjAG1fFWQaiQpCuPrVGn7jPss1kUjD6eTTdXXd29RiZqEqkgN7xqt/Pgn4g3I7Saqovanrfj3w==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.3.2.tgz", + "integrity": "sha512-zfayxCe19euNwRycCty1C7lF7snk9YwfRpB5M8GLr1a4ICH63znxaPNAubrMvj0yDvVozqfgsdYpXVUnpWBDpg==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -4720,10 +4720,11 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz", - "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -6754,12 +6755,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -9033,11 +9035,12 @@ "dev": true }, "node_modules/electron-updater": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.1.8.tgz", - "integrity": "sha512-hhOTfaFAd6wRHAfUaBhnAOYc+ymSGCWJLtFkw4xJqOvtpHmIdNHnXDV9m1MHC+A6q08Abx4Ykgyz/R5DGKNAMQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.0.tgz", + "integrity": "sha512-3Xlezhk+dKaSQrOnkQNqCGiuGSSUPO9BV9TQZ4Iig6AyTJ4FzJONE5gFFc382sY53Sh9dwJfzKsA3DxRHt2btw==", + "license": "MIT", "dependencies": { - "builder-util-runtime": "9.2.3", + "builder-util-runtime": "9.2.5", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", @@ -9048,9 +9051,10 @@ } }, "node_modules/electron-updater/node_modules/builder-util-runtime": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz", - "integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.5.tgz", + "integrity": "sha512-HjIDfhvqx/8B3TDN4GbABQcgpewTU4LMRTQPkVpKYV3lsuxEJoIfvg09GyWTNmfVNSUAYf+fbTN//JX4TH20pg==", + "license": "MIT", "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" @@ -10479,10 +10483,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -12042,6 +12047,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -19419,6 +19425,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -20734,9 +20741,10 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 6c27d53d3..dba23cec8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "email": "support@simplenote.com" }, "productName": "Simplenote", - "version": "2.22.0-beta1", + "version": "2.22.1", "main": "desktop/index.js", "license": "GPL-2.0", "homepage": "https://simplenote.com", @@ -46,7 +46,7 @@ "@babel/preset-env": "7.24.5", "@babel/preset-react": "7.24.1", "@babel/preset-typescript": "7.24.1", - "@electron/notarize": "2.3.0", + "@electron/notarize": "2.3.2", "@testing-library/react": "12.1.5", "@types/cookie": "0.6.0", "@types/debug": "4.1.12", @@ -123,7 +123,7 @@ "date-fns": "3.6.0", "electron-fetch": "1.9.1", "electron-progressbar": "2.2.1", - "electron-updater": "6.1.8", + "electron-updater": "6.3.0", "electron-window-state": "5.0.3", "file-saver": "2.0.5", "focus-trap-react": "10.2.3", diff --git a/tsconfig.json b/tsconfig.json index c4cb34415..b94741e11 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,8 @@ "compilerOptions": { "baseUrl": ".", "target": "esnext", - "module": "nodenext", - "moduleResolution": "nodenext", + "module": "esnext", + "moduleResolution": "bundler", "allowJs": true, "jsx": "react", "noEmit": true, diff --git a/webpack.config.js b/webpack.config.js index 95222ab12..a70271db9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,13 +1,37 @@ const autoprefixer = require('autoprefixer'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const getConfig = require('./get-config'); const spawnSync = require('child_process').spawnSync; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); +const fs = require('fs'); -// Object.assign(global, { WebSocket: require('ws') }); +function readConfig() { + const configPath = fs.existsSync('./config-local.json') + ? './config-local.json' + : './config.json'; + + try { + const config = fs.readFileSync(configPath, 'utf8'); + return JSON.parse(config); + } catch (e) { + // eslint-disable-next-line no-console + console.error( + `Could not load the required configuration file.\n` + + 'Please consult the project README.md for further information.' + ); + + throw e; + } +} + +function getConfig() { + var config = readConfig(); + var pkg = require('./package.json'); + config.version = pkg.version; + return config; +} module.exports = () => { const isDevMode = process.env.NODE_ENV === 'development'; @@ -26,7 +50,7 @@ module.exports = () => { publicPath: config.web_app_url + '/', }), }, - // target: 'node', + // target: 'browserslist', // this seems like it should be "node" or "electron-renderer" but those both crash module: { rules: [ { @@ -75,15 +99,19 @@ module.exports = () => { ], }, resolve: { - // fallback: { - // setImmediate: require.resolve('setimmediate/'), - // }, extensions: ['.js', '.jsx', '.json', '.scss', '.css', '.ts', '.tsx'], modules: ['node_modules'], }, plugins: [ new NodePolyfillPlugin({ - includeAliases: ['Buffer', 'path', 'process', 'stream', 'util'], + includeAliases: [ + 'Buffer', + 'buffer', + 'path', + 'process', + 'stream', + 'util', + ], }), new HtmlWebpackPlugin({ 'build-platform': process.platform, @@ -132,12 +160,6 @@ module.exports = () => { resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }), - // new webpack.ProvidePlugin({ - // setImmediate: require.resolve('setimmediate/'), - // }), - // new webpack.ProvidePlugin({ - // isemail: require.resolve('isemail/'), - // }), ], }; };