mirror of
				https://github.com/yeslayla/godot-build-tools.git
				synced 2025-11-04 09:03:05 +01:00 
			
		
		
		
	Logging & Godot Downloader
This commit is contained in:
		
							
								
								
									
										14
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					FROM golang:1-alpine3.18 AS builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN go build main.go -o /usr/local/bin/godot-build-tool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FROM alpine:3.18
 | 
				
			||||||
 | 
					RUN apk add --no-cache unzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \
 | 
				
			||||||
 | 
					    && wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r1/glibc-2.35-r1.apk \
 | 
				
			||||||
 | 
					    && apk add glibc-2.35-r1.apk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /opt
 | 
				
			||||||
 | 
					COPY --from=builder /usr/local/bin/godot-build-tool /opt/godot-build-tool
 | 
				
			||||||
 | 
					CMD ["/opt/godot-build-tool"]
 | 
				
			||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					module github.com/yeslayla/godot-build-tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/sethvargo/go-envconfig v0.9.0 // indirect
 | 
				
			||||||
 | 
						github.com/sethvargo/go-githubactions v1.1.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE=
 | 
				
			||||||
 | 
					github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
 | 
				
			||||||
 | 
					github.com/sethvargo/go-githubactions v1.1.0 h1:mg03w+b+/s5SMS298/2G6tHv8P0w0VhUFaqL1THIqzY=
 | 
				
			||||||
 | 
					github.com/sethvargo/go-githubactions v1.1.0/go.mod h1:qIboSF7yq2Qnaw2WXDsqCReM0Lo1gU4QXUWmhBC3pxE=
 | 
				
			||||||
							
								
								
									
										160
									
								
								internal/downloader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								internal/downloader.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					package internal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/yeslayla/godot-build-tools/logging"
 | 
				
			||||||
 | 
						"github.com/yeslayla/godot-build-tools/utils"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DownloaderOptions struct {
 | 
				
			||||||
 | 
						DownloadRepositoryURL string
 | 
				
			||||||
 | 
						BinDir                string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Downloader struct {
 | 
				
			||||||
 | 
						downloadRepositoryURL string
 | 
				
			||||||
 | 
						bin                   string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger logging.Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDownloader(targetOS TargetOS, logger logging.Logger, options *DownloaderOptions) *Downloader {
 | 
				
			||||||
 | 
						var url string = options.DownloadRepositoryURL
 | 
				
			||||||
 | 
						if url == "" {
 | 
				
			||||||
 | 
							url = "https://downloads.tuxfamily.org/godotengine/"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var binDir string = options.BinDir
 | 
				
			||||||
 | 
						if binDir == "" {
 | 
				
			||||||
 | 
							switch targetOS {
 | 
				
			||||||
 | 
							case TargetOSLinux:
 | 
				
			||||||
 | 
								home, _ := os.UserHomeDir()
 | 
				
			||||||
 | 
								binDir = filepath.Join(home, "/.local/bin")
 | 
				
			||||||
 | 
							case TargetOSWindows:
 | 
				
			||||||
 | 
								binDir = "C:\\Program Files (x86)\\Godot"
 | 
				
			||||||
 | 
							case TargetOSMacOS:
 | 
				
			||||||
 | 
								binDir = "/Applications/Godot"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Downloader{
 | 
				
			||||||
 | 
							downloadRepositoryURL: url,
 | 
				
			||||||
 | 
							bin:                   binDir,
 | 
				
			||||||
 | 
							logger:                logger,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getRemoteFileFormat(targetOS TargetOS, version string) string {
 | 
				
			||||||
 | 
						switch targetOS {
 | 
				
			||||||
 | 
						case TargetOSLinux:
 | 
				
			||||||
 | 
							if version[0] == '3' {
 | 
				
			||||||
 | 
								return "Godot_v%s-%s_x11.64.zip"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "Godot_v%s-%s_linux.x86_64.zip"
 | 
				
			||||||
 | 
						case TargetOSWindows:
 | 
				
			||||||
 | 
							return "Godot_v%s-%s_win64.exe.zip"
 | 
				
			||||||
 | 
						case TargetOSMacOS:
 | 
				
			||||||
 | 
							return "Godot_v%s-%s_macos.universal.zip"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Downloader) DownloadGodot(targetOS TargetOS, version string, release string) (string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var fileName string = fmt.Sprintf(getRemoteFileFormat(targetOS, version), version, release)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tempDir, _ := os.MkdirTemp("", "godot-build-tools")
 | 
				
			||||||
 | 
						outFile := filepath.Join(tempDir, fileName)
 | 
				
			||||||
 | 
						out, err := os.Create(outFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to create output file: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer out.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						downloadURL, err := url.Parse(d.downloadRepositoryURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to parse download repository URL: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						downloadURL.Path = path.Join(downloadURL.Path, version)
 | 
				
			||||||
 | 
						if release != "stable" {
 | 
				
			||||||
 | 
							downloadURL.Path = path.Join(downloadURL.Path, release)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						downloadURL.Path = path.Join(downloadURL.Path, fileName)
 | 
				
			||||||
 | 
						d.logger.Debugf("Download URL: %s", downloadURL.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := http.Get(downloadURL.String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to download Godot: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = io.Copy(out, resp.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to write Godot package to output file: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return outFile, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Downloader) UnzipGodot(targetOS TargetOS, godotPackage string) (string, error) {
 | 
				
			||||||
 | 
						files, err := utils.Unzip(godotPackage)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to unzip Godot package: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Look for godot binary
 | 
				
			||||||
 | 
						for _, file := range files {
 | 
				
			||||||
 | 
							switch targetOS {
 | 
				
			||||||
 | 
							case TargetOSLinux:
 | 
				
			||||||
 | 
								if path.Ext(file) == ".x86_64" || path.Ext(file) == ".64" {
 | 
				
			||||||
 | 
									return file, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case TargetOSWindows:
 | 
				
			||||||
 | 
								if path.Ext(file) == ".exe" {
 | 
				
			||||||
 | 
									return file, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case TargetOSMacOS:
 | 
				
			||||||
 | 
								if path.Ext(file) == ".universal" {
 | 
				
			||||||
 | 
									return file, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return "", fmt.Errorf("failed to find godot binary in Godot package")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Downloader) InstallGodot(godotPackage string, targetOS TargetOS, version string, release string) (string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Unzip package
 | 
				
			||||||
 | 
						godotUnzipBinPath, err := d.UnzipGodot(targetOS, godotPackage)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to unzip Godot package: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						godotBin := path.Base(godotUnzipBinPath)
 | 
				
			||||||
 | 
						godotBinPath := filepath.Join(d.bin, godotBin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy Godot binary to bin directory
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(godotUnzipBinPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to read Godot binary: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(godotBinPath, data, 0755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("failed to write Godot binary: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(godotUnzipBinPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return godotBinPath, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										56
									
								
								internal/godot4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/godot4.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					package internal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Godot4ArgBuilder struct {
 | 
				
			||||||
 | 
						args []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewGodot4ArgBuilder(projectDir string) GodotArgBuilder {
 | 
				
			||||||
 | 
						return &Godot4ArgBuilder{
 | 
				
			||||||
 | 
							args: []string{"--path", projectDir},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddHeadlessFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--headless")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddDebugFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--debug")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddVerboseFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--verbose")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddQuietFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--quiet")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddDumpGDExtensionInterfaceFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--dump-gdextension-interface")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddDumpExtensionApiFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--dump-extension-api")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddCheckOnlyFlag() {
 | 
				
			||||||
 | 
						b.args = append(b.args, "--check-only")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) AddExportFlag(exportType ExportType) {
 | 
				
			||||||
 | 
						switch exportType {
 | 
				
			||||||
 | 
						case ExportTypeRelease:
 | 
				
			||||||
 | 
							b.args = append(b.args, "--export")
 | 
				
			||||||
 | 
						case ExportTypeDebug:
 | 
				
			||||||
 | 
							b.args = append(b.args, "--export-debug")
 | 
				
			||||||
 | 
						case ExportTypePack:
 | 
				
			||||||
 | 
							b.args = append(b.args, "--export-pack")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Godot4ArgBuilder) GenerateArgs() string {
 | 
				
			||||||
 | 
						return strings.Join(b.args, " ")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								internal/godot_wrapper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/godot_wrapper.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					package internal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ExportType uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ExportTypeRelease ExportType = iota
 | 
				
			||||||
 | 
						ExportTypeDebug
 | 
				
			||||||
 | 
						ExportTypePack
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GodotArgBuilder interface {
 | 
				
			||||||
 | 
						AddHeadlessFlag()
 | 
				
			||||||
 | 
						AddDebugFlag()
 | 
				
			||||||
 | 
						AddVerboseFlag()
 | 
				
			||||||
 | 
						AddQuietFlag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AddDumpGDExtensionInterfaceFlag()
 | 
				
			||||||
 | 
						AddDumpExtensionApiFlag()
 | 
				
			||||||
 | 
						AddCheckOnlyFlag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AddExportFlag(exportType ExportType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GenerateArgs() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								internal/os.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/os.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					package internal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TargetOS uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TargetOSLinux TargetOS = iota
 | 
				
			||||||
 | 
						TargetOSWindows
 | 
				
			||||||
 | 
						TargetOSMacOS
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										136
									
								
								logging/github_actions.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								logging/github_actions.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GitHubActionsLogger is a logger that logs to GitHub Actions.
 | 
				
			||||||
 | 
					type GitHubActionsLogger struct {
 | 
				
			||||||
 | 
						info  *log.Logger
 | 
				
			||||||
 | 
						warn  *log.Logger
 | 
				
			||||||
 | 
						err   *log.Logger
 | 
				
			||||||
 | 
						debug *log.Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewGitHubActionsLogger creates a new GitHubActionsLogger.
 | 
				
			||||||
 | 
					func NewGitHubActionsLogger(debug bool) Logger {
 | 
				
			||||||
 | 
						var debugLogger *log.Logger
 | 
				
			||||||
 | 
						if debug {
 | 
				
			||||||
 | 
							debugLogger = log.New(os.Stdout, "::debug::", 0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &GitHubActionsLogger{
 | 
				
			||||||
 | 
							info:  log.New(os.Stdout, "", 0),
 | 
				
			||||||
 | 
							warn:  log.New(os.Stdout, "::warning::", 0),
 | 
				
			||||||
 | 
							err:   log.New(os.Stderr, "::error::", 0),
 | 
				
			||||||
 | 
							debug: debugLogger,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Infof logs an info message.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) Infof(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.info.Printf(format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warnf logs a warning message.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) Warnf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.warn.Printf(format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Errorf logs an error message.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) Errorf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.err.Printf(format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debugf logs a debug message if debug logging is enabled.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) Debugf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						if l.debug != nil {
 | 
				
			||||||
 | 
							l.debug.Printf(format, args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NoticeMessage sends a notice message to GitHub Actions.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) NoticeMessage(message string, input NoticeMessageInput) {
 | 
				
			||||||
 | 
						var prefix string = "::notice"
 | 
				
			||||||
 | 
						if input.Title != nil {
 | 
				
			||||||
 | 
							prefix += " title=" + *input.Title
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Filename != nil {
 | 
				
			||||||
 | 
							prefix += " file=" + *input.Filename
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Line != nil {
 | 
				
			||||||
 | 
							prefix += " line=" + fmt.Sprint(*input.Line)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.EndLine != nil {
 | 
				
			||||||
 | 
							prefix += " endLine=" + fmt.Sprint(*input.EndLine)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Col != nil {
 | 
				
			||||||
 | 
							prefix += " col=" + fmt.Sprint(*input.Col)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.EndCol != nil {
 | 
				
			||||||
 | 
							prefix += " endColumn=" + fmt.Sprint(*input.EndCol)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.info.Printf("%s::%s", prefix, message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StartGroup groups together log messages.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) StartGroup(name string) {
 | 
				
			||||||
 | 
						l.info.Printf("::group::%s", name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EndGroup ends a group.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) EndGroup() {
 | 
				
			||||||
 | 
						l.info.Println("::endgroup::")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mask masks a value in log output.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) Mask(value string) {
 | 
				
			||||||
 | 
						l.info.Printf("::add-mask::%s", value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetOutput sets an output parameter.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) SetOutput(name string, value string) {
 | 
				
			||||||
 | 
						outputFile := os.Getenv("GITHUB_OUTPUT")
 | 
				
			||||||
 | 
						if outputFile == "" {
 | 
				
			||||||
 | 
							l.Errorf("GITHUB_OUTPUT is not set")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to open output file: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := f.WriteString(fmt.Sprint(name, "=", value)); err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to write output file: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetSummary sets a job's summary in markdown format.
 | 
				
			||||||
 | 
					func (l *GitHubActionsLogger) SetSummary(summary string) {
 | 
				
			||||||
 | 
						summaryFile := os.Getenv("GITHUB_STEP_SUMMARY")
 | 
				
			||||||
 | 
						if summaryFile == "" {
 | 
				
			||||||
 | 
							l.Errorf("GITHUB_STEP_SUMMARY is not set")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := os.OpenFile(summaryFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to open summary file: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := f.WriteString(summary); err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to write summary file: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										202
									
								
								logging/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								logging/logger.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Logger is an interface for logging.
 | 
				
			||||||
 | 
					type Logger interface {
 | 
				
			||||||
 | 
						Infof(format string, args ...interface{})
 | 
				
			||||||
 | 
						Warnf(format string, args ...interface{})
 | 
				
			||||||
 | 
						Errorf(format string, args ...interface{})
 | 
				
			||||||
 | 
						Debugf(format string, args ...interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Mask(value string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						StartGroup(name string)
 | 
				
			||||||
 | 
						EndGroup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NoticeMessage(message string, input NoticeMessageInput)
 | 
				
			||||||
 | 
						SetOutput(name string, value string)
 | 
				
			||||||
 | 
						SetSummary(summary string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DefaultLogger is a logger that logs to the console.
 | 
				
			||||||
 | 
					type DefaultLogger struct {
 | 
				
			||||||
 | 
						info  *log.Logger
 | 
				
			||||||
 | 
						warn  *log.Logger
 | 
				
			||||||
 | 
						err   *log.Logger
 | 
				
			||||||
 | 
						debug *log.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						outputsFile string
 | 
				
			||||||
 | 
						summaryFile string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						groups []string
 | 
				
			||||||
 | 
						masks  []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NoticeMessageInput holds optional parameters for a notice message.
 | 
				
			||||||
 | 
					type NoticeMessageInput struct {
 | 
				
			||||||
 | 
						Title    *string
 | 
				
			||||||
 | 
						Filename *string
 | 
				
			||||||
 | 
						Line     *int
 | 
				
			||||||
 | 
						EndLine  *int
 | 
				
			||||||
 | 
						Col      *int
 | 
				
			||||||
 | 
						EndCol   *int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoggerOptions holds options for creating a new logger.
 | 
				
			||||||
 | 
					type LoggerOptions struct {
 | 
				
			||||||
 | 
						OutputsFile string
 | 
				
			||||||
 | 
						SummaryFile string
 | 
				
			||||||
 | 
						Debug       bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewLogger creates a new default logger.
 | 
				
			||||||
 | 
					func NewLogger(options *LoggerOptions) Logger {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var debugLogger *log.Logger
 | 
				
			||||||
 | 
						if options.Debug {
 | 
				
			||||||
 | 
							debugLogger = log.New(os.Stdout, "DEBUG ", log.LstdFlags)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &DefaultLogger{
 | 
				
			||||||
 | 
							info:   log.New(os.Stdout, "INFO ", log.LstdFlags),
 | 
				
			||||||
 | 
							warn:   log.New(os.Stdout, "WARNING ", log.LstdFlags),
 | 
				
			||||||
 | 
							err:    log.New(os.Stderr, "ERROR ", log.LstdFlags),
 | 
				
			||||||
 | 
							debug:  debugLogger,
 | 
				
			||||||
 | 
							groups: []string{},
 | 
				
			||||||
 | 
							masks:  []string{},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							outputsFile: options.OutputsFile,
 | 
				
			||||||
 | 
							summaryFile: options.SummaryFile,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// formatMessage wraps the message with the current group and removes any masked values.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) formatMessage(message string) string {
 | 
				
			||||||
 | 
						message = l.removeMasks(message)
 | 
				
			||||||
 | 
						message = l.addGroups(message)
 | 
				
			||||||
 | 
						return message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// removeMasks removes any masked values from the message.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) removeMasks(message string) string {
 | 
				
			||||||
 | 
						for _, mask := range l.masks {
 | 
				
			||||||
 | 
							message = strings.ReplaceAll(message, mask, "********")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// addGroups adds the current group to the message.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) addGroups(message string) string {
 | 
				
			||||||
 | 
						for _, group := range l.groups {
 | 
				
			||||||
 | 
							message = fmt.Sprint(group, " - ", message)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Infof logs an info message.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) Infof(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.info.Printf(l.formatMessage(format), args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warnf logs a warning message.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) Warnf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.warn.Printf(l.formatMessage(format), args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Errorf logs an error message.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) Errorf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						l.err.Printf(l.formatMessage(format), args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debugf logs a debug message if debug logging is enabled.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) Debugf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						if l.debug != nil {
 | 
				
			||||||
 | 
							l.debug.Printf(l.formatMessage(format), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NoticeMessage sends a notice about a line.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) NoticeMessage(message string, input NoticeMessageInput) {
 | 
				
			||||||
 | 
						var prefix string = ""
 | 
				
			||||||
 | 
						if input.Title != nil {
 | 
				
			||||||
 | 
							prefix += " title=" + *input.Title
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Filename != nil {
 | 
				
			||||||
 | 
							prefix += " file=" + *input.Filename
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Line != nil {
 | 
				
			||||||
 | 
							prefix += " line=" + fmt.Sprint(*input.Line)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.EndLine != nil {
 | 
				
			||||||
 | 
							prefix += " endLine=" + fmt.Sprint(*input.EndLine)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.Col != nil {
 | 
				
			||||||
 | 
							prefix += " col=" + fmt.Sprint(*input.Col)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if input.EndCol != nil {
 | 
				
			||||||
 | 
							prefix += " endColumn=" + fmt.Sprint(*input.EndCol)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.info.Printf(l.formatMessage(fmt.Sprintf("%s %s", prefix, message)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mask hides a value in the log output.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) Mask(value string) {
 | 
				
			||||||
 | 
						l.masks = append(l.masks, value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StartGroup groups together log messages.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) StartGroup(name string) {
 | 
				
			||||||
 | 
						l.groups = append(l.groups, name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EndGroup ends a group.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) EndGroup() {
 | 
				
			||||||
 | 
						if len(l.groups) > 0 {
 | 
				
			||||||
 | 
							l.groups = l.groups[:len(l.groups)-1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetOutput outputs a key-value pair to the outputs file.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) SetOutput(name string, value string) {
 | 
				
			||||||
 | 
						if l.outputsFile == "" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := os.OpenFile(l.outputsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to open outputs file: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := f.WriteString(fmt.Sprintf("%s=%s", name, value)); err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to write to outputs file: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetSummary outputs a markdown-formatted summary to the summary file.
 | 
				
			||||||
 | 
					func (l *DefaultLogger) SetSummary(summary string) {
 | 
				
			||||||
 | 
						if l.summaryFile == "" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := os.OpenFile(l.summaryFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to open summary file: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := f.WriteString(summary); err != nil {
 | 
				
			||||||
 | 
							l.Errorf("failed to write to summary file: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/yeslayla/godot-build-tools/internal"
 | 
				
			||||||
 | 
						"github.com/yeslayla/godot-build-tools/logging"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						logger := logging.NewLogger(&logging.LoggerOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var targetOS internal.TargetOS
 | 
				
			||||||
 | 
						switch runtime.GOOS {
 | 
				
			||||||
 | 
						case "linux":
 | 
				
			||||||
 | 
							targetOS = internal.TargetOSLinux
 | 
				
			||||||
 | 
						case "windows":
 | 
				
			||||||
 | 
							targetOS = internal.TargetOSWindows
 | 
				
			||||||
 | 
						case "darwin":
 | 
				
			||||||
 | 
							targetOS = internal.TargetOSMacOS
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GodotSetup(logger, targetOS, "3.3.2", "stable")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GodotSetup(logger logging.Logger, targetOS internal.TargetOS, version string, release string) (string, bool) {
 | 
				
			||||||
 | 
						logger.StartGroup("Godot Setup")
 | 
				
			||||||
 | 
						defer logger.EndGroup()
 | 
				
			||||||
 | 
						downloader := internal.NewDownloader(internal.TargetOSLinux, logger, &internal.DownloaderOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Infof("Downloading Godot")
 | 
				
			||||||
 | 
						godotPackage, err := downloader.DownloadGodot(internal.TargetOSLinux, version, release)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Errorf("Failed to download Godot: %s", err)
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(godotPackage)
 | 
				
			||||||
 | 
						logger.Infof("Godot package: %s", godotPackage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Infof("Installing Godot")
 | 
				
			||||||
 | 
						godotBin, err := downloader.InstallGodot(godotPackage, internal.TargetOSLinux, version, release)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Errorf("Failed to install Godot: %s", err)
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						logger.Infof("Godot binary: %s", godotBin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return godotBin, true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								utils/unzip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								utils/unzip.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"archive/zip"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unzip unzips a zip archive and returns the paths of the unzipped files.
 | 
				
			||||||
 | 
					func Unzip(archivePath string) ([]string, error) {
 | 
				
			||||||
 | 
						reader, err := zip.OpenReader(archivePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to open Godot package: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer reader.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						destDir, err := filepath.Abs(path.Dir(archivePath))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to get absolute path of Godot package: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var unzippedFiles []string = make([]string, 0)
 | 
				
			||||||
 | 
						for _, file := range reader.File {
 | 
				
			||||||
 | 
							filePath := filepath.Join(destDir, file.Name)
 | 
				
			||||||
 | 
							if !strings.HasPrefix(filePath, filepath.Clean(destDir)+string(os.PathSeparator)) {
 | 
				
			||||||
 | 
								return unzippedFiles, fmt.Errorf("%s: illegal file path", filePath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if file.FileInfo().IsDir() {
 | 
				
			||||||
 | 
								if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
 | 
				
			||||||
 | 
									return unzippedFiles, fmt.Errorf("failed to create directory: %s", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
 | 
				
			||||||
 | 
								return unzippedFiles, fmt.Errorf("failed to create directory: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							destFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return unzippedFiles, fmt.Errorf("failed to open file: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer destFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							zippedFile, err := file.Open()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return unzippedFiles, fmt.Errorf("failed to open zipped file: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer zippedFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if _, err := io.Copy(destFile, zippedFile); err != nil {
 | 
				
			||||||
 | 
								return unzippedFiles, fmt.Errorf("failed to copy file: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							unzippedFiles = append(unzippedFiles, filePath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return unzippedFiles, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user