diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/grif-cli.iml b/.idea/cli.iml similarity index 74% rename from .idea/grif-cli.iml rename to .idea/cli.iml index 5398c41..53f41fe 100644 --- a/.idea/grif-cli.iml +++ b/.idea/cli.iml @@ -3,7 +3,8 @@ - + + diff --git a/.idea/modules.xml b/.idea/modules.xml index d40b60d..6fcc0dc 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/remote-targets.xml b/.idea/remote-targets.xml deleted file mode 100644 index 100d818..0000000 --- a/.idea/remote-targets.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..2ee9a0a 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,27 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..d9f53e2 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + \ No newline at end of file diff --git a/src/api/API.go b/src/api/API.go index 794a84c..d5c9a35 100644 --- a/src/api/API.go +++ b/src/api/API.go @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "net/http" + "os" ) func Request(path string, data map[string]interface{}, hash interface{}) (response io.Reader,err error) { @@ -70,4 +71,86 @@ func Request(path string, data map[string]interface{}, hash interface{}) (respon return result,errors.New(errorInfo.Error.(string)) } return result, err +} + +func WorldWideWebRequest(url string, data map[string]interface{}) (response io.Reader,err error) { + var result io.Reader = nil + var body []byte + err = nil + client := &http.Client{} + if data != nil && len(data) > 0 { + postString, err := json.Marshal(&data) + if err!=nil { + return nil, err + } + req, err := http.NewRequest("POST",url, bytes.NewBuffer(postString)) + if err!=nil { + return nil, err + } + req.Header.Set("Accept","application/json") + req.Header.Set("User-Agent","grifpkg/cli") + res, err := client.Do(req) + if err!=nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err = Body.Close() + }(res.Body) + body, err = ioutil.ReadAll(res.Body) + if err!=nil { + return nil, err + } + result = bytes.NewReader(body) + } else { + req, err := http.NewRequest("GET",url, nil) + if err!=nil { + return nil, err + } + req.Header.Set("Accept","application/json") + req.Header.Set("User-Agent","grifpkg/cli") + res, err := client.Do(req) + defer func(Body io.ReadCloser) { + err = Body.Close() + }(res.Body) + body, err = ioutil.ReadAll(res.Body) + if err!=nil { + return nil, err + } + result = bytes.NewReader(body) + } + return result, err +} + +func DownloadFile(URL string, outputDirectoryPath interface{}, outputName string) (outputPath string,err error){ + + var outputPathString string = "" + if outputDirectoryPath!=nil { + outputPathString=outputDirectoryPath.(string) + } else { + outputPathString, err = os.Getwd() + if err != nil { + return "", err + } + } + outputPath=outputPathString+string(os.PathSeparator)+outputName + resp, err := http.Get(URL) + if err != nil { + return "", err + } + defer resp.Body.Close() + + err = os.Mkdir(outputPathString, 0700) + if err != nil && !os.IsExist(err) { + return "", err + } + out, err := os.Create(outputPath) + if err != nil && !os.IsExist(err) { + return "", err + } + defer out.Close() + _, err = io.Copy(out, resp.Body) + if err != nil { + return "", err + } + return outputPath, nil } \ No newline at end of file diff --git a/src/api/Globals.go b/src/api/Globals.go index 3c95aef..c4b1286 100644 --- a/src/api/Globals.go +++ b/src/api/Globals.go @@ -1,5 +1,5 @@ package api -const Version = "1.1.0" +const Version = "1.1.1" const KeychainService = "grifpkg" const KeychainHash = "session" \ No newline at end of file diff --git a/src/elements/deployment/Helper.go b/src/elements/deployment/Helper.go new file mode 100644 index 0000000..667da26 --- /dev/null +++ b/src/elements/deployment/Helper.go @@ -0,0 +1,153 @@ +package deployment + +import ( + "encoding/json" + "errors" + "github.com/grifpkg/cli/api" + "os/exec" + "runtime" + "strconv" + "strings" + "time" +) + +// DirectDeploy deploys directly with a command, f.e. creating a java vm/** +func DirectDeploy(fork string, version string, path string, args map[string]interface{}) (err error){ + version=strings.ToLower(version) + if version=="latest" { + version, err = GetLatestVersion("minecraft") + if err != nil { + return err + } + } + downloadURL, err := GetLatestSoftwareVersion(fork,version) + api.LogOne(api.Progress,"downloading the server software") + _, err = api.DownloadFile(downloadURL,nil,"bin.jar") + if err != nil { + return err + } + api.LogOne(api.Progress, "downloaded, serving locally") + var cmd = "java -jar bin.jar" + if runtime.GOOS == "windows" { + _, err = exec.Command("cmd", "/c", cmd).Output() + if err != nil { + return err + } + } else { + _, err := exec.Command("bash", "-c", cmd).Output() + if err != nil { + return err + } + } + return nil +} + +// DockerDeploy deploys locally with a docker image +func DockerDeploy(fork string, version string, path string, envs map[string]interface{}, image string) (err error){ + return errors.New("not implemented") +} + +// CloudDeploy deploys to a cloud service, AWS, Google Cloud or purecore hosting +func CloudDeploy(fork string, version string, path string, opts map[string]interface{}, service string, token string, identifier interface{}) (err error){ + return errors.New("not implemented") +} + +func GetLatestSoftwareVersion(software string, gameVersion string) (downloadURL string, err error){ + api.LogOne(api.Progress,"fetching latest software version") + software=strings.ToLower(software) + if software=="@papermc/paper" { + + type PaperBuilds struct { + ProjectId string `json:"project_id"` + ProjectName string `json:"project_name"` + Version string `json:"version"` + Builds []int `json:"builds"` + } + + type PaperBuild struct { + ProjectId string `json:"project_id"` + ProjectName string `json:"project_name"` + Version string `json:"version"` + Build int `json:"build"` + Time time.Time `json:"time"` + Changes []struct { + Commit string `json:"commit"` + Summary string `json:"summary"` + Message string `json:"message"` + } `json:"changes"` + Downloads struct { + Application struct { + Name string `json:"name"` + Sha256 string `json:"sha256"` + } `json:"application"` + } `json:"downloads"` + } + + result, err := api.WorldWideWebRequest("https://papermc.io/api/v2/projects/paper/versions/" + gameVersion, map[string]interface{}{}) + if err != nil { + return "", err + } + + var buildData = PaperBuilds{} + + err = json.NewDecoder(result).Decode(&buildData) + if err != nil { + return "", err + } + + var latestBuildData = PaperBuild{} + + result, err = api.WorldWideWebRequest("https://papermc.io/api/v2/projects/paper/versions/"+gameVersion+"/builds/"+strconv.Itoa(buildData.Builds[len(buildData.Builds)-1]), map[string]interface{}{}) + if err != nil { + return "", err + } + err = json.NewDecoder(result).Decode(&latestBuildData) + + return "https://papermc.io/api/v2/projects/paper/versions/"+gameVersion+"/builds/"+strconv.Itoa(buildData.Builds[len(buildData.Builds)-1])+"/downloads/"+latestBuildData.Downloads.Application.Name, nil + + } else if software=="@spigotmc/bungeecord" { + return "https://ci.md-5.net/job/BungeeCord/lastStableBuild/artifact/bootstrap/target/BungeeCord.jar", nil + } else { + return "", errors.New("unsupported software") + } +} + +func GetLatestVersion(game string) (version string,err error){ + api.LogOne(api.Progress,"fetching latest game version") + if game=="minecraft" { + + type MinecraftLatestReleaseStrings struct { + Release string `json:"release"` + Snapshot string `json:"snapshot"` + } + + type MinecraftReleaseInfo struct { + Id string `json:"id"` + Type string `json:"type"` + URL string `json:"url"` + Time string `json:"time"` + ReleaseTime string `json:"releaseTime"` + } + + type MinecraftVersionList struct { + Latest MinecraftLatestReleaseStrings `json:"latest"` + Versions []MinecraftReleaseInfo `json:"versions"` + } + + var versionData = MinecraftVersionList{} + + result, err := api.WorldWideWebRequest("https://launchermeta.mojang.com/mc/game/version_manifest.json",map[string]interface{}{}) + if err != nil { + return "", err + } + + err = json.NewDecoder(result).Decode(&versionData) + if err != nil { + return "", err + } + + return versionData.Latest.Release, nil + } else { + return "", errors.New("unsupported game") + } +} diff --git a/src/elements/project/Project.go b/src/elements/project/Project.go index 950d25c..f188713 100644 --- a/src/elements/project/Project.go +++ b/src/elements/project/Project.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" "github.com/grifpkg/cli/api" + "github.com/grifpkg/cli/elements/deployment" "io/ioutil" "os" "strconv" @@ -35,6 +36,18 @@ func GetProject() (project *Project, lock *ProjectLock, err error){ return &openedProject, &openedProjectLock, nil } +func (project Project) Deploy() (err error){ + wd, err := os.Getwd() + if err != nil { + return err + } + err = deployment.DirectDeploy(project.Software, project.Version, wd, map[string]interface{}{}) + if err != nil { + return err + } + return nil +} + func (project Project) InstallAll() (release []Release, err error) { _, lock, err := GetProject() if err != nil { @@ -253,7 +266,7 @@ func createProjectFile(path string) (project Project, err error){ Name: "software", Prompt: &survey.Select{ Message: "Which server software are you using (if you are using a fork, select the closest parent)", - Options: []string{"@spigotmc/spigot", "@spigotmc/bungeecord", "@papermc/paper", "@papermc/waterfall"}, + Options: []string{"@spigotmc/spigot", "@spigotmc/bungeecord", "@papermc/paper", "@papermc/waterfall", "@minecraft/java-server"}, Default: "@minecraft/java", }, }, diff --git a/src/grif.go b/src/grif.go index 0539006..ec8313d 100644 --- a/src/grif.go +++ b/src/grif.go @@ -40,6 +40,25 @@ var linkCMD = &cobra.Command{ }, } +var deployCMD = &cobra.Command{ + Use: "deploy", + Aliases: []string{"dep"}, + Short: "deploys the server using local resources (directly or via Docker) or using an external cloud service such as AWS, GCloud or purecore hosting", + Run: func(cmd *cobra.Command, args []string) { + project, _, err := project.GetProject() + if err != nil { + api.LogOne(api.Warn, err.Error()) + return + } + err = project.Deploy() + if err != nil { + api.LogOne(api.Warn, err.Error()) + return + } + api.LogOne(api.Success, "deployed") + }, +} + var importCMD = &cobra.Command{ Use: "import", Aliases: []string{"im"}, @@ -161,4 +180,5 @@ func init() { rootCMD.AddCommand(importCMD) rootCMD.AddCommand(excludeCMD) rootCMD.AddCommand(linkCMD) + rootCMD.AddCommand(deployCMD) } \ No newline at end of file diff --git a/src/installer/Windows.go b/src/installer/Windows.go index 91636ea..c574d41 100644 --- a/src/installer/Windows.go +++ b/src/installer/Windows.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path" + "strings" ) func InstallWindows() (release grifRelease, err error){ @@ -17,7 +18,7 @@ func InstallWindows() (release grifRelease, err error){ api.LogOne(api.Progress,"getting config dir") targetPath, _ := os.UserConfigDir() installPath := targetPath+"\\grifpkg\\bin\\" - randomId := ksuid.New().String() + randomId := ksuid.New().String()[0:3] api.LogOne(api.Progress,"downloading latest release") release, err = getLatest(installPath+randomId+"/") if err != nil { @@ -35,24 +36,36 @@ func InstallWindows() (release grifRelease, err error){ } } - api.LogOne(api.Progress,"creating install script") - createInstallScript(installPath, randomId) - - api.LogOne(api.Progress,"running install script") - err = exec.Command(installPath+"install.bat").Run() + api.LogOne(api.Progress,"creating path script") + err = createInstallScript(installPath,randomId) if err != nil { return grifRelease{}, err } + + api.LogOne(api.Progress,"running path script") + err = exec.Command(installPath+"install.bat").Run() + return release, nil } -func createInstallScript(installPath string, id string){ +func createInstallScript(installPath string, id string)(err error){ installScript, err := os.Create(installPath+"install.bat") if os.IsExist(err) { err = os.Remove(installPath+"install.bat") - } else { - // error } - _, err = installScript.WriteString("setx path \""+installPath+id+"\\;%PATH%\"") + + // path value gen + envPath := os.Getenv("PATH") + programs := strings.Split(envPath,";") + finalPrograms := installPath+id+";" + for _, program := range programs { + if !strings.HasSuffix(program,installPath) { + finalPrograms+=strings.TrimSuffix(program,";")+";" + } + } + + // path value set + _, err = installScript.WriteString("setx path \""+finalPrograms+"\"") defer installScript.Close() + return nil } \ No newline at end of file