Unify the two local zed scripts, take a flag for an instance count (#3106)

Max Brunsfeld created

This PR introduces a new script for running Zed against a local collab
server, called `script/zed-local`. This script replaces the two existing
scripts that we had for this purpose: `script/zed-with-local-servers`
and `script/start-local-collaboration`.

By default, the script starts one single instance of Zed, but you can
pass a numeric flag to start 1, 2, 3 or 4 instances. So to start up two
instances side by side, (like `start-local-collaboration` script), you'd
do this:

```
script/zed-local -2
```

But you can also start *three* (or even four) instances, each taking up
a quarter of the screen, like this:

```
script/zed-local -3
```

Like before, you can pass other arguments to the script, and they will
be passed through to the first zed instance.

Also, unlike the `start-local-collaboration` script, this script now
requires a call to GitHub to determine your GitHub username. It just
logs you in as Nathan by default, unless you set `ZED_IMPERSONATE`
explicitly.

Change summary

README.md                        |  4 -
docs/building-zed.md             |  3 
docs/local-collaboration.md      |  2 
script/start-local-collaboration | 59 ----------------------
script/zed-local                 | 88 ++++++++++++++++++++++++++++++++++
script/zed-with-local-servers    |  6 --
6 files changed, 91 insertions(+), 71 deletions(-)

Detailed changes

README.md 🔗

@@ -83,9 +83,7 @@ foreman start
 If you want to run Zed pointed at the local servers, you can run:
 
 ```
-script/zed-with-local-servers
-# or...
-script/zed-with-local-servers --release
+script/zed-local
 ```
 
 ### Dump element JSON

docs/building-zed.md 🔗

@@ -75,8 +75,7 @@ Expect this to take 30min to an hour! Some of these steps will take quite a whil
     - If you are just using the latest version, but not working on zed:
       - `cargo run --release`
     - If you need to run the collaboration server locally:
-      - `script/zed-with-local-servers`
-    - If you need to test collaboration with mutl
+      - `script/zed-local`
 
 ## Troubleshooting
 

docs/local-collaboration.md 🔗

@@ -17,6 +17,6 @@
 ## Testing collab locally
 
 1. Run `foreman start` from the root of the repo.
-1. In another terminal run `script/start-local-collaboration`.
+1. In another terminal run `script/zed-local -2`.
 1. Two copies of Zed will open. Add yourself as a contact in the one that is not you.
 1. Start a collaboration session as normal with any open project.

script/start-local-collaboration 🔗

@@ -1,59 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [[ -z "$GITHUB_TOKEN" ]]; then
-  cat <<-MESSAGE
-Missing \`GITHUB_TOKEN\` environment variable. This token is needed
-for fetching your GitHub identity from the command-line.
-
-Create an access token here: https://github.com/settings/tokens
-Then edit your \`~/.zshrc\` (or other shell initialization script),
-adding a line like this:
-
-    export GITHUB_TOKEN="(the token)"
-
-MESSAGE
-  exit 1
-fi
-
-# Install jq if it's not installed
-if ! command -v jq &> /dev/null; then
-    echo "Installing jq..."
-    brew install jq
-fi
-
-# Start one Zed instance as the current user and a second instance with a different user.
-username_1=$(curl -sH "Authorization: bearer $GITHUB_TOKEN" https://api.github.com/user | jq -r .login)
-username_2=nathansobo
-if [[ $username_1 == $username_2 ]]; then
-  username_2=as-cii
-fi
-
-# Make each Zed instance take up half of the screen.
-output=$(system_profiler SPDisplaysDataType -json)
-main_display=$(echo "$output" | jq '.SPDisplaysDataType[].spdisplays_ndrvs[] | select(.spdisplays_main == "spdisplays_yes")')
-resolution=$(echo "$main_display" | jq -r '._spdisplays_resolution')
-width=$(echo "$resolution" | jq -Rr 'match("(\\d+) x (\\d+)").captures[0].string')
-half_width=$(($width / 2))
-height=$(echo "$resolution" | jq -Rr 'match("(\\d+) x (\\d+)").captures[1].string')
-y=0
-
-position_1=0,${y}
-position_2=${half_width},${y}
-
-# Authenticate using the collab server's admin secret.
-export ZED_STATELESS=1
-export ZED_ALWAYS_ACTIVE=1
-export ZED_ADMIN_API_TOKEN=secret
-export ZED_SERVER_URL=http://localhost:8080
-export ZED_WINDOW_SIZE=${half_width},${height}
-
-cargo build
-sleep 0.5
-
-# Start the two Zed child processes. Open the given paths with the first instance.
-trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
-ZED_IMPERSONATE=${ZED_IMPERSONATE:=${username_1}} ZED_WINDOW_POSITION=${position_1} target/debug/Zed $@ &
-SECOND=true ZED_IMPERSONATE=${username_2} ZED_WINDOW_POSITION=${position_2} target/debug/Zed &
-wait

script/zed-local 🔗

@@ -0,0 +1,88 @@
+#!/usr/bin/env node
+
+const {spawn, execFileSync} = require('child_process')
+
+const RESOLUTION_REGEX = /(\d+) x (\d+)/
+const DIGIT_FLAG_REGEX = /^--?(\d+)$/
+
+const args = process.argv.slice(2)
+
+// Parse the number of Zed instances to spawn.
+let instanceCount = 1
+const digitMatch = args[0]?.match(DIGIT_FLAG_REGEX)
+if (digitMatch) {
+  instanceCount = parseInt(digitMatch[1])
+  args.shift()
+}
+if (instanceCount > 4) {
+  throw new Error('Cannot spawn more than 4 instances')
+}
+
+// Parse the resolution of the main screen
+const displayInfo = JSON.parse(
+  execFileSync(
+    'system_profiler',
+    ['SPDisplaysDataType', '-json'],
+    {encoding: 'utf8'}
+  )
+)
+const mainDisplayResolution = displayInfo
+  ?.SPDisplaysDataType[0]
+  ?.spdisplays_ndrvs
+  ?.find(entry => entry.spdisplays_main === "spdisplays_yes")
+  ?._spdisplays_resolution
+  ?.match(RESOLUTION_REGEX)
+if (!mainDisplayResolution) {
+  throw new Error('Could not parse screen resolution')
+}
+const screenWidth = parseInt(mainDisplayResolution[1])
+const screenHeight = parseInt(mainDisplayResolution[2])
+
+// Determine the window size for each instance
+let instanceWidth = screenWidth
+let instanceHeight = screenHeight
+if (instanceCount > 1) {
+  instanceWidth = Math.floor(screenWidth / 2)
+  if (instanceCount > 2) {
+    instanceHeight = Math.floor(screenHeight / 2)
+  }
+}
+
+let users = [
+  'nathansobo',
+  'as-cii',
+  'maxbrunsfeld',
+  'iamnbutler'
+]
+
+// If a user is specified, make sure it's first in the list
+const user = process.env.ZED_IMPERSONATE
+if (user) {
+  users = [user].concat(users.filter(u => u !== user))
+}
+
+const positions = [
+  '0,0',
+  `${instanceWidth},0`,
+  `0,${instanceHeight}`,
+  `${instanceWidth},${instanceHeight}`
+]
+
+execFileSync('cargo', ['build'], {stdio: 'inherit'})
+
+setTimeout(() => {
+  for (let i = 0; i < instanceCount; i++) {
+    spawn('target/debug/Zed', i == 0 ? args : [], {
+      stdio: 'inherit',
+      env: {
+        ZED_IMPERSONATE: users[i],
+        ZED_WINDOW_POSITION: positions[i],
+        ZED_STATELESS: '1',
+        ZED_ALWAYS_ACTIVE: '1',
+        ZED_SERVER_URL: 'http://localhost:8080',
+        ZED_ADMIN_API_TOKEN: 'secret',
+        ZED_WINDOW_SIZE: `${instanceWidth},${instanceHeight}`
+      }
+    })
+  }
+}, 0.1)

script/zed-with-local-servers 🔗

@@ -1,6 +0,0 @@
-#!/bin/bash
-
-: "${ZED_IMPERSONATE:=as-cii}"
-export ZED_IMPERSONATE
-
-ZED_ADMIN_API_TOKEN=secret ZED_SERVER_URL=http://localhost:8080 cargo run $@