diff --git a/.gitignore b/.gitignore index 2d8807a4b0..6923b060f6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /styles/src/types/zed.ts /crates/theme/schemas/theme.json /crates/collab/static/styles.css +/crates/collab/.admins.json /vendor/bin /assets/themes/*.json /assets/*licenses.md diff --git a/crates/collab/.admins.default.json b/crates/collab/.admins.default.json new file mode 100644 index 0000000000..6ee4d8726a --- /dev/null +++ b/crates/collab/.admins.default.json @@ -0,0 +1 @@ +["nathansobo", "as-cii", "maxbrunsfeld", "iamnbutler"] diff --git a/crates/collab/src/bin/seed.rs b/crates/collab/src/bin/seed.rs index 88fe0a647b..ed24ccef75 100644 --- a/crates/collab/src/bin/seed.rs +++ b/crates/collab/src/bin/seed.rs @@ -1,7 +1,11 @@ -use collab::{db, executor::Executor}; +use collab::{ + db::{self, NewUserParams}, + env::load_dotenv, + executor::Executor, +}; use db::{ConnectOptions, Database}; use serde::{de::DeserializeOwned, Deserialize}; -use std::fmt::Write; +use std::{fmt::Write, fs}; #[derive(Debug, Deserialize)] struct GitHubUser { @@ -12,90 +16,75 @@ struct GitHubUser { #[tokio::main] async fn main() { + load_dotenv().expect("failed to load .env.toml file"); + + let mut admin_logins = + load_admins("./.admins.default.json").expect("failed to load default admins file"); + if let Ok(other_admins) = load_admins("./.admins.json") { + admin_logins.extend(other_admins); + } + let database_url = std::env::var("DATABASE_URL").expect("missing DATABASE_URL env var"); let db = Database::new(ConnectOptions::new(database_url), Executor::Production) .await .expect("failed to connect to postgres database"); - let github_token = std::env::var("GITHUB_TOKEN").expect("missing GITHUB_TOKEN env var"); let client = reqwest::Client::new(); - let mut current_user = - fetch_github::(&client, &github_token, "https://api.github.com/user").await; - current_user - .email - .get_or_insert_with(|| "placeholder@example.com".to_string()); - let staff_users = fetch_github::>( - &client, - &github_token, - "https://api.github.com/orgs/zed-industries/teams/staff/members", - ) - .await; + // Create admin users for all of the users in `.admins.toml` or `.admins.default.toml`. + for admin_login in admin_logins { + let user = fetch_github::( + &client, + &format!("https://api.github.com/users/{admin_login}"), + ) + .await; + db.create_user( + &user.email.unwrap_or(format!("{admin_login}@example.com")), + true, + NewUserParams { + github_login: user.login, + github_user_id: user.id, + }, + ) + .await + .expect("failed to create admin user"); + } - let mut zed_users = Vec::new(); - zed_users.push((current_user, true)); - zed_users.extend(staff_users.into_iter().map(|user| (user, true))); - - let user_count = db + // Fetch 100 other random users from GitHub and insert them into the database. + let mut user_count = db .get_all_users(0, 200) .await .expect("failed to load users from db") .len(); - if user_count < 100 { - let mut last_user_id = None; - for _ in 0..10 { - let mut uri = "https://api.github.com/users?per_page=100".to_string(); - if let Some(last_user_id) = last_user_id { - write!(&mut uri, "&since={}", last_user_id).unwrap(); - } - let users = fetch_github::>(&client, &github_token, &uri).await; - if let Some(last_user) = users.last() { - last_user_id = Some(last_user.id); - zed_users.extend(users.into_iter().map(|user| (user, false))); - } else { - break; - } + let mut last_user_id = None; + while user_count < 100 { + let mut uri = "https://api.github.com/users?per_page=100".to_string(); + if let Some(last_user_id) = last_user_id { + write!(&mut uri, "&since={}", last_user_id).unwrap(); } - } + let users = fetch_github::>(&client, &uri).await; - for (github_user, admin) in zed_users { - if db - .get_user_by_github_login(&github_user.login) + for github_user in users { + last_user_id = Some(github_user.id); + user_count += 1; + db.get_or_create_user_by_github_account( + &github_user.login, + Some(github_user.id), + github_user.email.as_deref(), + ) .await - .expect("failed to fetch user") - .is_none() - { - if admin { - db.create_user( - &format!("{}@zed.dev", github_user.login), - admin, - db::NewUserParams { - github_login: github_user.login, - github_user_id: github_user.id, - }, - ) - .await - .expect("failed to insert user"); - } else { - db.get_or_create_user_by_github_account( - &github_user.login, - Some(github_user.id), - github_user.email.as_deref(), - ) - .await - .expect("failed to insert user"); - } + .expect("failed to insert user"); } } } -async fn fetch_github( - client: &reqwest::Client, - access_token: &str, - url: &str, -) -> T { +fn load_admins(path: &str) -> anyhow::Result> { + let file_content = fs::read_to_string(path)?; + Ok(serde_json::from_str(&file_content)?) +} + +async fn fetch_github(client: &reqwest::Client, url: &str) -> T { let response = client .get(url) - .bearer_auth(&access_token) .header("user-agent", "zed") .send() .await diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index 954ec5f0d8..8f975b5cbe 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -20,7 +20,11 @@ impl Database { }) .on_conflict( OnConflict::column(user::Column::GithubLogin) - .update_column(user::Column::GithubLogin) + .update_columns([ + user::Column::Admin, + user::Column::EmailAddress, + user::Column::GithubUserId, + ]) .to_owned(), ) .exec_with_returning(&*tx) diff --git a/script/seed-db b/script/seed-db index 6bb0f96933..277ea89ba3 100755 --- a/script/seed-db +++ b/script/seed-db @@ -2,8 +2,4 @@ set -e cd crates/collab - -# Export contents of .env.toml -eval "$(cargo run --quiet --bin dotenv)" - cargo run --quiet --package=collab --features seed-support --bin seed -- $@ diff --git a/script/zed-local b/script/zed-local index 090fbd5876..4ae4013a4c 100755 --- a/script/zed-local +++ b/script/zed-local @@ -4,6 +4,11 @@ const HELP = ` USAGE zed-local [options] [zed args] +SUMMARY + Runs 1-4 instances of Zed using a locally-running collaboration server. + Each instance of Zed will be signed in as a different user specified in + either \`.admins.json\` or \`.admins.default.json\`. + OPTIONS --help Print this help message --release Build Zed in release mode @@ -12,6 +17,16 @@ OPTIONS `.trim(); const { spawn, execFileSync } = require("child_process"); +const assert = require("assert"); + +const defaultUsers = require("../crates/collab/.admins.default.json"); +let users = defaultUsers; +try { + const customUsers = require("../crates/collab/.admins.json"); + assert(customUsers.length > 0); + assert(customUsers.every((user) => typeof user === "string")); + users.splice(0, 0, ...customUsers); +} catch (_) {} const RESOLUTION_REGEX = /(\d+) x (\d+)/; const DIGIT_FLAG_REGEX = /^--?(\d+)$/; @@ -71,10 +86,6 @@ if (instanceCount > 1) { } } -let users = ["nathansobo", "as-cii", "maxbrunsfeld", "iamnbutler"]; - -const RUST_LOG = process.env.RUST_LOG || "info"; - // If a user is specified, make sure it's first in the list const user = process.env.ZED_IMPERSONATE; if (user) { @@ -88,18 +99,12 @@ const positions = [ `${instanceWidth},${instanceHeight}`, ]; -const buildArgs = (() => { - const buildArgs = ["build"]; - if (isReleaseMode) { - buildArgs.push("--release"); - } - - return buildArgs; -})(); -const zedBinary = (() => { - const target = isReleaseMode ? "release" : "debug"; - return `target/${target}/Zed`; -})(); +let buildArgs = ["build"]; +let zedBinary = "target/debug/Zed"; +if (isReleaseMode) { + buildArgs.push("--release"); + zedBinary = "target/release/Zed"; +} execFileSync("cargo", buildArgs, { stdio: "inherit" }); setTimeout(() => { @@ -115,7 +120,7 @@ setTimeout(() => { ZED_ADMIN_API_TOKEN: "secret", ZED_WINDOW_SIZE: `${instanceWidth},${instanceHeight}`, PATH: process.env.PATH, - RUST_LOG, + RUST_LOG: process.env.RUST_LOG || "info", }, }); }