installer: env uses repoio
diff --git a/core/installer/cmd/env.go b/core/installer/cmd/env.go
index 4b258b1..798b89f 100644
--- a/core/installer/cmd/env.go
+++ b/core/installer/cmd/env.go
@@ -4,22 +4,17 @@
 package main
 
 import (
-	"bytes"
 	"embed"
 	"encoding/base64"
 	"fmt"
-	"io"
 	"log"
 	"os"
 	"path"
 	"text/template"
 
-	"golang.org/x/exp/slices"
-
 	"github.com/giolekva/pcloud/core/installer"
 	"github.com/giolekva/pcloud/core/installer/soft"
 	"github.com/spf13/cobra"
-	"sigs.k8s.io/yaml"
 )
 
 //go:embed env-tmpl
@@ -27,6 +22,8 @@
 
 var createEnvFlags struct {
 	name         string
+	ip           string
+	port         int
 	adminPrivKey string
 }
 
@@ -42,6 +39,18 @@
 		"",
 	)
 	cmd.Flags().StringVar(
+		&createEnvFlags.ip,
+		"ip",
+		"",
+		"",
+	)
+	cmd.Flags().IntVar(
+		&createEnvFlags.port,
+		"ip",
+		22,
+		"",
+	)
+	cmd.Flags().StringVar(
 		&createEnvFlags.adminPrivKey,
 		"admin-priv-key",
 		"",
@@ -55,7 +64,7 @@
 	if err != nil {
 		return err
 	}
-	ss, err := soft.NewClient("192.168.0.211", 22, adminPrivKey, log.Default())
+	ss, err := soft.NewClient(createEnvFlags.ip, createEnvFlags.port, adminPrivKey, log.Default())
 	if err != nil {
 		return err
 	}
@@ -82,69 +91,42 @@
 	if err := ss.AddCollaborator(createEnvFlags.name, fluxUserName); err != nil {
 		return err
 	}
-	repo, err := ss.CloneRepository("pcloud")
+	repo, err := ss.GetRepo("pcloud")
 	if err != nil {
 		return err
 	}
-	wt, err := repo.Worktree()
+	repoIO := installer.NewRepoIO(repo, ss.Signer)
+	kust, err := repoIO.ReadKustomization("environments/kustomization.yaml")
 	if err != nil {
 		return err
 	}
-	envKust := "environments/kustomization.yaml"
-	envKustFile, err := wt.Filesystem.Open(envKust)
-	if err != nil {
-		return err
-	}
-	kust, err := installer.ReadKustomization(envKustFile)
-	if err != nil {
-		return err
-	}
-	if slices.Contains(kust.Resources, createEnvFlags.name) {
-		return fmt.Errorf("Environment already exists: %s", createEnvFlags.name)
-	}
+	kust.AddResources(createEnvFlags.name)
 	tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
 	if err != nil {
 		return err
 	}
 	for _, tmpl := range tmpls.Templates() {
 		dstPath := path.Join("environments", createEnvFlags.name, tmpl.Name())
-		fmt.Println(dstPath)
-		dst, err := wt.Filesystem.Create(dstPath)
+		dst, err := repoIO.Writer(dstPath)
 		if err != nil {
 			return err
 		}
+		defer dst.Close()
 		if err := tmpl.Execute(dst, map[string]string{
 			"Name":       createEnvFlags.name,
 			"PrivateKey": base64.StdEncoding.EncodeToString([]byte(priv)),
 			"PublicKey":  base64.StdEncoding.EncodeToString([]byte(pub)),
-			"GitHost":    "192.168.0.211",
-			"KnownHosts": base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("192.168.0.211 %s", ssPubKey))),
+			"GitHost":    createEnvFlags.ip,
+			"KnownHosts": base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s %s", createEnvFlags.ip, ssPubKey))),
 		}); err != nil {
 			return err
 		}
-		if _, err := wt.Add(dstPath); err != nil {
-			return err
-		}
 	}
 	kust.AddResources(createEnvFlags.name)
-	ff, err := wt.Filesystem.Create(envKust)
-	if err != nil {
+	if err := repoIO.WriteKustomization("environments/kustomization.yaml", *kust); err != nil {
 		return err
 	}
-	contents, err := yaml.Marshal(kust)
-	if err != nil {
-		return err
-	}
-	if _, err := io.Copy(ff, bytes.NewReader(contents)); err != nil {
-		return err
-	}
-	if _, err := wt.Add(envKust); err != nil {
-		return err
-	}
-	if err := ss.Commit(wt, fmt.Sprintf("%s: new environment", createEnvFlags.name)); err != nil {
-		return err
-	}
-	if err := ss.Push(repo); err != nil {
+	if err := repoIO.CommitAndPush(fmt.Sprintf("%s: initialize environment", createEnvFlags.name)); err != nil {
 		return err
 	}
 	return nil
diff --git a/core/installer/repoio.go b/core/installer/repoio.go
index 271a1c4..a1d86d2 100644
--- a/core/installer/repoio.go
+++ b/core/installer/repoio.go
@@ -1,6 +1,7 @@
 package installer
 
 import (
+	"io"
 	"io/fs"
 	"path/filepath"
 	"time"
@@ -14,6 +15,7 @@
 	ReadKustomization(path string) (*Kustomization, error)
 	WriteKustomization(path string, kust Kustomization) error
 	CommitAndPush(message string) error
+	Writer(path string) (io.WriteCloser, error)
 }
 
 type repoIO struct {
@@ -41,19 +43,22 @@
 	return ReadKustomization(inp)
 }
 
-func (r *repoIO) WriteKustomization(path string, kust Kustomization) error {
+func (r *repoIO) Writer(path string) (io.WriteCloser, error) {
 	wt, err := r.repo.Worktree()
 	if err != nil {
-		return err
+		return nil, err
 	}
 	if err := wt.Filesystem.MkdirAll(filepath.Dir(path), fs.ModePerm); err != nil {
-		return err
+		return nil, err
 	}
-	out, err := wt.Filesystem.Create(path)
+	return wt.Filesystem.Create(path)
+}
+
+func (r *repoIO) WriteKustomization(path string, kust Kustomization) error {
+	out, err := r.Writer(path)
 	if err != nil {
 		return err
 	}
-	defer out.Close()
 	return kust.Write(out)
 }
 
@@ -74,7 +79,7 @@
 		return err
 	}
 	return r.repo.Push(&git.PushOptions{
-		RemoteName: "soft", // TODO(giolekva): configurable
+		RemoteName: "origin",
 		Auth:       auth(r.signer),
 	})
 }
diff --git a/core/installer/soft/client.go b/core/installer/soft/client.go
index 68964d3..e07532e 100644
--- a/core/installer/soft/client.go
+++ b/core/installer/soft/client.go
@@ -129,7 +129,7 @@
 	return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
 		URL:             ss.addressGit(),
 		Auth:            ss.authGit(),
-		RemoteName:      "soft",
+		RemoteName:      "origin",
 		ReferenceName:   "refs/heads/master",
 		Depth:           1,
 		InsecureSkipTLS: true,
@@ -141,7 +141,7 @@
 	return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
 		URL:             fmt.Sprintf("%s/%s", ss.addressGit(), name),
 		Auth:            ss.authGit(),
-		RemoteName:      "soft",
+		RemoteName:      "origin",
 		ReferenceName:   "refs/heads/master",
 		Depth:           1,
 		InsecureSkipTLS: true,
@@ -166,7 +166,7 @@
 
 func (ss *Client) Push(repo *git.Repository) error {
 	return repo.Push(&git.PushOptions{
-		RemoteName: "soft",
+		RemoteName: "origin",
 		Auth:       ss.authGit(),
 	})
 }
@@ -188,7 +188,7 @@
 	return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
 		URL:             ss.repoPathByName(name),
 		Auth:            ss.authGit(),
-		RemoteName:      "soft",
+		RemoteName:      "origin",
 		InsecureSkipTLS: true,
 	})
 }