Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/builds.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::auth::AuthManager;
use crate::config::{self, WavedashConfig};
use crate::config::{self, UploadSource, WavedashConfig};
use crate::file_staging::FileStaging;
use anyhow::Result;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -43,6 +43,7 @@ struct BuildUploadInfo<'a> {
entrypoint_params: Option<serde_json::Value>,
message: Option<&'a str>,
build_size_bytes: u64,
upload_source: UploadSource,
}

async fn get_temp_credentials(
Expand All @@ -59,6 +60,7 @@ async fn get_temp_credentials(

let mut request_body = serde_json::json!({
"buildSizeBytes": info.build_size_bytes,
"uploadSource": info.upload_source.as_label(),
});

if let Some(eng) = info.engine {
Expand Down Expand Up @@ -133,6 +135,7 @@ pub async fn handle_build_push(
config_path: PathBuf,
verbose: bool,
message: Option<String>,
upload_source: UploadSource,
) -> Result<()> {
// Load wavedash.toml config
let wavedash_config = WavedashConfig::load(&config_path)?;
Expand Down Expand Up @@ -177,6 +180,7 @@ pub async fn handle_build_push(
entrypoint_params: wavedash_config.executable_entrypoint_params()?,
message: message.as_deref(),
build_size_bytes: total_bytes,
upload_source,
},
&api_key,
)
Expand Down
27 changes: 27 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
//! project config at all.

use anyhow::Result;
use clap::ValueEnum;
use colored::Colorize;
use directories::BaseDirs;
use serde::Deserialize;
Expand Down Expand Up @@ -478,6 +479,25 @@ impl EngineKind {
}
}

// The API's third source, `WEB`, has no variant here: it's set server-side and
// rejected from any client that claims it.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum)]
pub enum UploadSource {
#[default]
#[value(skip)]
Cli,
GodotPlugin,
}

impl UploadSource {
pub fn as_label(&self) -> &'static str {
match self {
UploadSource::Cli => "CLI",
UploadSource::GodotPlugin => "GODOT_PLUGIN",
}
}
}

/// Resolve a game_id from, in precedence order: the `--game-id` flag,
/// `WAVEDASH_GAME_ID`, then `game_id` in the wavedash.toml at `config_path`.
/// Errors include the config path so the user knows which file we tried to read.
Expand Down Expand Up @@ -1805,4 +1825,11 @@ mod tests {
Some(serde_json::json!({ "executable": "game.swf" }))
);
}

#[test]
fn upload_source_labels_are_the_ones_the_api_accepts() {
assert_eq!(UploadSource::default(), UploadSource::Cli);
assert_eq!(UploadSource::Cli.as_label(), "CLI");
assert_eq!(UploadSource::GodotPlugin.as_label(), "GODOT_PLUGIN");
}
}
15 changes: 12 additions & 3 deletions src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::Deserialize;
use walkdir::WalkDir;

use crate::auth::{generate_state, AuthManager};
use crate::config::{self, EngineKind, WavedashConfig};
use crate::config::{self, EngineKind, UploadSource, WavedashConfig};
use crate::file_staging::FileStaging;

mod server;
Expand All @@ -23,13 +23,16 @@ async fn create_local_build(
engine: Option<&str>,
engine_version: Option<&str>,
entrypoint: Option<&str>,
upload_source: UploadSource,
api_key: &str,
) -> Result<CreateLocalBuildResponse> {
let client = config::create_http_client()?;
let api_host = config::get("api_host")?;
let url = format!("{}/api/games/{}/builds/create-local", api_host, game_id);

let mut request_body = serde_json::json!({});
let mut request_body = serde_json::json!({
"uploadSource": upload_source.as_label(),
});
if let Some(eng) = engine {
request_body["engine"] = serde_json::json!(eng);
}
Expand All @@ -54,7 +57,12 @@ async fn create_local_build(

const DEFAULT_CONFIG: &str = "./wavedash.toml";

pub async fn handle_dev(config_path: Option<PathBuf>, verbose: bool, no_open: bool) -> Result<()> {
pub async fn handle_dev(
config_path: Option<PathBuf>,
verbose: bool,
no_open: bool,
upload_source: UploadSource,
) -> Result<()> {
let auth_manager = AuthManager::new()?;
let api_key = auth_manager
.get_api_key()
Expand Down Expand Up @@ -128,6 +136,7 @@ pub async fn handle_dev(config_path: Option<PathBuf>, verbose: bool, no_open: bo
engine_kind.map(|e| e.as_label()),
wavedash_config.engine_version()?,
entrypoint.as_deref(),
upload_source,
&api_key,
)
.await?;
Expand Down
137 changes: 132 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use builds::handle_build_push;
use clap::{Parser, Subcommand};
use clear_playtest_data::{handle_clear_playtest_data, ClearPlaytestDataArgs};
use colored::Colorize;
use config::resolve_game_id;
use config::{resolve_game_id, UploadSource};
use dev::handle_dev;
use init::{
handle_init, handle_project_create, handle_project_list, handle_team_create, handle_team_list,
Expand Down Expand Up @@ -87,6 +87,13 @@ enum Commands {
help = "Don't automatically open the browser; just print the local URL"
)]
no_open: bool,
#[arg(
long = "upload-source",
value_enum,
hide = true,
help = "Attribute the build to the tool running the CLI instead of the CLI itself"
)]
upload_source: Option<UploadSource>,
},
#[command(
about = "Publish an uploaded build to wavedash.com",
Expand Down Expand Up @@ -245,6 +252,13 @@ enum BuildCommands {
config: PathBuf,
#[arg(short = 'm', long = "message", help = "Build message")]
message: Option<String>,
#[arg(
long = "upload-source",
value_enum,
hide = true,
help = "Attribute the build to the tool running the CLI instead of the CLI itself"
)]
upload_source: Option<UploadSource>,
},
}

Expand Down Expand Up @@ -588,12 +602,32 @@ async fn run() -> Result<()> {
}
}
Commands::Build { action } => match action {
BuildCommands::Push { config, message } => {
handle_build_push(config, cli.verbose, message).await?;
BuildCommands::Push {
config,
message,
upload_source,
} => {
handle_build_push(
config,
cli.verbose,
message,
upload_source.unwrap_or_default(),
)
.await?;
}
},
Commands::Dev { config, no_open } => {
handle_dev(config, cli.verbose, no_open).await?;
Commands::Dev {
config,
no_open,
upload_source,
} => {
handle_dev(
config,
cli.verbose,
no_open,
upload_source.unwrap_or_default(),
)
.await?;
}
Commands::Publish {
config,
Expand Down Expand Up @@ -832,4 +866,97 @@ mod tests {
checked
);
}

#[test]
fn upload_source_is_hidden_and_only_offers_the_godot_plugin() {
fn walk(cmd: &clap::Command, path: &[String], found: &mut Vec<String>) {
if let Some(arg) = cmd
.get_arguments()
.find(|arg| arg.get_long() == Some("upload-source"))
{
let command = path.join(" ");
assert!(
arg.is_hide_set(),
"`{}` lists --upload-source in its help",
command
);
let values: Vec<String> = arg
.get_possible_values()
.into_iter()
.map(|value| value.get_name().to_string())
.collect();
assert_eq!(
values,
["godot-plugin"],
"`{}` offers a source other than the Godot plugin's",
command
);
found.push(command);
}

for sub in cmd.get_subcommands() {
let mut sub_path = path.to_vec();
sub_path.push(sub.get_name().to_string());
walk(sub, &sub_path, found);
}
}

let mut found = Vec::new();
walk(&Cli::command(), &["wavedash".to_string()], &mut found);
found.sort();

assert_eq!(
found,
["wavedash build push", "wavedash dev"],
"every command that creates a build row should be able to name its source"
);
}

#[test]
fn upload_source_parses_the_plugin_and_defaults_to_the_cli() {
fn push_source(argv: &[&str]) -> Option<UploadSource> {
let parsed = Cli::try_parse_from(argv).expect("should parse");
let Some(Commands::Build {
action: BuildCommands::Push { upload_source, .. },
}) = parsed.command
else {
panic!("`{:?}` did not parse as `build push`", argv);
};
upload_source
}

assert_eq!(
push_source(&[
"wavedash",
"build",
"push",
"--upload-source",
"godot-plugin"
]),
Some(UploadSource::GodotPlugin)
);
assert_eq!(push_source(&["wavedash", "build", "push"]), None);
assert_eq!(
push_source(&["wavedash", "build", "push"]).unwrap_or_default(),
UploadSource::Cli
);

// Not `try_parse_from`: `expect_err` needs the Ok type to be `Debug`, and
// `Cli` isn't — the fix is here, not a `derive(Debug)` on `Cli`.
for rejected in ["cli", "CLI", "web", "WEB", "godot", "GODOT_PLUGIN", ""] {
let err = Cli::command()
.try_get_matches_from(["wavedash", "build", "push", "--upload-source", rejected])
.expect_err(&format!(
"`build push` accepted --upload-source {:?}",
rejected
));
assert_eq!(
err.kind(),
clap::error::ErrorKind::InvalidValue,
"--upload-source {:?} was rejected for the wrong reason: {}",
rejected,
err
);
}
}
}
Loading