Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow CLI integration module #139

Merged
merged 52 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
008c2dd
add backend wrapper class
bartolomej Oct 30, 2022
68f028f
fix page_view event tracking
bartolomej Oct 31, 2022
44c56fe
start temporary project
bartolomej Oct 31, 2022
5ebb4b5
handle temp project errors
bartolomej Oct 31, 2022
5407789
add mvp darwin logic
bartolomej Nov 1, 2022
f88f543
fix arch detection, install detection
bartolomej Nov 2, 2022
36fe8cc
add todo comment
bartolomej Nov 2, 2022
ae3a4e8
use native unzip tool
bartolomej Nov 2, 2022
22ae84f
add windows install logic
bartolomej Nov 2, 2022
ee096ef
add windows zip target
bartolomej Nov 2, 2022
2761f4f
fix return type
bartolomej Nov 2, 2022
76efe32
add todo
bartolomej Nov 2, 2022
e7b522d
fix error handling
bartolomej Nov 2, 2022
3d5e090
fix file existance check
bartolomej Nov 2, 2022
74e886f
add missing error handling, use cmd.Run
bartolomej Nov 2, 2022
fff693f
rename error variables
bartolomej Nov 2, 2022
3fc5725
define platform variable in root function call
bartolomej Nov 2, 2022
a27de6e
reorganise functions, minor fixes
bartolomej Nov 2, 2022
0b442da
accept custom install dir path
bartolomej Nov 7, 2022
c846d7a
implement methods on struct, add simple test
bartolomej Nov 7, 2022
ba23ec0
use default platform value if none provided
bartolomej Nov 8, 2022
d9e6429
add Remove function
bartolomej Nov 10, 2022
3ab05a4
cleanup
bartolomej Nov 10, 2022
a15ec75
fix bugs
bartolomej Nov 11, 2022
aef1430
move things around
sideninja Nov 11, 2022
b64e6f5
rename struct
sideninja Nov 11, 2022
15c6af4
remove platform
sideninja Nov 11, 2022
b545e34
change name to path
sideninja Nov 11, 2022
1413f84
refactor implementation of App
sideninja Nov 11, 2022
7b2c744
fix the test
sideninja Nov 11, 2022
3979c07
mod tidy
sideninja Nov 11, 2022
72d47fb
rename to go idiomatic naming
sideninja Nov 11, 2022
ac0de9c
tidy
sideninja Nov 11, 2022
5ab8487
move go mod to top dir
sideninja Nov 11, 2022
df822b5
fix package import
sideninja Nov 11, 2022
7fa911b
create constructor for flowser
sideninja Nov 11, 2022
3a40496
dont return err on check
sideninja Nov 11, 2022
c9d9b9b
remove install dir
sideninja Nov 11, 2022
64157fd
remove returning path
sideninja Nov 11, 2022
d7d23df
add mod
sideninja Nov 14, 2022
5eb4677
export app
sideninja Nov 14, 2022
b9c3a23
Merge branch 'main' into flow_cli_module
bartolomej Nov 15, 2022
2f99052
app name
sideninja Nov 18, 2022
c8cdca2
Merge remote-tracking branch 'origin/flow_cli_module' into flow_cli_m…
sideninja Nov 18, 2022
f2b237d
update main program
bartolomej Nov 18, 2022
3a4e77b
remove outdated test
bartolomej Nov 18, 2022
ed3e4f3
remove install dir
sideninja Nov 21, 2022
1383496
fix comment
sideninja Nov 21, 2022
abc367b
watch for sigint
sideninja Nov 21, 2022
2776b06
watch for sigint
sideninja Nov 21, 2022
7685fb2
Merge remote-tracking branch 'origin/flow_cli_module' into flow_cli_m…
sideninja Nov 21, 2022
b13e622
fix project path
sideninja Nov 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/src/targets/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { SentryMainService } from "./services/sentry-main.service";
import { setupMenu } from "./menu";
import { ServiceRegistry } from "./services/service-registry";
import { FlowserBackend } from "./backend";
import { ProjectEntity } from "@flowser/backend";

fixPath();

Expand Down
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/onflowser/flowser

go 1.19

require github.com/google/go-github v17.0.0+incompatible

require github.com/google/go-querystring v1.1.0 // indirect
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2 changes: 2 additions & 0 deletions pkg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Temp installation dir used for testing
test-install
207 changes: 207 additions & 0 deletions pkg/flowser/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package flowser

import (
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path"
"runtime"
"strings"

"github.com/google/go-github/github"
)

const (
darwin = "darwin"
windows = "windows"
)

// New create new Flowser application.
func New() *App {
return &App{}
}

type App struct {
installDir string
sideninja marked this conversation as resolved.
Show resolved Hide resolved
}

var errorPlatformNotSupported = errors.New("OS not supported, only supporting Windows and Mac OS")

// Run starts the Flowser application with provided path to the flow project.
//
// Project path if exists should be set to the folder containing flow.json, if no such path exists pass empty value.
func (a *App) Run(installDir string, projectPath string) error {
exe, err := a.executable(installDir)
if err != nil {
return err
}

if projectPath != "" {
projectPath = fmt.Sprintf("--project-path=%s", projectPath)
}

return exec.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the child process (Flowser) keeps running on Windows after you kill the parent (go program).

I'm not sure why that's the case, but it seems to be the default behavior on Windows. Here is the orphan process info:
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not the highest priority, so we can fix this at some later point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point!

Command(exe, projectPath).
Run()
}

// Install Flowser application in the provided install directory.
//
// Install directory is optional, if you want to default to your system location you can provide empty value.
func (a *App) Install(installDir string) error {
downloadDir, err := downloadLatestReleaseAsset()
if err != nil {
return err
}

defer os.Remove(downloadDir)
return a.unzip(downloadDir, installDir)
}

// Installed checks whether the flowser application is already installed on the system.
func (a *App) Installed(installDir string) bool {
executable, err := a.executable(installDir)
if err != nil {
return false
}

_, err = os.Stat(executable)
return err == nil
}

// Remove Flowser application from provide directory.
//
// Install directory is optional, if you don't provide a value default will be used.
sideninja marked this conversation as resolved.
Show resolved Hide resolved
func (a *App) Remove(installDir string) error {
a.installDir = installDir
sideninja marked this conversation as resolved.
Show resolved Hide resolved

dir, err := a.appDir(installDir)
if err != nil {
return err
}

return os.RemoveAll(dir)
}

// unzip content from source compressed file to a target directory.
func (a *App) unzip(source string, target string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case darwin:
// Use native unzip tool as it handles the creation of required symbolic links
cmd = exec.Command("unzip", source, "-d", target)
case windows:
appDir, err := a.appDir(target)
if err != nil {
return err
}

if err := os.MkdirAll(appDir, os.ModePerm); err != nil {
return err
}

// tar utility is available from Windows build 17063 consider using other command or a custom implementation
// https://learn.microsoft.com/en-us/virtualization/community/team-blog/2017/20171219-tar-and-curl-come-to-windows
cmd = exec.Command("tar", "-xf", source, "-C", appDir)
default:
return errorPlatformNotSupported
}

return cmd.Run()
}

// appDir returns the location of the executable application inside the installation dir.
func (a *App) appDir(installDir string) (string, error) {
return path.Join(installDir, "Flowser.App"), nil
}

// executable returns the location of application executable.
func (a *App) executable(installDir string) (string, error) {
files := map[string]string{
darwin: "Contents/MacOS/Flowser",
windows: "Flowser.exe",
}
file, ok := files[runtime.GOOS]
if !ok {
return "", errorPlatformNotSupported
}

appDir, err := a.appDir(installDir)
if err != nil {
return "", err
}

return path.Join(appDir, file), nil
}

func downloadLatestReleaseAsset() (string, error) {
release, err := getLatestRelease()

if err != nil {
return "", err
}

resp, err := http.Get(*release.asset.BrowserDownloadURL)

if err != nil {
return "", err
}

defer resp.Body.Close()

out, _ := os.Create(*release.asset.Name)
defer out.Close()

_, err = io.Copy(out, resp.Body)
if err != nil {
return "", err
}

return out.Name(), nil
}

type flowserRelease struct {
version string
asset github.ReleaseAsset
}

func getLatestRelease() (*flowserRelease, error) {
client := github.NewClient(nil)
release, _, err := client.Repositories.GetLatestRelease(context.Background(), "onflowser", "flowser")
if err != nil {
return nil, err
}
latestVersion := strings.Replace(*release.TagName, "v", "", 1)
targetAssetName, err := getAssetName(latestVersion)
if err != nil {
return nil, err
}
for _, asset := range release.Assets {
if *asset.Name == targetAssetName {
return &flowserRelease{
version: latestVersion,
asset: asset,
}, nil
}
}
return nil, errors.New("no asset found")
}

func getAssetName(version string) (string, error) {
isArm := strings.HasPrefix(runtime.GOARCH, "arm")
switch runtime.GOOS {
case darwin:
if isArm {
return fmt.Sprintf("Flowser-%s-arm64-mac.zip", version), nil
}
return fmt.Sprintf("Flowser-%s-mac.zip", version), nil
case windows:
return fmt.Sprintf("Flowser-%s-win.zip", version), nil
default:
return "", errorPlatformNotSupported
}
}
32 changes: 32 additions & 0 deletions pkg/flowser/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package flowser

import (
"testing"
)

var app = &app{}

func TestInstallAndRun(t *testing.T) {
installDir, err := app.Install("")
t.Logf("Installed to %s", installDir)

if err != nil {
t.Fatalf("Installed with error %s", err)
}
isInstalled, err := app.Installed()
if err != nil {
t.Fatalf("Installed with error %s", err)
}
if !isInstalled {
t.Fatalf("Should return isInstalled = true")
}
err = app.Run("/invalid/path")
if err == nil {
t.Fatal("Didn't return error for invalid project path")
}

err = app.Remove("")
if err != nil {
t.Fatalf("Failed to remove installed App: %s", err)
}
}
17 changes: 17 additions & 0 deletions pkg/main/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"github.com/onflowser/flowser/pkg/flowser"
)

func main() {
app := flowser.New()

location, err := app.Install("")
if err != nil {
panic(err)
}

fmt.Println("Flowser installed at:", location)
}