Remove personal team

This commit is contained in:
Paulo Cabral Sanz 2025-03-19 21:43:24 -03:00
parent 9d65bd3f49
commit 155e0bc719
13 changed files with 20 additions and 205 deletions

View file

@ -34,7 +34,6 @@ pub async fn command(args: Args, _json: bool) -> Result<()> {
let team_id = match team {
Team::Team(team) => Some(team.id.clone()),
_ => None,
};
let vars = mutations::project_create::Variables {
@ -84,18 +83,17 @@ pub async fn command(args: Args, _json: bool) -> Result<()> {
}
fn get_team_names(teams: Vec<&UserProjectsMeTeamsEdgesNode>) -> Vec<Team> {
let mut team_names = teams
let team_names = teams
.iter()
.map(|team| Team::Team(team))
.collect::<Vec<_>>();
team_names.insert(0, Team::Personal);
team_names
}
fn prompt_team(teams: Vec<Team>) -> Result<Team> {
// If there is only the personal team, return None
if teams.len() == 1 {
return Ok(Team::Personal);
return Ok(teams[0].clone());
}
let team = prompt_select("Team", teams)?;
Ok(team)
@ -132,14 +130,12 @@ fn prompt_project_name() -> Result<String> {
#[derive(Debug, Clone)]
enum Team<'a> {
Team(&'a UserProjectsMeTeamsEdgesNode),
Personal,
}
impl Display for Team<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Team::Team(team) => write!(f, "{}", team.name),
Team::Personal => write!(f, "{}", "Personal".bold()),
}
}
}

View file

@ -9,8 +9,7 @@ use crate::{
use super::{
queries::user_projects::{
UserProjectsMeProjectsEdgesNode, UserProjectsMeTeamsEdgesNode,
UserProjectsMeTeamsEdgesNodeProjectsEdgesNode,
UserProjectsMeTeamsEdgesNode, UserProjectsMeTeamsEdgesNodeProjectsEdgesNode,
},
*,
};
@ -50,7 +49,7 @@ pub async fn command(args: Args, _json: bool) -> Result<()> {
let team = select_team(args.project.clone(), args.team, &me)?;
let project = select_project(team, args.project.clone(), &me)?;
let project = select_project(team, args.project.clone())?;
let environment = select_environment(args.environment, &project)?;
@ -145,25 +144,8 @@ fn select_environment(
fn select_project(
team: Team<'_>,
project: Option<String>,
me: &queries::user_projects::UserProjectsMe,
) -> Result<NormalisedProject, anyhow::Error> {
let project = NormalisedProject::from(match team {
Team::Personal => {
if let Some(project) = project {
let proj = me.projects.edges.iter().find(|pro| {
(pro.node.id.to_lowercase() == project.to_lowercase())
|| (pro.node.name.to_lowercase() == project.to_lowercase())
});
if let Some(project) = proj {
fake_select("Select a project", &project.node.name);
Project(ProjectType::Personal(project.node.clone()))
} else {
return Err(RailwayError::ProjectNotFound.into());
}
} else {
prompt_personal_projects(me)?
}
}
Team::Team(team) => {
if let Some(project) = project {
let proj = team.projects.edges.iter().find(|pro| {
@ -197,15 +179,7 @@ fn select_team(
let team = match (project.as_ref(), team.as_ref()) {
(Some(project), None) if uuid_regex.is_match(project) => {
// It's a project id, figure out team
if me
.projects
.edges
.iter()
.any(|pro| pro.node.id.to_lowercase() == project.to_lowercase())
{
fake_select("Select a team", "Personal");
Team::Personal
} else if let Some(team) = me.teams.edges.iter().find(|team| {
if let Some(team) = me.teams.edges.iter().find(|team| {
team.node
.projects
.edges
@ -218,32 +192,16 @@ fn select_team(
return Err(RailwayError::ProjectNotFound.into());
}
}
(Some(project), None) => {
(Some(_), None) => {
// this means project name without team
if me.teams.edges.is_empty() {
// no teams, so it's personal
// if there is a project that has the same name
if me
.projects
.edges
.iter()
.any(|p| p.node.name.to_lowercase() == project.to_lowercase())
{
fake_select("Select a team", "Personal");
Team::Personal
} else {
return Err(RailwayError::ProjectNotFound.into());
}
return Err(RailwayError::ProjectNotFound.into());
} else {
prompt_teams(me)?
}
}
(None, Some(team_arg)) | (Some(_), Some(team_arg)) => {
match team_arg.to_lowercase().as_str() {
"personal" => {
fake_select("Select a team", "Personal");
Team::Personal
}
_ => {
if let Some(team) = me.teams.edges.iter().find(|team| {
(team.node.name.to_lowercase() == team_arg.to_lowercase())
@ -258,10 +216,7 @@ fn select_team(
}
}
(None, None) if !me.teams.edges.is_empty() => prompt_teams(me)?,
(None, None) => {
fake_select("Select a team", "Personal");
Team::Personal
}
(None, None) => return Err(RailwayError::NoWorkspaceFound.into()),
};
Ok(team)
}
@ -269,7 +224,7 @@ fn select_team(
fn prompt_teams(me: &queries::user_projects::UserProjectsMe) -> Result<Team<'_>> {
let teams: Vec<&UserProjectsMeTeamsEdgesNode> =
me.teams.edges.iter().map(|team| &team.node).collect();
let mut team_names = vec![Team::Personal];
let mut team_names = vec![];
team_names.extend(teams.into_iter().map(Team::Team));
prompt_options("Select a team", team_names)
}
@ -294,27 +249,6 @@ fn prompt_team_projects(
prompt_options("Select a project", prompt_projects)
}
fn prompt_personal_projects(
me: &queries::user_projects::UserProjectsMe,
) -> Result<Project, anyhow::Error> {
let mut personal_projects = me
.projects
.edges
.iter()
.map(|project| &project.node)
.collect::<Vec<&UserProjectsMeProjectsEdgesNode>>();
if personal_projects.is_empty() {
return Err(RailwayError::NoProjects.into());
}
personal_projects.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
let prompt_projects = personal_projects
.iter()
.cloned()
.map(|project| Project(ProjectType::Personal(project.clone())))
.collect::<Vec<Project>>();
prompt_options("Select a project", prompt_projects)
}
structstruck::strike! {
#[strikethrough[derive(Debug, Clone, derive_new::new)]]
struct NormalisedProject {
@ -348,34 +282,6 @@ structstruck::strike! {
impl From<Project> for NormalisedProject {
fn from(value: Project) -> Self {
match value.0 {
ProjectType::Personal(personal) => NormalisedProject::new(
personal.id,
personal.name,
personal
.environments
.edges
.into_iter()
.map(|env| NormalisedEnvironment::new(env.node.id, env.node.name))
.collect(),
personal
.services
.edges
.into_iter()
.map(|service| {
NormalisedService::new(
service.node.id,
service.node.name,
service
.node
.service_instances
.edges
.into_iter()
.map(|instance| instance.node.environment_id)
.collect(),
)
})
.collect(),
),
ProjectType::Team(team) => NormalisedProject::new(
team.id,
team.name,
@ -409,21 +315,18 @@ impl From<Project> for NormalisedProject {
#[derive(Debug, Clone)]
enum Team<'a> {
Team(&'a UserProjectsMeTeamsEdgesNode),
Personal,
}
impl Display for Team<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Team::Team(team) => write!(f, "{}", team.name),
Team::Personal => write!(f, "{}", "Personal".bold()),
}
}
}
#[derive(Debug, Clone)]
enum ProjectType {
Personal(UserProjectsMeProjectsEdgesNode),
Team(UserProjectsMeTeamsEdgesNodeProjectsEdgesNode),
}
@ -433,7 +336,6 @@ struct Project(ProjectType);
impl Display for Project {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
ProjectType::Personal(personal) => write!(f, "{}", personal.name),
ProjectType::Team(team_project) => write!(f, "{}", team_project.name),
}
}

View file

@ -1,11 +1,6 @@
use serde::Serialize;
use super::{
queries::{
projects::ProjectsProjectsEdgesNode, user_projects::UserProjectsMeProjectsEdgesNode,
},
*,
};
use super::{queries::projects::ProjectsProjectsEdgesNode, *};
/// List all projects in your Railway account
#[derive(Parser)]
@ -21,35 +16,9 @@ pub async fn command(_args: Args, json: bool) -> Result<()> {
.await?
.me;
let mut personal_projects: Vec<_> = me
.projects
.edges
.iter()
.map(|project| &project.node)
.collect();
// Sort by most recently updated (matches dashboard behavior)
personal_projects.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
let mut all_projects: Vec<_> = personal_projects
.iter()
.map(|project| Project::Me((*project).clone()))
.collect();
let mut all_projects: Vec<_> = vec![];
let teams: Vec<_> = me.teams.edges.iter().map(|team| &team.node).collect();
if !json {
println!("{}", "Personal".bold());
for project in &personal_projects {
let project_name = if linked_project.is_some()
&& project.id == linked_project.as_ref().unwrap().project
{
project.name.purple().bold()
} else {
project.name.white()
};
println!(" {project_name}");
}
}
for team in teams {
if !json {
println!();
@ -95,6 +64,5 @@ pub async fn command(_args: Args, json: bool) -> Result<()> {
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
enum Project {
Me(UserProjectsMeProjectsEdgesNode),
Team(ProjectsProjectsEdgesNode),
}

View file

@ -1,5 +1,5 @@
// src/commands/ssh/common.rs
use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, Result};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::Client;
use tokio::time::Duration;
@ -113,7 +113,6 @@ pub async fn establish_connection(
ws_url: &str,
token: &str,
params: &SSHConnectParams,
spinner: &ProgressBar,
) -> Result<TerminalClient> {
let mut client = TerminalClient::new(ws_url, token, params).await?;

View file

@ -3,7 +3,6 @@ use clap::Parser;
use crate::client::GQLClient;
use crate::config::Configs;
use crate::controllers::terminal::SSHConnectParams;
pub const SSH_CONNECTION_TIMEOUT_SECS: u64 = 30;
pub const SSH_MESSAGE_TIMEOUT_SECS: u64 = 10;
@ -58,7 +57,7 @@ pub async fn command(args: Args, _json: bool) -> Result<()> {
let spinner = create_spinner(running_command);
let ws_url = format!("wss://{}", configs.get_relay_host_path());
let mut terminal_client = establish_connection(&ws_url, &token, &params, &spinner).await?;
let mut terminal_client = establish_connection(&ws_url, &token, &params).await?;
if running_command {
// Run single command

View file

@ -1,6 +1,5 @@
use anyhow::Result;
use crossterm::terminal;
use futures_util::FutureExt;
use tokio::io::AsyncReadExt;
use tokio::select;

View file

@ -4,14 +4,13 @@ use std::{
time::Duration,
};
use anyhow::{bail, Result};
use anyhow::Result;
use futures::StreamExt;
use gzp::{deflate::Gzip, ZBuilder};
use ignore::WalkBuilder;
use indicatif::{ProgressBar, ProgressFinish, ProgressIterator, ProgressStyle};
use is_terminal::IsTerminal;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use synchronized_writer::SynchronizedWriter;
use tar::Builder;
@ -21,7 +20,7 @@ use crate::{
controllers::{
deployment::{stream_build_logs, stream_deploy_logs},
environment::get_matched_environment,
project::{ensure_project_and_environment_exist, get_project},
project::get_project,
service::get_or_prompt_service,
},
errors::RailwayError,
@ -29,7 +28,6 @@ use crate::{
subscriptions::deployment::DeploymentStatus,
util::{
logs::format_attr_log,
prompt::{prompt_select, PromptService},
},
};

View file

@ -69,12 +69,6 @@ struct GithubApiRelease {
const GITHUB_API_RELEASE_URL: &str = "https://api.github.com/repos/railwayapp/cli/releases/latest";
pub const SSH_CONNECTION_TIMEOUT_SECS: u64 = 10;
pub const SSH_MESSAGE_TIMEOUT_SECS: u64 = 5;
pub const SSH_RECONNECT_DELAY_SECS: u64 = 1;
pub const SSH_MAX_RECONNECT_ATTEMPTS: u32 = 3;
pub const SSH_MAX_EMPTY_MESSAGES: u32 = 5;
impl Configs {
pub fn new() -> Result<Self> {
let environment = Self::get_environment_id();

View file

@ -169,7 +169,6 @@ impl TerminalClient {
/// Process incoming messages from the server
pub async fn handle_server_messages(&mut self) -> Result<()> {
let mut consecutive_empty_messages = 0;
let mut exit_code: Option<i32> = None;
let mut ping_interval = interval(Duration::from_secs(SSH_PING_INTERVAL_SECS));

View file

@ -2,12 +2,11 @@ use anyhow::{bail, Result};
use async_tungstenite::tungstenite::handshake::client::generate_key;
use async_tungstenite::tungstenite::http::Request;
use async_tungstenite::WebSocketStream;
use serde::{Deserialize, Serialize};
use tokio::time::{sleep, timeout, Duration};
use url::Url;
use crate::commands::ssh::{
SSH_CONNECTION_TIMEOUT_SECS, SSH_MAX_RECONNECT_ATTEMPTS, SSH_MESSAGE_TIMEOUT_SECS,
SSH_CONNECTION_TIMEOUT_SECS, SSH_MAX_RECONNECT_ATTEMPTS,
SSH_RECONNECT_DELAY_SECS,
};
use crate::consts::get_user_agent;

View file

@ -3,7 +3,6 @@ mod connection;
mod messages;
pub use client::TerminalClient;
pub use connection::{attempt_connection, establish_connection, SSHConnectParams};
pub use messages::{ClientMessage, ClientPayload, DataPayload, ServerMessage, ServerPayload};
pub use connection::SSHConnectParams;
pub const SSH_PING_INTERVAL_SECS: u64 = 10;

View file

@ -33,9 +33,6 @@ pub enum RailwayError {
#[error("Environment is deleted. Run `railway environment` to connect to an environment.")]
EnvironmentDeleted,
#[error("No projects found. Run `railway init` to create a new project")]
NoProjects,
#[error("Project does not have any services")]
NoServices,
@ -50,6 +47,9 @@ pub enum RailwayError {
#[error("Service \"{0}\" not found.\nRun `railway service` to connect to a service.")]
ServiceNotFound(String),
#[error("No workspace found.")]
NoWorkspaceFound,
#[error("Team \"{0}\" not found.")]
TeamNotFound(String),

View file

@ -1,42 +1,5 @@
query UserProjects {
me {
projects {
edges {
node {
id
name
createdAt
updatedAt
team {
id
name
}
environments {
edges {
node {
id
name
}
}
}
services {
edges {
node {
id
name
serviceInstances {
edges {
node {
environmentId
}
}
}
}
}
}
}
}
}
teams {
edges {
node {