🎨 Improved project structure

main
Nichole Mattera 2 years ago
parent 889d61b2b1
commit 09a3700b1c
  1. 4
      .gitignore
  2. 19
      Makefile
  3. 0
      assets/modules/01-atmosphere.json
  4. 0
      assets/modules/02-hekate.json
  5. 0
      assets/modules/03-fusee.json
  6. 0
      assets/modules/04-kosmos.json
  7. 0
      assets/modules/05-kosmos-reborn-setup.json
  8. 96
      cmd/builder/main.go
  9. 6
      go.mod
  10. 160
      internal/common.go
  11. 159
      internal/github.go
  12. 144
      internal/instructions.go
  13. 97
      internal/modules.go
  14. 16
      makefile
  15. 122
      src/builder.go
  16. 162
      src/common.go
  17. 161
      src/github.go
  18. 146
      src/instructions.go
  19. 99
      src/modules.go

4
.gitignore vendored

@ -1,3 +1 @@
kosmos-reborn-builder
tmp
*.zip
/build

@ -0,0 +1,19 @@
TARGET := builder
SOURCE := ./cmd/builder/
OUTPUT := ./build/
all: clean deps $(TARGET)
clean:
@rm -rf ./$(OUTPUT)
@echo Cleaned previous build...
deps:
@go mod download
@echo Dependencies downloaded...
$(TARGET):
@mkdir -p ./$(OUTPUT)/modules
@cp -R ./assets/* ./$(OUTPUT)/
@go build -o ./$(OUTPUT)/$@ $(SOURCE)
@echo Kosmos Reborn Builder has been built - $(OUTPUT)$@

@ -0,0 +1,96 @@
// Kosmos Reborn Builder
// Copyright (C) 2021 Nichole Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"github.com/TeamLibra/Kosmos-Reborn/internal"
)
type Config struct {
GithubUsername string
GithubPassword string
}
func main() {
// Parse command line arguments.
var version string
var output string
flag.StringVar(&version, "v", "", "The Kosmos Reborn version. (Required)")
flag.StringVar(&output, "o", "", "Path of where to generate the zip file. (Required)")
flag.Parse()
if version == "" || output == "" {
fmt.Println("Usage:")
flag.PrintDefaults()
return
}
// Get config.
config := GetConfig()
if config.GithubUsername == "" || config.GithubPassword == "" {
fmt.Println("Error: Make sure you have the following environment variables set:")
fmt.Printf("\tGH_USERNAME - Github Username\n")
fmt.Printf("\tGH_PASSWORD - Github Password\n")
return
}
// Create temp directory.
cwd, _ := os.Getwd()
if internal.Exists(filepath.Join(cwd, "tmp")) {
os.RemoveAll(filepath.Join(cwd, "tmp"))
}
tempDirectory := internal.GenerateTempPath()
os.MkdirAll(tempDirectory, os.ModePerm)
// Start build process.
fmt.Printf("Kosmos Reborn %s built with:\n", version)
buildMessage, err := internal.BuildModules(tempDirectory, version, config.GithubUsername, config.GithubPassword)
if err == nil {
// Clean up any old output file.
os.RemoveAll(output)
// Create zip file.
err = internal.Compress(tempDirectory, filepath.Join(cwd, output))
if err != nil {
fmt.Println("Failed: " + err.Error())
} else {
fmt.Println(buildMessage)
}
} else {
fmt.Println("Failed: " + err.Error())
}
// Clean up temp directory.
os.RemoveAll(filepath.Join(cwd, "tmp"))
}
func GetConfig() Config {
return Config {
os.Getenv("GH_USERNAME"),
os.Getenv("GH_PASSWORD"),
}
}

@ -1,5 +1,5 @@
module builder
module github.com/TeamLibra/Kosmos-Reborn
go 1.15
go 1.16
require github.com/google/uuid v1.2.0
require github.com/google/uuid v1.2.0 // indirect

@ -0,0 +1,160 @@
// Kosmos Reborn Builder
// Copyright (C) 2021 Nichole Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
package internal
import (
"archive/zip"
"bufio"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/google/uuid"
)
func GenerateTempPath() string {
cwd, _ := os.Getwd()
return filepath.Join(cwd, "tmp", uuid.New().String())
}
func Compress(src, dst string) error {
zipFile, err := os.Create(dst)
if err != nil {
return err
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
f, err := zipWriter.Create(path[len(src) + 1:])
if err != nil {
return err
}
_, err = io.Copy(f, file)
if err != nil {
return err
}
return nil
})
if err != nil {
panic(err)
}
return nil
}
func CopyFile(src, dst string) error {
srcfd, err := os.Open(src);
if err != nil {
return err
}
defer srcfd.Close()
dstfd, err := os.Create(dst);
if err != nil {
return err
}
defer dstfd.Close()
_, err = io.Copy(dstfd, srcfd);
if err != nil {
return err
}
srcinfo, err := os.Stat(src);
if err != nil {
return err
}
return os.Chmod(dst, srcinfo.Mode())
}
func CopyDirectory(src, dst string) error {
srcinfo, err := os.Stat(src);
if err != nil {
return err
}
err = os.MkdirAll(dst, srcinfo.Mode());
if err != nil {
return err
}
fds, err := ioutil.ReadDir(src);
if err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := path.Join(dst, fd.Name())
if fd.IsDir() {
err = CopyDirectory(srcfp, dstfp);
if err != nil {
return err
}
} else {
err = CopyFile(srcfp, dstfp);
if err != nil {
return err
}
}
}
return nil
}
func Exists(dst string) bool {
_, err := os.Stat(dst)
return !os.IsNotExist(err)
}
func WriteToFile(dst, value string) error {
file, err := os.Create(dst)
if err != nil {
return err
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(value)
writer.Flush()
return nil
}

@ -0,0 +1,159 @@
// Kosmos Reborn Builder
// Copyright (C) 2021 Nichole Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
package internal
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
type GitHubUser struct {
Login string `json:"login"`
Id int `json:"id"`
NodeId string `json:"node_id"`
AvatarUrl string `json:"avatar_url"`
GravatarId string `json:"gravatar_id"`
Url string `json:"url"`
HtmlUrl string `json:"html_url"`
FollowersUrl string `json:"followers_url"`
FollowingUrl string `json:"following_url"`
GistsUrl string `json:"gists_url"`
StarredUrl string `json:"starred_url"`
SubscriptionsUrl string `json:"subscriptions_url"`
OrganizationsUrl string `json:"organizations_url"`
ReposUrl string `json:"repos_url"`
EventsUrl string `json:"events_url"`
ReceivedEventsUrl string `json:"received_events_url"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
type GitHubAsset struct {
Url string `json:"url"`
Id int `json:"id"`
NodeId string `json:"node_id"`
Name string `json:"name"`
Label string `json:"label"`
Uploader GitHubUser `json:"uploader"`
ContentType string `json:"content_type"`
State string `json:"state"`
Size int `json:"size"`
DownloadCount int `json:"download_count"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
BrowserDownloadUrl string `json:"browser_download_url"`
}
type GitHubRelease struct {
Url string `json:"url"`
AssetsUrl string `json:"assets_url"`
UploadUrl string `json:"upload_url"`
HtmlUrl string `json:"html_url"`
Id int `json:"id"`
Author GitHubUser `json:"author"`
NodeId string `json:"node_id"`
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
Assets []GitHubAsset `json:"assets"`
TarballUrl string `json:"tarball_url"`
ZipballUrl string `json:"zipball_url"`
Body string `json:"body"`
}
func GetLatestRelease(organization string, repository string, assetPattern string, githubUsername string, githubPassword string) (string, string, error) {
resp, err := http.Get("https://" + githubUsername + ":" + githubPassword + "@api.github.com/repos/" + organization + "/" + repository + "/releases/latest")
if err != nil {
return "", "", err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", "", errors.New("Getting latest release returned status code: " + strconv.Itoa(resp.StatusCode))
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", err
}
var release GitHubRelease
json.Unmarshal(body, &release)
for _, asset := range release.Assets {
matched, err := regexp.Match(assetPattern, []byte(asset.Name))
if err != nil {
return "", "", err
}
if matched {
return release.TagName, asset.BrowserDownloadUrl, nil
}
}
return "", "", errors.New("No assets")
}
func DownloadFile(rawUrl string, destination string) (string, error) {
url, err := url.Parse(rawUrl)
if err != nil {
return "", err
}
segments := strings.Split(url.Path, "/")
fileName := segments[len(segments)-1]
path := filepath.Join(destination, fileName)
file, err := os.Create(path)
if err != nil {
return "", err
}
defer file.Close()
resp, err := http.Get(rawUrl)
if err != nil {
return "", err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", errors.New("Download file returned status code: " + strconv.Itoa(resp.StatusCode))
}
defer resp.Body.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
return "", err
}
return path, nil
}

@ -0,0 +1,144 @@
// Kosmos Reborn Builder
// Copyright (C) 2021 Nichole Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
package internal
import (
"archive/zip"
"errors"
"io"
"os"
"path/filepath"
"strings"
)
type Action string
const (
Copy Action = "copy"
Delete Action = "delete"
Extract Action = "extract"
Mkdir Action = "mkdir"
)
type Instruction struct {
Action Action
Source string
Destination string
}
func CopyInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
for _, match := range matches {
matchInfo, err := os.Stat(match)
if err != nil {
return err
}
if matchInfo.IsDir() {
CopyDirectory(match, filepath.Join(tempDirectory, instruction.Destination))
} else {
CopyFile(match, filepath.Join(tempDirectory, instruction.Destination))
}
}
return nil
}
func DeleteInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
for _, match := range matches {
if Exists(match) {
err = os.Remove(match)
if err != nil {
return err
}
}
}
return nil
}
func ExtractInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
if len(matches) < 1 {
return errors.New("Nothing to unzip for pattern: " + instruction.Source)
}
for _, match := range matches {
zipReader, err := zip.OpenReader(match)
if err != nil {
return err
}
defer zipReader.Close()
for _, file := range zipReader.File {
path := filepath.Join(moduleTempDirectory, file.Name)
if !strings.HasPrefix(path, filepath.Clean(moduleTempDirectory) + string(os.PathSeparator)) {
return errors.New("Illegal file path: " + path)
}
// Extract folder
if file.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
continue
}
// Extract file
err = os.MkdirAll(filepath.Dir(path), os.ModePerm);
if err != nil {
return err
}
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return err
}
defer outFile.Close()
inFile, err := file.Open()
if err != nil {
return err
}
defer inFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return err
}
}
}
return nil
}
func MkdirInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
return os.MkdirAll(filepath.Join(tempDirectory, instruction.Destination), os.ModePerm)
}

@ -0,0 +1,97 @@
// Kosmos Reborn Builder
// Copyright (C) 2021 Nichole Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
package internal
import (
"encoding/json"
"io/ioutil"
"os"
)
type Module struct {
Name string
Org string
Repo string
AssetPattern string
Instructions []Instruction
}
func BuildModules(tempDirectory string, version string, githubUsername string, githubPassword string) (string, error) {
modules, err := ioutil.ReadDir("./modules")
if err != nil {
return "", err
}
buildMessage := ""
for _, file := range modules {
moduleFile, err := os.Open("./modules/" + file.Name())
if err != nil {
return "", err
}
defer moduleFile.Close()
byteValue, _ := ioutil.ReadAll(moduleFile)
var module Module
json.Unmarshal(byteValue, &module)
moduleTempDirectory := GenerateTempPath()
os.MkdirAll(moduleTempDirectory, os.ModePerm)
version, downloadURL, err := GetLatestRelease(module.Org, module.Repo, module.AssetPattern, githubUsername, githubPassword)
if err != nil {
return "", err
}
_, err = DownloadFile(downloadURL, moduleTempDirectory)
if err != nil {
return "", err
}
for _, instruction := range module.Instructions {
switch (instruction.Action) {
case Copy:
err = CopyInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Delete:
err = DeleteInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Extract:
err = ExtractInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Mkdir:
err = MkdirInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
}
if err != nil {
return "", err
}
}
os.RemoveAll(moduleTempDirectory)
buildMessage += "\t" + module.Name + ": " + version + "\n"
}
return buildMessage, nil
}

@ -1,16 +0,0 @@
TARGET := kosmos-reborn-builder
SOURCE := ./src/
all: clean deps $(TARGET)
clean:
@rm ./$(TARGET)
@echo Cleaned previous build...
deps:
@go mod download
@echo Dependencies downloaded...
$(TARGET):
@go build -o $@ $(SOURCE)
@echo Kosmos Reborn Builder has been built - $@

@ -1,122 +0,0 @@
/*
* Kosmos Reborn Builder
* Copyright (C) 2021 Nichole Mattera
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
)
type Config struct {
GithubUsername string
GithubPassword string
}
type KosmosRebornConfig struct {
Version string `json:"version"`
InstalledPackages []string `json:"installed_packages"`
}
func main() {
var version string
var output string
flag.StringVar(&version, "v", "", "The Kosmos Reborn version. (Required)")
flag.StringVar(&output, "o", "", "Path of where to generate the zip file. (Required)")
flag.Parse()
if version == "" || output == "" {
fmt.Println("Usage:")
flag.PrintDefaults()
return
}
config := GetConfig()
if config.GithubUsername == "" || config.GithubPassword == "" {
fmt.Println("Error: Make sure you have the following environment variables set:")
fmt.Printf("\tGH_USERNAME - Github Username\n")
fmt.Printf("\tGH_PASSWORD - Github Password\n")
return
}
cwd, _ := os.Getwd()
if Exists(filepath.Join(cwd, "tmp")) {
os.RemoveAll(filepath.Join(cwd, "tmp"))
}
tempDirectory := GenerateTempPath()
os.MkdirAll(tempDirectory, os.ModePerm)
fmt.Printf("Kosmos Reborn %s built with:\n", version)
buildMessage, err := BuildModules(tempDirectory, version, config)
os.RemoveAll(output)
if err == nil {
err = WriteKosmosRebornConfig(tempDirectory, version)
if err != nil {
fmt.Println("Failed: " + err.Error())
os.RemoveAll(filepath.Join(cwd, "tmp"))
return
}
err = Compress(tempDirectory, filepath.Join(cwd, output))
if err != nil {
fmt.Println("Failed: " + err.Error())
os.RemoveAll(filepath.Join(cwd, "tmp"))
return
}
fmt.Println(buildMessage)
} else {
fmt.Println("Failed: " + err.Error())
}
os.RemoveAll(filepath.Join(cwd, "tmp"))
}
func GetConfig() Config {
return Config {
os.Getenv("GH_USERNAME"),
os.Getenv("GH_PASSWORD"),
}
}
func WriteKosmosRebornConfig(tempDirectory string, version string) error {
config := KosmosRebornConfig{version, []string{}}
res, err := json.Marshal(config)
if err != nil {
return err
}
err = WriteToFile(filepath.Join(tempDirectory, "atmosphere", "kosmos-reborn.json"), string(res[:]))
if err != nil {
return err
}
return nil
}

@ -1,162 +0,0 @@
/*
* Kosmos Reborn Builder
* Copyright (C) 2021 Nichole Mattera
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package main
import (
"archive/zip"
"bufio"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/google/uuid"
)
func GenerateTempPath() string {
cwd, _ := os.Getwd()
return filepath.Join(cwd, "tmp", uuid.New().String())
}
func Compress(src, dst string) error {
zipFile, err := os.Create(dst)
if err != nil {
return err
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
f, err := zipWriter.Create(path[len(src) + 1:])
if err != nil {
return err
}
_, err = io.Copy(f, file)
if err != nil {
return err
}
return nil
})
if err != nil {
panic(err)
}
return nil
}
func CopyFile(src, dst string) error {
srcfd, err := os.Open(src);
if err != nil {
return err
}
defer srcfd.Close()
dstfd, err := os.Create(dst);
if err != nil {
return err
}
defer dstfd.Close()
_, err = io.Copy(dstfd, srcfd);
if err != nil {
return err
}
srcinfo, err := os.Stat(src);
if err != nil {
return err
}
return os.Chmod(dst, srcinfo.Mode())
}
func CopyDirectory(src, dst string) error {
srcinfo, err := os.Stat(src);
if err != nil {
return err
}
err = os.MkdirAll(dst, srcinfo.Mode());
if err != nil {
return err
}
fds, err := ioutil.ReadDir(src);
if err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := path.Join(dst, fd.Name())
if fd.IsDir() {
err = CopyDirectory(srcfp, dstfp);
if err != nil {
return err
}
} else {
err = CopyFile(srcfp, dstfp);
if err != nil {
return err
}
}
}
return nil
}
func Exists(dst string) bool {
_, err := os.Stat(dst)
return !os.IsNotExist(err)
}
func WriteToFile(dst, value string) error {
file, err := os.Create(dst)
if err != nil {
return err
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(value)
writer.Flush()
return nil
}

@ -1,161 +0,0 @@
/*
* Kosmos Reborn Builder
* Copyright (C) 2021 Nichole Mattera
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package main
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
type GitHubUser struct {
Login string `json:"login"`
Id int `json:"id"`
NodeId string `json:"node_id"`
AvatarUrl string `json:"avatar_url"`
GravatarId string `json:"gravatar_id"`
Url string `json:"url"`
HtmlUrl string `json:"html_url"`
FollowersUrl string `json:"followers_url"`
FollowingUrl string `json:"following_url"`
GistsUrl string `json:"gists_url"`
StarredUrl string `json:"starred_url"`
SubscriptionsUrl string `json:"subscriptions_url"`
OrganizationsUrl string `json:"organizations_url"`
ReposUrl string `json:"repos_url"`
EventsUrl string `json:"events_url"`
ReceivedEventsUrl string `json:"received_events_url"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
type GitHubAsset struct {
Url string `json:"url"`
Id int `json:"id"`
NodeId string `json:"node_id"`
Name string `json:"name"`
Label string `json:"label"`
Uploader GitHubUser `json:"uploader"`
ContentType string `json:"content_type"`
State string `json:"state"`
Size int `json:"size"`
DownloadCount int `json:"download_count"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
BrowserDownloadUrl string `json:"browser_download_url"`
}
type GitHubRelease struct {
Url string `json:"url"`
AssetsUrl string `json:"assets_url"`
UploadUrl string `json:"upload_url"`
HtmlUrl string `json:"html_url"`
Id int `json:"id"`
Author GitHubUser `json:"author"`
NodeId string `json:"node_id"`
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
Assets []GitHubAsset `json:"assets"`
TarballUrl string `json:"tarball_url"`
ZipballUrl string `json:"zipball_url"`
Body string `json:"body"`
}
func GetLatestRelease(organization string, repository string, assetPattern string, config Config) (string, string, error) {
resp, err := http.Get("https://" + config.GithubUsername + ":" + config.GithubPassword + "@api.github.com/repos/" + organization + "/" + repository + "/releases/latest")
if err != nil {
return "", "", err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", "", errors.New("Getting latest release returned status code: " + strconv.Itoa(resp.StatusCode))
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", err
}
var release GitHubRelease
json.Unmarshal(body, &release)
for _, asset := range release.Assets {
matched, err := regexp.Match(assetPattern, []byte(asset.Name))
if err != nil {
return "", "", err
}
if matched {
return release.TagName, asset.BrowserDownloadUrl, nil
}
}
return "", "", errors.New("No assets")
}
func DownloadFile(rawUrl string, destination string) (string, error) {
url, err := url.Parse(rawUrl)
if err != nil {
return "", err
}
segments := strings.Split(url.Path, "/")
fileName := segments[len(segments)-1]
path := filepath.Join(destination, fileName)
file, err := os.Create(path)
if err != nil {
return "", err
}
defer file.Close()
resp, err := http.Get(rawUrl)
if err != nil {
return "", err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", errors.New("Download file returned status code: " + strconv.Itoa(resp.StatusCode))
}
defer resp.Body.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
return "", err
}
return path, nil
}

@ -1,146 +0,0 @@
/*
* Kosmos Reborn Builder
* Copyright (C) 2021 Nichole Mattera
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package main
import (
"archive/zip"
"errors"
"io"
"os"
"path/filepath"
"strings"
)
type Action string
const (
Copy Action = "copy"
Delete Action = "delete"
Extract Action = "extract"
Mkdir Action = "mkdir"
)
type Instruction struct {
Action Action
Source string
Destination string
}
func CopyInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
for _, match := range matches {
matchInfo, err := os.Stat(match)
if err != nil {
return err
}
if matchInfo.IsDir() {
CopyDirectory(match, filepath.Join(tempDirectory, instruction.Destination))
} else {
CopyFile(match, filepath.Join(tempDirectory, instruction.Destination))
}
}
return nil
}
func DeleteInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
for _, match := range matches {
if Exists(match) {
err = os.Remove(match)
if err != nil {
return err
}
}
}
return nil
}
func ExtractInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
matches, err := filepath.Glob(filepath.Join(moduleTempDirectory, instruction.Source))
if err != nil {
return err
}
if len(matches) < 1 {
return errors.New("Nothing to unzip for pattern: " + instruction.Source)
}
for _, match := range matches {
zipReader, err := zip.OpenReader(match)
if err != nil {
return err
}
defer zipReader.Close()
for _, file := range zipReader.File {
path := filepath.Join(moduleTempDirectory, file.Name)
if !strings.HasPrefix(path, filepath.Clean(moduleTempDirectory) + string(os.PathSeparator)) {
return errors.New("Illegal file path: " + path)
}
// Extract folder
if file.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
continue
}
// Extract file
err = os.MkdirAll(filepath.Dir(path), os.ModePerm);
if err != nil {
return err
}
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return err
}
defer outFile.Close()
inFile, err := file.Open()
if err != nil {
return err
}
defer inFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return err
}
}
}
return nil
}
func MkdirInstruction(module Module, instruction Instruction, moduleTempDirectory string, tempDirectory string) error {
return os.MkdirAll(filepath.Join(tempDirectory, instruction.Destination), os.ModePerm)
}

@ -1,99 +0,0 @@
/*
* Kosmos Reborn Builder
* Copyright (C) 2021 Nichole Mattera
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package main
import (
"encoding/json"
"io/ioutil"
"os"
)
type Module struct {
Name string
Org string
Repo string
AssetPattern string
Instructions []Instruction
}
func BuildModules(tempDirectory string, version string, config Config) (string, error) {
modules, err := ioutil.ReadDir("./modules")
if err != nil {
return "", err
}
buildMessage := ""
for _, file := range modules {
moduleFile, err := os.Open("./modules/" + file.Name())
if err != nil {
return "", err
}
defer moduleFile.Close()
byteValue, _ := ioutil.ReadAll(moduleFile)
var module Module
json.Unmarshal(byteValue, &module)
moduleTempDirectory := GenerateTempPath()
os.MkdirAll(moduleTempDirectory, os.ModePerm)
version, downloadURL, err := GetLatestRelease(module.Org, module.Repo, module.AssetPattern, config)
if err != nil {
return "", err
}
_, err = DownloadFile(downloadURL, moduleTempDirectory)
if err != nil {
return "", err
}
for _, instruction := range module.Instructions {
switch (instruction.Action) {
case Copy:
err = CopyInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Delete:
err = DeleteInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Extract:
err = ExtractInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
case Mkdir:
err = MkdirInstruction(module, instruction, moduleTempDirectory, tempDirectory)
break
}
if err != nil {
return "", err
}
}
os.RemoveAll(moduleTempDirectory)
buildMessage += "\t" + module.Name + ": " + version + "\n"
}
return buildMessage, nil
}
Loading…
Cancel
Save