Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ jobs:
env:
DOTNET_INSTALL_DIR: ~/.dotnet

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'

- name: Build
working-directory: crates/bench/
run: |
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ jobs:
with:
run_install: true

# Go is required for Go module compilation tests.
- name: Set up Go
if: runner.os == 'Linux'
uses: actions/setup-go@v5
with:
go-version: '1.25'

# Install emscripten for C++ module compilation tests.
- name: Install emscripten (Linux)
if: runner.os == 'Linux'
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/go-sdk-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Go SDK Tests

on:
pull_request:
paths:
- 'sdks/go/**'
- 'crates/codegen/src/go.rs'
- '.github/workflows/go-sdk-test.yml'
push:
branches:
- master
paths:
- 'sdks/go/**'
- 'crates/codegen/src/go.rs'
merge_group:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || format('sha-{0}', github.sha) }}
cancel-in-progress: true

jobs:
go-unit-tests:
name: Go SDK Unit Tests
runs-on: spacetimedb-new-runner-2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
cache-dependency-path: sdks/go/go.sum
- name: Run Go tests
working-directory: sdks/go
run: go test -v -count=1 ./...
- name: Run Go vet
working-directory: sdks/go
run: go vet ./...
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

### Go ###
*generated.go

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

Expand Down
8 changes: 5 additions & 3 deletions crates/bench/benches/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use spacetimedb_bench::{
};
use spacetimedb_lib::sats::AlgebraicType;
use spacetimedb_primitives::ColId;
use spacetimedb_testing::modules::{Csharp, Rust};
use spacetimedb_testing::modules::{Csharp, GoBenchmarks, Rust};

#[cfg(target_env = "msvc")]
#[global_allocator]
Expand All @@ -32,12 +32,14 @@ fn criterion_benchmark(c: &mut Criterion) {
bench_suite::<sqlite::SQLite>(c, true).unwrap();
bench_suite::<spacetime_raw::SpacetimeRaw>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, true).unwrap();
// bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<GoBenchmarks>>(c, true).unwrap();

bench_suite::<sqlite::SQLite>(c, false).unwrap();
bench_suite::<spacetime_raw::SpacetimeRaw>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, false).unwrap();
// bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<GoBenchmarks>>(c, false).unwrap();
}

#[inline(never)]
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/src/spacetime_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ impl SpacetimeConfig {
"typescript" => package_manager.map(|pm| pm.run_dev_command()).unwrap_or("npm run dev"),
"rust" => "cargo run",
"csharp" | "c#" => "dotnet run",
"go" | "golang" => "go run .",
_ => "npm run dev", // default fallback
};
Self {
Expand Down Expand Up @@ -1156,6 +1157,11 @@ pub fn detect_client_command(project_dir: &Path) -> Option<(String, Option<Packa
}
}

// Go: go.mod file
if project_dir.join("go.mod").exists() {
return Some(("go run .".to_string(), None));
}

None
}

Expand Down
32 changes: 30 additions & 2 deletions crates/cli/src/subcommands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use clap::Arg;
use clap::ArgAction::{Set, SetTrue};
use fs_err as fs;
use spacetimedb_codegen::{
generate, private_table_names, CodegenOptions, CodegenVisibility, Csharp, Lang, OutputFile, Rust, TypeScript,
generate, private_table_names, CodegenOptions, CodegenVisibility, Csharp, Go, Lang, OutputFile, Rust, TypeScript,
UnrealCpp, AUTO_GENERATED_PREFIX,
};
use spacetimedb_lib::de::serde::DeserializeWrapper;
Expand Down Expand Up @@ -395,6 +395,9 @@ fn detect_default_language(client_project_dir: &Path) -> anyhow::Result<Language
return Ok(Language::Csharp);
}
}
if client_project_dir.join("go.mod").exists() {
return Ok(Language::Go);
}

anyhow::bail!(
"Could not auto-detect client language from '{}'. \
Expand All @@ -409,6 +412,7 @@ fn language_cli_name(lang: Language) -> &'static str {
Language::Csharp => "csharp",
Language::TypeScript => "typescript",
Language::UnrealCpp => "unrealcpp",
Language::Go => "go",
}
}

Expand All @@ -417,6 +421,7 @@ pub fn default_out_dir_for_language(lang: Language) -> Option<PathBuf> {
Language::Rust | Language::TypeScript => Some(PathBuf::from("src/module_bindings")),
Language::Csharp => Some(PathBuf::from("module_bindings")),
Language::UnrealCpp => None,
Language::Go => Some(PathBuf::from("module_bindings/")),
}
}

Expand Down Expand Up @@ -517,6 +522,7 @@ pub async fn run_prepared_generate_configs(
}
Language::Rust => &Rust,
Language::TypeScript => &TypeScript,
Language::Go => &Go,
};

for OutputFile { filename, code } in generate(&module, gen_lang, &options) {
Expand Down Expand Up @@ -679,18 +685,21 @@ pub enum Language {
Rust,
#[serde(alias = "uecpp", alias = "ue5cpp", alias = "unreal")]
UnrealCpp,
#[serde(alias = "golang")]
Go,
}

impl clap::ValueEnum for Language {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Csharp, Self::TypeScript, Self::Rust, Self::UnrealCpp]
&[Self::Csharp, Self::TypeScript, Self::Rust, Self::UnrealCpp, Self::Go]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(match self {
Self::Csharp => clap::builder::PossibleValue::new("csharp").aliases(["c#", "cs"]),
Self::TypeScript => clap::builder::PossibleValue::new("typescript").aliases(["ts", "TS"]),
Self::Rust => clap::builder::PossibleValue::new("rust").aliases(["rs", "RS"]),
Self::UnrealCpp => PossibleValue::new("unrealcpp").aliases(["uecpp", "ue5cpp", "unreal"]),
Self::Go => PossibleValue::new("go").aliases(["golang"]),
})
}
}
Expand All @@ -703,6 +712,7 @@ impl Language {
Language::Csharp => "C#",
Language::TypeScript => "TypeScript",
Language::UnrealCpp => "Unreal C++",
Language::Go => "Go",
}
}

Expand All @@ -716,6 +726,16 @@ impl Language {
Language::UnrealCpp => {
// TODO: implement formatting.
}
Language::Go => {
let files: Vec<_> = generated_files.iter().map(|f| f.as_os_str()).collect();
let status = std::process::Command::new("gofmt")
.arg("-w")
.args(&files)
.status();
if let Err(e) = status {
eprintln!("Warning: gofmt not available: {e}");
}
}
}

Ok(())
Expand Down Expand Up @@ -1382,6 +1402,14 @@ mod tests {
serde_json::from_value::<Language>(serde_json::Value::String("unreal".into())).unwrap(),
Language::UnrealCpp
);
assert_eq!(
serde_json::from_value::<Language>(serde_json::Value::String("go".into())).unwrap(),
Language::Go
);
assert_eq!(
serde_json::from_value::<Language>(serde_json::Value::String("golang".into())).unwrap(),
Language::Go
);

// Invalid language should error
assert!(serde_json::from_value::<Language>(serde_json::Value::String("java".into())).is_err());
Expand Down
Loading