Skip to content

Commit

Permalink
Added an app uploader and fixed some backend related stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
frikky committed Nov 13, 2024
1 parent e384f49 commit 98e75ce
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 62 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.swo
*.swp
*.swn
56 changes: 6 additions & 50 deletions analyze.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package main

import (
"bytes"
"log"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"

"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -33,25 +32,21 @@ func VerifyFolder(folderPath string) error {

// Check for discrepancies in name and version
if !strings.EqualFold(apiData.Name, folderPath) {
fmt.Printf("Bad name: %s vs %s\n", folderPath, apiData.Name)
log.Printf("[ERROR] Bad name: '%s' vs '%s'\n", folderPath, apiData.Name)
}

if apiData.AppVersion != folderPath {
fmt.Printf("Bad version in %s: expected %s, found %s\n", folderPath, apiData.AppVersion, folderPath)
log.Printf("[ERROR] Bad version in %s: expected %s, found %s\n", folderPath, apiData.AppVersion, folderPath)
}

// Check unsupported large_image format
if strings.Contains(apiData.LargeImage, "svg") {
fmt.Printf("Unsupported large_image format in %s: svg\n", apiFilePath)
log.Printf("[ERROR] Unsupported large_image format in %s: svg\n", apiFilePath)
}

// Validate actions in app.py
if err := checkActionsInPython(apiData.Actions, pythonFilePath); err != nil {
fmt.Println(err)
}

// Run the Python script and capture output
if err := runPythonScript(pythonFilePath); err != nil {
fmt.Printf("Error running Python script %s: %v\n", pythonFilePath, err)
log.Printf("[ERROR] Problem with python check: %s", err)
}

return nil
Expand Down Expand Up @@ -93,42 +88,3 @@ func checkActionsInPython(actions []shuffle.WorkflowAppAction, pythonFilePath st

return nil
}

// runPythonScript executes a Python script and checks for errors
func runPythonScript(pythonFilePath string) error {
cmd := exec.Command("python3", pythonFilePath)

// Capture stdout and stderr
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

if err := cmd.Run(); err != nil {
// Check stderr for ModuleNotFoundError specifically
if strings.Contains(stderr.String(), "ModuleNotFoundError") {
return nil // Ignore missing modules as per original logic
}
return fmt.Errorf("Failed to run script: %v\nStderr: %s", err, stderr.String())
}

// Output stdout or stderr
if stdout.Len() > 0 {
fmt.Println("Script output:", stdout.String())
} else if stderr.Len() > 0 {
fmt.Println("Script error output:", stderr.String())
}

return nil
}

/*
func main() {
// Example usage for a single folder
folder := "./example_folder"
if err := VerifyFolder(folder); err != nil {
fmt.Println("Verification error:", err)
} else {
fmt.Println("Folder verification succeeded.")
}
}
*/
223 changes: 211 additions & 12 deletions cli.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package main

import (
"fmt"
"os"
"fmt"
"log"
"os/exec"
"io"
"bytes"
"os/exec"
"strings"
"io/ioutil"
"net/http"
"archive/zip"
"mime/multipart"

"path/filepath"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -90,7 +96,7 @@ var runApp = &cobra.Command{
}

func validatePythonfile(filepath string) error {
cmd := exec.Command("python3", "-m", "pip", "install", "shuffle_sdk", "--break-system-packages")
cmd := exec.Command("python3", "-m", "pip", "install", "shuffle_sdk", "--upgrade", "--break-system-packages")
log.Printf("[DEBUG] Ensuring shuffle-sdk is installed for testing")

var stdoutBuffer, stderrBuffer bytes.Buffer
Expand Down Expand Up @@ -209,30 +215,223 @@ func validateAppFilepath(filepath string) error {
return nil
}

func runUploadValidation(args []string) error {
err := validateAppFilepath(args[0])
if err != nil {
log.Printf("[ERROR] Failed validating app directory: %s", err)
return err
}

pyFile := fmt.Sprintf("%s/src/app.py", args[0])
err = validatePythonfile(pyFile)
if err != nil {
log.Printf("[ERROR] Problem validating python file: %s", err)
return err
}

err = VerifyFolder(args[0])
if err != nil {
log.Printf("[ERROR] Problem verifying folder %s: %s", args[0], err)
return err
}

log.Printf("[INFO] Zip + Uploading app from directory: %s", args[0])
return nil
}

func ZipFiles(filename string, files []string) error {
newZipFile, err := os.Create(filename)
if err != nil {
return err
}

defer newZipFile.Close()

zipWriter := zip.NewWriter(newZipFile)
defer zipWriter.Close()

// Add files to zip
for _, file := range files {
zipfile, err := os.Open(file)
if err != nil {
return err
}
defer zipfile.Close()

// Get the file information
info, err := zipfile.Stat()
if err != nil {
return err
}

header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}

// Using FileInfoHeader() above only uses the basename of the file. If we want
// to preserve the folder structure we can overwrite this with the full path.
filesplit := strings.Split(file, "/")
if len(filesplit) > 1 {
header.Name = filesplit[len(filesplit)-1]
} else {
header.Name = file
}

// Change to deflate to gain better compression
// see http://golang.org/pkg/archive/zip/#pkg-constants
header.Method = zip.Deflate

writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if _, err = io.Copy(writer, zipfile); err != nil {
return err
}
}

return nil
}

func UploadAppFromRepo(folderpath string) error {
log.Printf("[DEBUG] Uploading app from %#v: ", folderpath)


// Walk the path and add
allFiles := []string{}
err := filepath.Walk(folderpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if !info.IsDir() {
allFiles = append(allFiles, path)
}

return nil
})

if err != nil {
log.Printf("[ERROR] Problem walking path: %s", err)
}

zipLocation := fmt.Sprintf("%s/upload.zip", folderpath)
err = ZipFiles(zipLocation, allFiles)
if err != nil {
log.Printf("[ERROR] Problem zipping files: %s", err)
return err
}

newUrl := fmt.Sprintf("%s/api/v1/apps/upload", uploadUrl)
log.Printf("\n\n[INFO] Zipped files to %s. Starting upload to %s. This may take a while, as validation will take place on cloud.", zipLocation, newUrl)

// Add file to request
file, err := os.Open(zipLocation)
if err != nil {
log.Printf("[ERROR] Problem opening file: %s", err)
return err
}

defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)

// Add the file to the form with the field name "shuffle_file"
part, err := writer.CreateFormFile("shuffle_file", filepath.Base(zipLocation))
if err != nil {
return err
}

// Copy the file into the form
_, err = io.Copy(part, file)
if err != nil {
return err
}

// Close the multipart writer to finalize the form
err = writer.Close()
if err != nil {
return err
}

client := &http.Client{}
req, err := http.NewRequest(
"POST",
newUrl,
body,
)

if err != nil {
log.Printf("[ERROR] Problem creating request: %s", err)
return err
}

req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apikey))
req.Header.Set("Content-Type", writer.FormDataContentType())

// Upload the file
resp, err := client.Do(req)
if err != nil {
log.Printf("[ERROR] Problem uploading file: %s", err)
return err
}

outputBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("[ERROR] Problem reading response body: %s", err)
return err
}

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Bad status: %s. Raw: %s", resp.Status, string(outputBody))
}

log.Printf("[INFO] File uploaded successfully: %s", resp.Status)

return nil
}

var uploadApp = &cobra.Command{
Use: "upload",
Short: "Uploads and app from a directory containing the api.yaml",
Run: func(cmd *cobra.Command, args []string) {
if len(apikey) <= 0 {
fmt.Println("Please set the SHUFFLE_APIKEY or SHUFFLE_AUTHORIZATION environment variables to help with upload/download.")
os.Exit(1)
}

// Look for if there is a filepath or not, which contains an api.yaml file AND a src/app.py file
if len(args) <= 0 {
args = append(args, ".")
log.Println("[DEBUG] No directory provided. Using current directory.")
}

err := validateAppFilepath(args[0])
err := runUploadValidation(args)
if err != nil {
log.Printf("[ERROR] Validating app directory: %s", err)
return
log.Printf("[ERROR] Problem with validation: %s", err)
//return
}

// Get user input for whether to continue or not with Y/n
input := "Y"
fmt.Print("\n\nContinue with upload? [Y/n]: ")
fmt.Scanln(&input)

log.Printf("INPUT: %#v", input)
if strings.ToUpper(input) != "Y" {
log.Println("[INFO] Aborting upload.")
return
}

pyFile := fmt.Sprintf("%s/src/app.py", args[0])
err = validatePythonfile(pyFile)
// Upload the app
err = UploadAppFromRepo(args[0])
if err != nil {
log.Printf("[ERROR] Problem validating python file: %s", err)
log.Printf("[ERROR] Problem uploading app: %s", err)
return
}

log.Printf("[INFO] Zip + Uploading app from directory: %s", args[0])
log.Println("[INFO] App uploaded successfully.")
},
}

Expand All @@ -244,9 +443,9 @@ var appCmd = &cobra.Command{

func init() {
// Register subcommands to the math command
appCmd.AddCommand(testApp)
appCmd.AddCommand(uploadApp)

appCmd.AddCommand(runApp)
//appCmd.AddCommand(testApp)
//appCmd.AddCommand(runApp)
}

0 comments on commit 98e75ce

Please sign in to comment.