diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9813456..ca975ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ permissions: contents: write jobs: - goreleaser: + release: runs-on: ubuntu-latest steps: - name: Checkout @@ -23,11 +23,15 @@ jobs: GITHUB_TOKEN: ${{ github.token }} - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - uses: actions/setup-node@v4 with: - go-version: 1.17 + node-version: lts - - name: Run GoReleaser + - name: Build Go Binaries uses: goreleaser/goreleaser-action@v2 with: distribution: goreleaser @@ -36,10 +40,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-node@v1 - with: - node-version: 10 - - name: Update version in package.json uses: actions/github-script@v6 with: @@ -50,7 +50,7 @@ jobs: packageJson.version = '${{ steps.get_release.outputs.tag_name }}'.replace(/^v/, ''); fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2)); - - run: npm install + - run: npm ci - uses: JS-DevTools/npm-publish@v1 with: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2853174..93d465f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -15,7 +15,7 @@ builds: - linux - windows - darwin - main: ./cmd/crx/main.go + main: ./cmd/main.go binary: crx ldflags: - -s -w -X github.com/customrealms/cli/pkg/version.Version={{.Version}} diff --git a/cmd/cmd_build.go b/cmd/cmd_build.go new file mode 100644 index 0000000..6cc1c4a --- /dev/null +++ b/cmd/cmd_build.go @@ -0,0 +1,55 @@ +package main + +import ( + "os" + + "github.com/customrealms/cli/internal/actions/build" + "github.com/customrealms/cli/internal/project" +) + +type BuildCmd struct { + ProjectDir string `name:"project" short:"p" usage:"plugin project directory" optional:""` + McVersion string `name:"mc" usage:"Minecraft version number target" optional:""` + TemplateJarFile string `name:"jar" short:"t" usage:"template JAR file" optional:""` + OutputFile string `name:"output" short:"o" usage:"output JAR file path"` +} + +func (c *BuildCmd) Run() error { + // Root context for the CLI + ctx, cancel := rootContext() + defer cancel() + + // Default to the current working directory + if c.ProjectDir == "" { + c.ProjectDir, _ = os.Getwd() + } + + // Get the Minecraft version + minecraftVersion := mustMinecraftVersion(ctx, c.McVersion) + + // Create the JAR template to build with + var jarTemplate build.JarTemplate + if len(c.TemplateJarFile) > 0 { + jarTemplate = &build.FileJarTemplate{ + Filename: c.TemplateJarFile, + } + } else { + jarTemplate = &build.GitHubJarTemplate{ + MinecraftVersion: minecraftVersion, + } + } + + // Create the project + crProject := project.Project{ + Dir: c.ProjectDir, + } + + // Create the build action + buildAction := build.BuildAction{ + Project: &crProject, + JarTemplate: jarTemplate, + MinecraftVersion: minecraftVersion, + OutputFile: c.OutputFile, + } + return buildAction.Run(ctx) +} diff --git a/cmd/cmd_init.go b/cmd/cmd_init.go new file mode 100644 index 0000000..07ce8e6 --- /dev/null +++ b/cmd/cmd_init.go @@ -0,0 +1,31 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/customrealms/cli/internal/actions/initialize" +) + +type InitCmd struct { + ProjectDir string `name:"project" short:"p" usage:"plugin project directory" optional:""` +} + +func (c *InitCmd) Run() error { + // Root context for the CLI + ctx, cancel := rootContext() + defer cancel() + + // Default to the current working directory + if c.ProjectDir == "" { + c.ProjectDir, _ = os.Getwd() + } + + // Create the init runner + initAction := initialize.InitAction{ + Name: filepath.Base(c.ProjectDir), + Dir: c.ProjectDir, + Template: nil, + } + return initAction.Run(ctx) +} diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go new file mode 100644 index 0000000..df0f1b4 --- /dev/null +++ b/cmd/cmd_run.go @@ -0,0 +1,80 @@ +package main + +import ( + "os" + + "github.com/customrealms/cli/internal/actions/build" + "github.com/customrealms/cli/internal/actions/serve" + "github.com/customrealms/cli/internal/project" + "github.com/customrealms/cli/internal/server" +) + +type RunCmd struct { + ProjectDir string `name:"project" short:"p" usage:"plugin project directory" optional:""` + McVersion string `name:"mc" short:"mc" usage:"Minecraft version number target" optional:""` + TemplateJarFile string `name:"jar" short:"t" usage:"template JAR file" optional:""` +} + +func (c *RunCmd) Run() error { + // Root context for the CLI + ctx, cancel := rootContext() + defer cancel() + + // Default to the current working directory + if c.ProjectDir == "" { + c.ProjectDir, _ = os.Getwd() + } + + // Get the Minecraft version + minecraftVersion := mustMinecraftVersion(ctx, c.McVersion) + + // Generate a temp filename for the plugin JAR file + ofile, _ := os.CreateTemp("", "cr-jar-output-*.jar") + ofile.Close() + outputFile := ofile.Name() + defer os.Remove(outputFile) + + // Create the JAR template to build with + var jarTemplate build.JarTemplate + if len(c.TemplateJarFile) > 0 { + jarTemplate = &build.FileJarTemplate{ + Filename: c.TemplateJarFile, + } + } else { + jarTemplate = &build.GitHubJarTemplate{ + MinecraftVersion: minecraftVersion, + } + } + + // Create the project + crProject := project.Project{ + Dir: c.ProjectDir, + } + + // Create the build action + buildAction := build.BuildAction{ + Project: &crProject, + JarTemplate: jarTemplate, + MinecraftVersion: minecraftVersion, + OutputFile: outputFile, + } + + // Run the build action + if err := buildAction.Run(ctx); err != nil { + return err + } + + // Create a fetcher for the Minecraft server JAR file that caches the files locally + serverJarFetcher, err := server.NewCachedFetcher(&server.HttpFetcher{}) + if err != nil { + return err + } + + // Create the serve runner + serveAction := serve.ServeAction{ + MinecraftVersion: minecraftVersion, + PluginJarPath: outputFile, + ServerJarFetcher: serverJarFetcher, + } + return serveAction.Run(ctx) +} diff --git a/cmd/cmd_version.go b/cmd/cmd_version.go new file mode 100644 index 0000000..8113afe --- /dev/null +++ b/cmd/cmd_version.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + + "github.com/customrealms/cli/pkg/version" +) + +type VersionCmd struct{} + +func (c *VersionCmd) Run() error { + fmt.Printf("@customrealms/cli (crx) v%s\n", version.Version) + return nil +} diff --git a/cmd/cmd_yml.go b/cmd/cmd_yml.go new file mode 100644 index 0000000..156c539 --- /dev/null +++ b/cmd/cmd_yml.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "os" + + "github.com/customrealms/cli/internal/actions/build" + "github.com/customrealms/cli/internal/project" +) + +type YmlCmd struct { + ProjectDir string `name:"project" short:"p" usage:"plugin project directory" optional:""` + McVersion string `name:"mc" short:"mc" usage:"Minecraft version number target" optional:""` +} + +func (c *YmlCmd) Run() error { + // Root context for the CLI + ctx, cancel := rootContext() + defer cancel() + + // Default to the current working directory + if c.ProjectDir == "" { + c.ProjectDir, _ = os.Getwd() + } + + // Get the Minecraft version + minecraftVersion := mustMinecraftVersion(ctx, c.McVersion) + + // Create the project + crProject := project.Project{ + Dir: c.ProjectDir, + } + + // Read the package.json file + packageJson, err := crProject.PackageJSON() + if err != nil { + return err + } + + // Define the plugin.yml details for the plugin + pluginYml := &build.PluginYml{ + MinecraftVersion: minecraftVersion, + PackageJSON: packageJson, + } + fmt.Println(pluginYml) + + return nil +} diff --git a/cmd/crx/main.go b/cmd/crx/main.go deleted file mode 100644 index 2573903..0000000 --- a/cmd/crx/main.go +++ /dev/null @@ -1,373 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - "os/signal" - "path/filepath" - "syscall" - - "github.com/customrealms/cli/actions/build" - "github.com/customrealms/cli/actions/initialize" - "github.com/customrealms/cli/actions/serve" - "github.com/customrealms/cli/minecraft" - "github.com/customrealms/cli/project" - "github.com/customrealms/cli/server" -) - -const VERSION = "0.4.10" - -func main() { - - // Define the map of commands - commands := map[string]func() error{ - "version": func() error { - fmt.Printf("customrealms-cli (crx) v%s\n", VERSION) - return nil - }, - "init": crxInit, - "serve": crxServe, - "build": crxBuild, - "run": crxBuildAndServe, - "yml": crxYaml, - } - - // If there are no command line arguments - if len(os.Args) <= 1 { - fmt.Println("Missing command name.") - fmt.Println("List of available commands:") - for cmd := range commands { - fmt.Printf(" -> crx %s ...\n", cmd) - } - fmt.Println() - os.Exit(1) - } - - // Check for the command function - commandFunc, ok := commands[os.Args[1]] - if !ok { - fmt.Printf("Unsupported command %q\n", os.Args[1]) - fmt.Println("List of available commands:") - for cmd := range commands { - fmt.Printf(" -> crx %s ...\n", cmd) - } - fmt.Println() - os.Exit(1) - } - - // Run the command function - if err := commandFunc(); err != nil { - fmt.Println(err) - os.Exit(1) - } - -} - -// mustMinecraftVersion takes a user-supplied Minecraft version string and resolves the corresponding minecraft.Version -// instance. If nothing can be found, it exits the process -func mustMinecraftVersion(versionString string) minecraft.Version { - if len(versionString) == 0 { - mcVersion := minecraft.LatestVersion() - if mcVersion == nil { - fmt.Println("Failed to resolve the default Minecraft version") - os.Exit(1) - } - return mcVersion - } else { - minecraftVersion := minecraft.FindVersion(versionString) - if minecraftVersion == nil { - fmt.Println("Unsupported Minecraft version: ", versionString) - fmt.Println() - fmt.Println("Please use a supported Minecraft version:") - for _, version := range minecraft.SupportedVersions { - fmt.Println(" -> ", version) - } - fmt.Println() - os.Exit(1) - } - return minecraftVersion - } -} - -func crxYaml() error { - - // Parse command line arguments - var projectDir string - var mcVersion string - flag.StringVar(&projectDir, "p", ".", "plugin project directory") - flag.StringVar(&mcVersion, "mc", "", "Minecraft version number target") - flag.CommandLine.Parse(os.Args[2:]) - - // Get the Minecraft version - minecraftVersion := mustMinecraftVersion(mcVersion) - - // Create the project - crProject := project.Project{ - Dir: projectDir, - } - - // Read the package.json file - packageJson, err := crProject.PackageJSON() - if err != nil { - return err - } - - // Define the plugin.yml details for the plugin - pluginYml := &build.PluginYml{ - MinecraftVersion: minecraftVersion, - PackageJSON: packageJson, - } - fmt.Println(pluginYml) - - return nil - -} - -func crxInit() error { - - // Parse command line arguments - cwd, _ := os.Getwd() - var projectDir string - flag.StringVar(&projectDir, "p", cwd, "plugin project directory") - flag.CommandLine.Parse(os.Args[2:]) - - // Create the init runner - initAction := initialize.InitAction{ - Name: filepath.Base(projectDir), - Dir: projectDir, - Template: nil, - } - - // Run the init action - return initAction.Run(context.Background()) - -} - -func crxBuild() error { - - // Parse command line arguments - var projectDir string - var mcVersion string - var outputFile string - var templateJarFile string - flag.StringVar(&projectDir, "p", ".", "plugin project directory") - flag.StringVar(&mcVersion, "mc", "", "Minecraft version number target") - flag.StringVar(&outputFile, "o", "", "output JAR file path") - flag.StringVar(&templateJarFile, "jar", "", "template JAR file") - flag.CommandLine.Parse(os.Args[2:]) - - // Require the output file path - if len(outputFile) == 0 { - fmt.Println("Output JAR file is required: -o option") - os.Exit(1) - } - - // Get the Minecraft version - minecraftVersion := mustMinecraftVersion(mcVersion) - - // Create the JAR template to build with - var jarTemplate build.JarTemplate - if len(templateJarFile) > 0 { - jarTemplate = &build.FileJarTemplate{ - Filename: templateJarFile, - } - } else { - jarTemplate = &build.GitHubJarTemplate{ - MinecraftVersion: minecraftVersion, - } - } - - // Create the project - crProject := project.Project{ - Dir: projectDir, - } - - // Create the build action - buildAction := build.BuildAction{ - Project: &crProject, - JarTemplate: jarTemplate, - MinecraftVersion: minecraftVersion, - OutputFile: outputFile, - } - - // Run the build action - return buildAction.Run(context.Background()) - -} - -func crxJar() error { - - // Parse command line arguments - var projectDir string - var mcVersion string - var outputFile string - var templateJarFile string - flag.StringVar(&projectDir, "p", ".", "plugin project directory") - flag.StringVar(&mcVersion, "mc", "", "Minecraft version number target") - flag.StringVar(&outputFile, "o", "", "output JAR file path") - flag.StringVar(&templateJarFile, "jar", "", "template JAR file") - flag.CommandLine.Parse(os.Args[2:]) - - // Require the output file path - if len(outputFile) == 0 { - fmt.Println("Output JAR file is required: -o option") - os.Exit(1) - } - - // Get the Minecraft version - minecraftVersion := mustMinecraftVersion(mcVersion) - - // Create the JAR template to build with - var jarTemplate build.JarTemplate - if len(templateJarFile) > 0 { - jarTemplate = &build.FileJarTemplate{ - Filename: templateJarFile, - } - } else { - jarTemplate = &build.GitHubJarTemplate{ - MinecraftVersion: minecraftVersion, - } - } - - // Create the project - crProject := project.Project{ - Dir: projectDir, - } - - // Create the build action - buildAction := build.JarAction{ - Project: &crProject, - JarTemplate: jarTemplate, - MinecraftVersion: minecraftVersion, - OutputFile: outputFile, - } - - // Run the build action - return buildAction.Run(context.Background()) - -} - -func crxServe() error { - - // Parse command line arguments - var jarFile string - var mcVersion string - flag.StringVar(&jarFile, "jar", "", "path to the plugin JAR file") - flag.StringVar(&mcVersion, "mc", "", "Minecraft version number target") - flag.CommandLine.Parse(os.Args[2:]) - - // Require the JAR file path - if len(jarFile) == 0 { - fmt.Println("JAR file path is required: -jar option") - os.Exit(1) - } - - // Get the Minecraft version - minecraftVersion := mustMinecraftVersion(mcVersion) - - // Create the context - ctx := context.Background() - ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) - defer cancel() - - // Create a fetcher for the Minecraft server JAR file that caches the files locally - serverJarFetcher, err := server.NewCachedFetcher(&server.HttpFetcher{}) - if err != nil { - return err - } - - // Create the serve runner - serveAction := serve.ServeAction{ - MinecraftVersion: minecraftVersion, - PluginJarPath: jarFile, - ServerJarFetcher: serverJarFetcher, - } - - // Run the init action - return serveAction.Run(ctx) - -} - -func crxBuildAndServe() error { - - // Parse command line arguments - var projectDir string - var mcVersion string - var outputFile string - var templateJarFile string - flag.StringVar(&projectDir, "p", ".", "plugin project directory") - flag.StringVar(&mcVersion, "mc", "", "Minecraft version number target") - flag.StringVar(&outputFile, "o", "", "output JAR file path") - flag.StringVar(&templateJarFile, "jar", "", "template JAR file") - flag.CommandLine.Parse(os.Args[2:]) - - // Get the Minecraft version - minecraftVersion := mustMinecraftVersion(mcVersion) - - // If there is no output file provided, default to a temp file - if len(outputFile) == 0 { - - // Generate a temp filename for the plugin JAR file - ofile, _ := os.CreateTemp("", "cr-jar-output-*.jar") - ofile.Close() - outputFile = ofile.Name() - - // Make sure to delete the generated file at the end - defer os.Remove(outputFile) - - } - - // Create the context - ctx := context.Background() - ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) - defer cancel() - - // Create the JAR template to build with - var jarTemplate build.JarTemplate - if len(templateJarFile) > 0 { - jarTemplate = &build.FileJarTemplate{ - Filename: templateJarFile, - } - } else { - jarTemplate = &build.GitHubJarTemplate{ - MinecraftVersion: minecraftVersion, - } - } - - // Create the project - crProject := project.Project{ - Dir: projectDir, - } - - // Create the build action - buildAction := build.BuildAction{ - Project: &crProject, - JarTemplate: jarTemplate, - MinecraftVersion: minecraftVersion, - OutputFile: outputFile, - } - - // Run the build action - if err := buildAction.Run(ctx); err != nil { - return err - } - - // Create a fetcher for the Minecraft server JAR file that caches the files locally - serverJarFetcher, err := server.NewCachedFetcher(&server.HttpFetcher{}) - if err != nil { - return err - } - - // Create the serve runner - serveAction := serve.ServeAction{ - MinecraftVersion: minecraftVersion, - PluginJarPath: outputFile, - ServerJarFetcher: serverJarFetcher, - } - - // Run the init action - return serveAction.Run(ctx) - -} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..e31ec14 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/alecthomas/kong" + "github.com/customrealms/cli/internal/minecraft" +) + +var cli struct { + VersionCmd VersionCmd `cmd:"" help:"Show the version of the CLI."` + InitCmd InitCmd `cmd:"" help:"Initialize a new plugin project."` + BuildCmd BuildCmd `cmd:"" help:"Build the plugin JAR file."` + RunCmd RunCmd `cmd:"" help:"Build and serve the plugin in a Minecraft server."` + YmlCmd YmlCmd `cmd:"" help:"Generate the plugin.yml file."` +} + +func rootContext() (context.Context, context.CancelFunc) { + ctx := context.Background() + return signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) +} + +func main() { + ctx := kong.Parse(&cli) + err := ctx.Run() + ctx.FatalIfErrorf(err) +} + +// mustMinecraftVersion takes a user-supplied Minecraft version string and resolves the corresponding minecraft.Version +// instance. If nothing can be found, it exits the process +func mustMinecraftVersion(ctx context.Context, versionString string) minecraft.Version { + if len(versionString) == 0 { + versionString = "1.20.6" + } + minecraftVersion, err := minecraft.LookupVersion(ctx, versionString) + if err != nil { + fmt.Println("Failed to resolve the Minecraft version: ", err) + os.Exit(1) + } + return minecraftVersion +} diff --git a/go.mod b/go.mod index 5b0504f..a2a9c5d 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/customrealms/cli -go 1.17 +go 1.22 + +require github.com/alecthomas/kong v0.9.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..524151d --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA= +github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= diff --git a/actions/action.go b/internal/actions/action.go similarity index 100% rename from actions/action.go rename to internal/actions/action.go diff --git a/actions/build/build.go b/internal/actions/build/build.go similarity index 94% rename from actions/build/build.go rename to internal/actions/build/build.go index 0e3030f..945fc8d 100644 --- a/actions/build/build.go +++ b/internal/actions/build/build.go @@ -9,8 +9,8 @@ import ( "path/filepath" "time" - "github.com/customrealms/cli/minecraft" - "github.com/customrealms/cli/project" + "github.com/customrealms/cli/internal/minecraft" + "github.com/customrealms/cli/internal/project" ) //go:embed config/webpack.config.js diff --git a/actions/build/config/webpack.config.js b/internal/actions/build/config/webpack.config.js similarity index 100% rename from actions/build/config/webpack.config.js rename to internal/actions/build/config/webpack.config.js diff --git a/actions/build/jar.go b/internal/actions/build/jar.go similarity index 97% rename from actions/build/jar.go rename to internal/actions/build/jar.go index 7f1757a..dff3ebe 100644 --- a/actions/build/jar.go +++ b/internal/actions/build/jar.go @@ -10,8 +10,8 @@ import ( "path/filepath" "strings" - "github.com/customrealms/cli/minecraft" - "github.com/customrealms/cli/project" + "github.com/customrealms/cli/internal/minecraft" + "github.com/customrealms/cli/internal/project" ) type JarAction struct { diff --git a/actions/build/jar_template.go b/internal/actions/build/jar_template.go similarity index 100% rename from actions/build/jar_template.go rename to internal/actions/build/jar_template.go diff --git a/actions/build/jar_template_file.go b/internal/actions/build/jar_template_file.go similarity index 100% rename from actions/build/jar_template_file.go rename to internal/actions/build/jar_template_file.go diff --git a/actions/build/jar_template_github.go b/internal/actions/build/jar_template_github.go similarity index 59% rename from actions/build/jar_template_github.go rename to internal/actions/build/jar_template_github.go index 022248d..a4882f6 100644 --- a/actions/build/jar_template_github.go +++ b/internal/actions/build/jar_template_github.go @@ -5,24 +5,17 @@ import ( "io" "net/http" - "github.com/customrealms/cli/minecraft" + "github.com/customrealms/cli/internal/minecraft" ) type GitHubJarTemplate struct { MinecraftVersion minecraft.Version } -func (t *GitHubJarTemplate) getJarUrl() string { - return fmt.Sprintf( - "https://github.com/customrealms/bukkit-runtime/releases/latest/download/bukkit-runtime-%s.jar", - t.MinecraftVersion, - ) -} - func (t *GitHubJarTemplate) Jar() (io.ReadCloser, error) { // Get the JAR url - jarUrl := t.getJarUrl() + jarUrl := t.MinecraftVersion.PluginJarUrl() // Download the JAR file fmt.Printf(" -> %s\n", jarUrl) diff --git a/actions/build/pluginyml.go b/internal/actions/build/pluginyml.go similarity index 96% rename from actions/build/pluginyml.go rename to internal/actions/build/pluginyml.go index 8ee3c40..b4ddea7 100644 --- a/actions/build/pluginyml.go +++ b/internal/actions/build/pluginyml.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/customrealms/cli/minecraft" - "github.com/customrealms/cli/project" + "github.com/customrealms/cli/internal/minecraft" + "github.com/customrealms/cli/internal/project" ) const JAR_MAIN_CLASS = "io.customrealms.MainPlugin" diff --git a/actions/initialize/initialize.go b/internal/actions/initialize/initialize.go similarity index 95% rename from actions/initialize/initialize.go rename to internal/actions/initialize/initialize.go index c5c6e91..bc7e48b 100644 --- a/actions/initialize/initialize.go +++ b/internal/actions/initialize/initialize.go @@ -6,7 +6,7 @@ import ( "os" "os/exec" - "github.com/customrealms/cli/actions/initialize/template" + "github.com/customrealms/cli/internal/actions/initialize/template" ) type InitAction struct { diff --git a/actions/initialize/template/installer.go b/internal/actions/initialize/template/installer.go similarity index 100% rename from actions/initialize/template/installer.go rename to internal/actions/initialize/template/installer.go diff --git a/actions/initialize/template/template.go b/internal/actions/initialize/template/template.go similarity index 100% rename from actions/initialize/template/template.go rename to internal/actions/initialize/template/template.go diff --git a/actions/initialize/template/template_github.go b/internal/actions/initialize/template/template_github.go similarity index 100% rename from actions/initialize/template/template_github.go rename to internal/actions/initialize/template/template_github.go diff --git a/actions/serve/serve.go b/internal/actions/serve/serve.go similarity index 97% rename from actions/serve/serve.go rename to internal/actions/serve/serve.go index d0f68c0..75f3673 100644 --- a/actions/serve/serve.go +++ b/internal/actions/serve/serve.go @@ -8,8 +8,8 @@ import ( "os/exec" "path/filepath" - "github.com/customrealms/cli/minecraft" - "github.com/customrealms/cli/server" + "github.com/customrealms/cli/internal/minecraft" + "github.com/customrealms/cli/internal/server" ) type ServeAction struct { diff --git a/minecraft/version.go b/internal/minecraft/version.go similarity index 85% rename from minecraft/version.go rename to internal/minecraft/version.go index c1bdf7e..baf3cff 100644 --- a/minecraft/version.go +++ b/internal/minecraft/version.go @@ -5,4 +5,5 @@ type Version interface { ApiVersion() string ServerJarType() string ServerJarUrl() string + PluginJarUrl() string } diff --git a/minecraft/version_papermc.go b/internal/minecraft/version_papermc.go similarity index 77% rename from minecraft/version_papermc.go rename to internal/minecraft/version_papermc.go index 4f49f2a..2f3f715 100644 --- a/minecraft/version_papermc.go +++ b/internal/minecraft/version_papermc.go @@ -32,3 +32,7 @@ func (v *paperMcVersion) ServerJarUrl() string { v.paperBuild, ) } + +func (v *paperMcVersion) PluginJarUrl() string { + return fmt.Sprintf("https://github.com/customrealms/bukkit-runtime/releases/latest/download/bukkit-runtime-%s.jar", v.version) +} diff --git a/internal/minecraft/versions.go b/internal/minecraft/versions.go new file mode 100644 index 0000000..f102b4f --- /dev/null +++ b/internal/minecraft/versions.go @@ -0,0 +1,88 @@ +package minecraft + +import ( + "context" + "encoding/json" + "fmt" + "net/http" +) + +type paperMcBuilds struct { + Builds []paperMcBuild `json:"builds"` +} + +type paperMcBuild struct { + Build int `json:"build"` + Channel string `json:"channel"` + Downloads struct { + Application struct { + Name string `json:"name"` + Sha256 string `json:"sha256"` + } `json:"application"` + } `json:"downloads"` +} + +func LookupVersion(ctx context.Context, versionStr string) (Version, error) { + // Lookup the version from PaperMC + builds, err := downloadJSON[paperMcBuilds](ctx, fmt.Sprintf("https://papermc.io/api/v2/projects/paper/versions/%s", versionStr)) + if err != nil { + return nil, fmt.Errorf("download builds list: %w", err) + } + if builds == nil || len(builds.Builds) == 0 { + return nil, fmt.Errorf("no builds found for version %s", versionStr) + } + + // The last entry is the latest build + build := builds.Builds[len(builds.Builds)-1] + version := &paperMcVersion{versionStr, build.Build} + + // Check that the version has a downloadable plugin JAR + ok, err := checkHttpOK(ctx, version.PluginJarUrl()) + if err != nil { + return nil, fmt.Errorf("check plugin jar url: %w", err) + } + if !ok { + return nil, fmt.Errorf("no customrealms bukkit-runtime found for version %s", versionStr) + } + return version, nil +} + +func downloadJSON[T any](ctx context.Context, url string) (*T, error) { + // Create the HTTP request + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("create http request: %w", err) + } + + // Send the HTTP request + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send http request: %w", err) + } + defer res.Body.Close() + + // Decode the JSON response + var result T + if err := json.NewDecoder(res.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("decode json response: %w", err) + } + return &result, nil +} + +func checkHttpOK(ctx context.Context, url string) (bool, error) { + // Create the HTTP request + req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil) + if err != nil { + return false, fmt.Errorf("create http request: %w", err) + } + + // Send the HTTP request + res, err := http.DefaultClient.Do(req) + if err != nil { + return false, fmt.Errorf("send http request: %w", err) + } + defer res.Body.Close() + + // Check the status code + return res.StatusCode == http.StatusOK, nil +} diff --git a/project/package_json.go b/internal/project/package_json.go similarity index 100% rename from project/package_json.go rename to internal/project/package_json.go diff --git a/project/project.go b/internal/project/project.go similarity index 100% rename from project/project.go rename to internal/project/project.go diff --git a/server/fetcher.go b/internal/server/fetcher.go similarity index 70% rename from server/fetcher.go rename to internal/server/fetcher.go index e42726a..8a89712 100644 --- a/server/fetcher.go +++ b/internal/server/fetcher.go @@ -3,7 +3,7 @@ package server import ( "io" - "github.com/customrealms/cli/minecraft" + "github.com/customrealms/cli/internal/minecraft" ) type JarFetcher interface { diff --git a/server/fetcher_cache.go b/internal/server/fetcher_cache.go similarity index 97% rename from server/fetcher_cache.go rename to internal/server/fetcher_cache.go index dbb31cb..edd1dae 100644 --- a/server/fetcher_cache.go +++ b/internal/server/fetcher_cache.go @@ -7,7 +7,7 @@ import ( "os" "path" - "github.com/customrealms/cli/minecraft" + "github.com/customrealms/cli/internal/minecraft" ) type cachedFetcher struct { diff --git a/server/fetcher_http.go b/internal/server/fetcher_http.go similarity index 87% rename from server/fetcher_http.go rename to internal/server/fetcher_http.go index 947d123..e365b6f 100644 --- a/server/fetcher_http.go +++ b/internal/server/fetcher_http.go @@ -4,7 +4,7 @@ import ( "io" "net/http" - "github.com/customrealms/cli/minecraft" + "github.com/customrealms/cli/internal/minecraft" ) type HttpFetcher struct{} diff --git a/minecraft/versions.go b/minecraft/versions.go deleted file mode 100644 index 466700a..0000000 --- a/minecraft/versions.go +++ /dev/null @@ -1,28 +0,0 @@ -package minecraft - -// SupportedVersions slice of all the supported Minecraft versions. To be supported, two things must be true: -// 1. We must have a JAR build of `bukkit-runtime` for that version -// 2. There must be a PaperMC build in that Minecraft version -var SupportedVersions = []Version{ - &paperMcVersion{"1.20.6", 147}, - &paperMcVersion{"1.17.1", 399}, - &paperMcVersion{"1.16.5", 792}, -} - -// FindVersion finds a supported version with the given version string -func FindVersion(version string) Version { - for _, v := range SupportedVersions { - if v.String() == version || v.ApiVersion() == version { - return v - } - } - return nil -} - -// LatestVersion gets the latest Minecraft version available -func LatestVersion() Version { - if len(SupportedVersions) == 0 { - return nil - } - return SupportedVersions[0] -} diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..cfac9a3 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,4 @@ +package version + +// Version is the current version of the CLI. This is set at build time. +var Version string