Move server into separate module

Toby Padilla created

Change summary

.github/workflows/cd.yml         | 182 ----------------------------------
.github/workflows/ci.yml         |  34 ------
.github/workflows/soft-serve.yml |  22 ----
.gitignore                       |  10 -
Dockerfile                       |  23 ----
cmd/soft/main.go                 |  37 ++++++
config/config.go                 |   2 
go.mod                           |   2 
main.go                          |  59 -----------
main.tf                          |  45 --------
server.go                        |  44 ++++++++
tui/bubble.go                    |  10 
tui/bubbles/commits/bubble.go    |   2 
tui/bubbles/repo/bubble.go       |   4 
tui/bubbles/selection/bubble.go  |   2 
tui/commands.go                  |   6 
tui/defaults.go                  |  94 -----------------
tui/session.go                   |   2 
18 files changed, 99 insertions(+), 481 deletions(-)

Detailed changes

.github/workflows/cd.yml 🔗

@@ -1,182 +0,0 @@
-name: CD
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-
-
-jobs:
-  cd:
-    strategy:
-      matrix:
-        go-version: [~1.17]
-    runs-on: ubuntu-latest
-    env:
-      GO111MODULE: "on"
-      CONTAINER_REPO: "ghcr.io/${{ github.repository }}"
-      ENVIRONMENT: development
-      AWS_DEFAULT_REGION: us-east-1
-      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
-      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
-      SOFT_SERVE_REPO_KEYS: "${{ secrets.SOFT_SERVE_REPO_KEYS }}"
-
-    steps:
-    - name: Install Go
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-
-    - name: Checkout code
-      uses: actions/checkout@v2
-      with:
-        fetch-depth: 0
-
-    # Remove this later
-    - name: Clone internal repositories
-      run: |
-        git clone -b release https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/charm-internal ../charm
-        git clone -b master https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/bubbletea-internal ../bubbletea
-        git clone -b master https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/wish ../wish
-
-    - name: Login to GitHub Container Registry
-      uses: docker/login-action@v1
-      if: github.event_name == 'push'
-      with:
-        registry: ghcr.io
-        username: ${{ github.repository_owner }}
-        password: ${{ secrets.GITHUB_TOKEN }}
-
-    - name: Build Docker images using GoReleaser
-      uses: goreleaser/goreleaser-action@master
-      if: github.event_name == 'push'
-      with:
-        version: latest
-        # https://github.com/goreleaser/goreleaser/discussions/1534
-        args: -f .goreleaser.yml --snapshot
-
-    # Must add GH Actions write access
-    # https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
-    - name: Push Docker images
-      if: github.event_name == 'push'
-      run: |
-        docker push $CONTAINER_REPO:snapshot
-        docker push $CONTAINER_REPO:$GITHUB_SHA-snapshot
-
-    - name: Setup Terraform
-      uses: hashicorp/setup-terraform@v1
-      with:
-        # terraform_version: 0.13.0
-        cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
-
-    - name: Terraform Variables
-      id: tfvars
-      run: |
-        cat <<EOF >live.auto.tfvars
-        environment        = "$ENVIRONMENT"
-        aws_region         = "$AWS_DEFAULT_REGION"
-        app_image          = "$CONTAINER_REPO:$GITHUB_SHA-snapshot"
-        authorization_keys = <<EOT
-        $SOFT_SERVE_REPO_KEYS
-        EOT
-        EOF
-        TF_VARS="-var-file=live.auto.tfvars"
-        echo "::set-output name=vars::$TF_VARS"
-
-    - name: Terraform Format
-      id: fmt
-      run: terraform fmt -check
-
-    - name: Terraform Init
-      id: init
-      run: terraform init
-
-    - name: Terraform Validate
-      id: validate
-      run: terraform validate -no-color
-
-    - name: Terraform Plan
-      id: plan
-      if: github.event_name == 'pull_request'
-      run: terraform plan -no-color ${{ steps.tfvars.outputs.vars }}
-      continue-on-error: true
-
-    - name: Find Comment
-      if: github.event_name == 'pull_request'
-      uses: peter-evans/find-comment@v1.2.0
-      id: fc
-      with:
-        issue-number: ${{ github.event.pull_request.number }}
-        comment-author: github-actions[bot]
-        body-includes: Terraform Summary
-
-    - name: Update Pull Request
-      uses: actions/github-script@0.9.0
-      if: github.event_name == 'pull_request'
-      env:
-        PLAN: "${{ steps.plan.outputs.stdout }}"
-        COMMENT_ID: "${{ steps.fc.outputs.comment-id }}"
-      with:
-        github-token: ${{ secrets.GITHUB_TOKEN }}
-        script: |
-          const output = `## Terraform Summary
-          - Terraform Format and Style 🖌 \`${{ steps.fmt.outcome }}\`
-          - Terraform Initialization ⚙️ \`${{ steps.init.outcome }}\`
-          - Terraform Plan 📖 \`${{ steps.plan.outcome }}\`
-          - Terraform Validation 🤖 \`${{ steps.validate.outcome }}\`
-
-          <details><summary>Show Plan</summary>
-
-          \`\`\`\n
-          ${process.env.PLAN}
-          \`\`\`
-
-          </details>
-
-          *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
-
-          if (process.env.COMMENT_ID) {
-            github.issues.updateComment({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              comment_id: process.env.COMMENT_ID,
-              body: output
-            })
-          } else {
-            github.issues.createComment({
-              issue_number: context.issue.number,
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              body: output
-            })
-          }
-
-    - name: Terraform Plan Status
-      if: steps.plan.outcome == 'failure'
-      run: exit 1
-
-
-    - name: Terraform Apply
-      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
-      run: terraform apply -auto-approve ${{ steps.tfvars.outputs.vars }}
-
-
-  slack-workflow-status:
-    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
-    name: Post Workflow Status To Slack
-    needs:
-      - cd
-    runs-on: ubuntu-latest
-    steps:
-      - name: Slack Workflow Notification
-        uses: Gamesight/slack-workflow-status@master
-        with:
-          # Required Input
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
-          # Optional Input
-          channel: '#devops'
-          name: "${{ github.repository }} 🥤 workflow"
-          icon_emoji: ':cup_with_straw:'
-          icon_url: 'https://avatars.githubusercontent.com/u/57376114?s=200&v=4'

.github/workflows/ci.yml 🔗

@@ -1,34 +0,0 @@
-name: CI
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-
-jobs:
-
-  build:
-    strategy:
-      matrix:
-        go-version: [~1.17]
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v2
-
-    # Remove this later
-    - name: Clone internal repositories
-      run: |
-        git clone -b release https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/charm-internal ../charm
-        git clone -b master https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/bubbletea-internal ../bubbletea
-        git clone -b master https://${{ secrets.ACCESS_TOKEN }}@github.com/charmbracelet/wish ../wish
-
-    - name: Set up Go
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-
-    - name: Build
-      run: go build -v ./...
-
-    - name: Test
-      run: go test -v ./...

.github/workflows/soft-serve.yml 🔗

@@ -1,22 +0,0 @@
-name: Soft-Serve
-
-on:
-  push:
-    branches:
-      - main
-
-jobs:
-  softserve:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v2
-        with:
-          fetch-depth: 0
-
-      - name: Push to Soft-Serve
-        uses: charmbracelet/soft-serve-action@v1
-        with:
-          server: "beta.charm.sh"
-          ssh-key: "${{ secrets.CHARM_SOFT_SERVE_KEY }}"
-          name: "soft-serve"

.gitignore 🔗

@@ -1,7 +1,3 @@
-soft-serve
-.ssh
-.repos
-dist
-.terraform*
-*.tfstate*
-*auto.tfvars
+cmd/soft/soft
+cmd/soft/.ssh
+cmd/soft/.repos

Dockerfile 🔗

@@ -1,23 +0,0 @@
-FROM alpine:latest
-
-RUN apk update && apk add --update nfs-utils git && rm -rf /var/cache/apk/*
-
-COPY soft-serve /usr/local/bin/soft-serve
-
-# Create directories
-WORKDIR /soft-serve
-# Expose data volume
-VOLUME /soft-serve
-
-# Environment variables
-ENV SOFT_SERVE_KEY_PATH "/soft-serve/ssh/soft_serve_server_ed25519"
-ENV SOFT_SERVE_REPO_KEYS ""
-ENV SOFT_SERVE_REPO_KEYS_PATH "/soft-serve/ssh/soft_serve_git_authorized_keys"
-ENV SOFT_SERVE_REPO_PATH "/soft-serve/repos"
-
-# Expose ports
-# SSH
-EXPOSE 23231/tcp
-
-# Set the default command
-ENTRYPOINT [ "/usr/local/bin/soft-serve" ]

cmd/soft/main.go 🔗

@@ -0,0 +1,37 @@
+package main
+
+import (
+	"log"
+
+	"github.com/charmbracelet/soft"
+
+	"github.com/meowgorithm/babyenv"
+)
+
+type serverConfig struct {
+	Host     string `env:"SOFT_SERVE_HOST" default:""`
+	Port     int    `env:"SOFT_SERVE_PORT" default:"23231"`
+	KeyPath  string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
+	RepoPath string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
+	AuthKey  string `env:"SOFT_SERVE_AUTH_KEY" default:""`
+}
+
+func main() {
+	var cfg serverConfig
+	err := babyenv.Parse(&cfg)
+	if err != nil {
+		log.Fatalln(err)
+	}
+	s := soft.NewServer(
+		cfg.Host,
+		cfg.Port,
+		cfg.KeyPath,
+		cfg.RepoPath,
+		cfg.AuthKey,
+	)
+	log.Printf("Starting SSH server on %s:%d\n", cfg.Host, cfg.Port)
+	err = s.ListenAndServe()
+	if err != nil {
+		log.Fatalln(err)
+	}
+}

config/config.go 🔗

@@ -6,8 +6,8 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"soft-serve/git"
 
+	"github.com/charmbracelet/soft/git"
 	gg "github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/plumbing/object"
 )

go.mod 🔗

@@ -1,4 +1,4 @@
-module soft-serve
+module github.com/charmbracelet/soft
 
 go 1.17
 

main.go 🔗

@@ -1,59 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"log"
-	"soft-serve/config"
-	"soft-serve/git"
-	"soft-serve/tui"
-
-	"github.com/charmbracelet/wish"
-	bm "github.com/charmbracelet/wish/bubbletea"
-	gm "github.com/charmbracelet/wish/git"
-	lm "github.com/charmbracelet/wish/logging"
-	"github.com/gliderlabs/ssh"
-
-	"github.com/meowgorithm/babyenv"
-)
-
-type serverConfig struct {
-	Port     int    `env:"SOFT_SERVE_PORT" default:"23231"`
-	Host     string `env:"SOFT_SERVE_HOST" default:""`
-	InitKey  string `env:"SOFT_SERVE_REPO_KEY" default:""`
-	KeyPath  string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
-	RepoPath string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
-}
-
-func main() {
-	var scfg serverConfig
-	var cfg *config.Config
-	var err error
-	err = babyenv.Parse(&scfg)
-	if err != nil {
-		log.Fatalln(err)
-	}
-	rs := git.NewRepoSource(scfg.RepoPath)
-	cfg, err = config.NewConfig(scfg.Host, scfg.Port, scfg.InitKey, rs)
-	if err != nil {
-		log.Fatalln(err)
-	}
-	s, err := wish.NewServer(
-		ssh.PublicKeyAuth(cfg.PublicKeyHandler),
-		ssh.PasswordAuth(cfg.PasswordHandler),
-		wish.WithAddress(fmt.Sprintf("%s:%d", scfg.Host, scfg.Port)),
-		wish.WithHostKeyPath(scfg.KeyPath),
-		wish.WithMiddlewares(
-			bm.Middleware(tui.SessionHandler(cfg)),
-			gm.Middleware(scfg.RepoPath, cfg),
-			lm.Middleware(),
-		),
-	)
-	if err != nil {
-		log.Fatalln(err)
-	}
-	log.Printf("Starting SSH server on %s:%d\n", scfg.Host, scfg.Port)
-	err = s.ListenAndServe()
-	if err != nil {
-		log.Fatalln(err)
-	}
-}

main.tf 🔗

@@ -1,45 +0,0 @@
-terraform {
-  backend "s3" {
-    bucket = "charm-terraform-backend"
-    key    = "soft-serve-development"
-    region = "us-east-1"
-  }
-}
-
-variable "environment" {
-  default = "development"
-}
-
-variable "aws_region" {
-  default = "us-east-1"
-}
-
-variable "app_image" {
-  default = "ghcr.io/charmbracelet/soft-serve-internal:snapshot"
-}
-
-variable "force_new_deployment" {
-  default = false
-}
-
-variable "authorization_keys" {
-  default = ""
-}
-
-module "soft_serve" {
-  # source = "../terraform-aws-soft-serve"
-  source  = "app.terraform.io/charm/soft-serve/aws"
-  version = "0.3.2"
-
-  environment                  = var.environment
-  aws_region                   = var.aws_region
-  ecs_task_execution_role_name = "softServeEcsTaskExecutionRole-${var.environment}"
-  app_image                    = var.app_image
-  app_count                    = 2
-  app_ssh_port                 = 23231
-  fargate_cpu                  = "1024"
-  fargate_memory               = "2048"
-  force_new_deployment         = var.force_new_deployment
-  app_use_default_ssh_port     = true
-  authorization_keys           = var.authorization_keys
-}

server.go 🔗

@@ -0,0 +1,44 @@
+package soft
+
+import (
+	"fmt"
+	"log"
+
+	"github.com/charmbracelet/soft/config"
+	"github.com/charmbracelet/soft/git"
+	"github.com/charmbracelet/soft/tui"
+
+	"github.com/charmbracelet/wish"
+	bm "github.com/charmbracelet/wish/bubbletea"
+	gm "github.com/charmbracelet/wish/git"
+	lm "github.com/charmbracelet/wish/logging"
+	"github.com/gliderlabs/ssh"
+)
+
+// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
+// server key-pair will be created if none exists. An initial admin SSH public
+// key can be provided with authKey. If authKey is provided, access will be
+// restricted to that key. If authKey is not provided, the server will be
+// publicly writable until configured otherwise by cloning the `config` repo.
+func NewServer(host string, port int, serverKeyPath string, repoPath string, authKey string) *ssh.Server {
+	rs := git.NewRepoSource(repoPath)
+	cfg, err := config.NewConfig(host, port, authKey, rs)
+	if err != nil {
+		log.Fatalln(err)
+	}
+	s, err := wish.NewServer(
+		ssh.PublicKeyAuth(cfg.PublicKeyHandler),
+		ssh.PasswordAuth(cfg.PasswordHandler),
+		wish.WithAddress(fmt.Sprintf("%s:%d", host, port)),
+		wish.WithHostKeyPath(serverKeyPath),
+		wish.WithMiddlewares(
+			bm.Middleware(tui.SessionHandler(cfg)),
+			gm.Middleware(repoPath, cfg),
+			lm.Middleware(),
+		),
+	)
+	if err != nil {
+		log.Fatalln(err)
+	}
+	return s
+}

tui/bubble.go 🔗

@@ -2,15 +2,15 @@ package tui
 
 import (
 	"fmt"
-	"soft-serve/config"
-	"soft-serve/git"
-	"soft-serve/tui/bubbles/repo"
-	"soft-serve/tui/bubbles/selection"
-	"soft-serve/tui/style"
 	"strings"
 
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/soft/config"
+	"github.com/charmbracelet/soft/git"
+	"github.com/charmbracelet/soft/tui/bubbles/repo"
+	"github.com/charmbracelet/soft/tui/bubbles/selection"
+	"github.com/charmbracelet/soft/tui/style"
 )
 
 type sessionState int

tui/bubbles/commits/bubble.go 🔗

@@ -1,7 +1,7 @@
 package commits
 
 import (
-	"soft-serve/git"
+	"soft-serve/server/git"
 	"strings"
 
 	"github.com/charmbracelet/bubbles/viewport"

tui/bubbles/repo/bubble.go 🔗

@@ -4,8 +4,6 @@ import (
 	"bytes"
 	"fmt"
 	"log"
-	"soft-serve/git"
-	"soft-serve/tui/style"
 	"strconv"
 	"text/template"
 	"time"
@@ -14,6 +12,8 @@ import (
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/glamour"
 	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/soft/git"
+	"github.com/charmbracelet/soft/tui/style"
 	"github.com/muesli/reflow/truncate"
 	"github.com/muesli/reflow/wrap"
 )

tui/bubbles/selection/bubble.go 🔗

@@ -1,11 +1,11 @@
 package selection
 
 import (
-	"soft-serve/tui/style"
 	"strings"
 
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/soft/tui/style"
 	"github.com/muesli/reflow/truncate"
 )
 

tui/commands.go 🔗

@@ -3,13 +3,13 @@ package tui
 import (
 	"fmt"
 	"log"
-	"soft-serve/config"
-	"soft-serve/tui/bubbles/repo"
-	"soft-serve/tui/bubbles/selection"
 	"time"
 
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/soft/config"
+	"github.com/charmbracelet/soft/tui/bubbles/repo"
+	"github.com/charmbracelet/soft/tui/bubbles/selection"
 	"github.com/muesli/termenv"
 )
 

tui/defaults.go 🔗

@@ -1,94 +0,0 @@
-package tui
-
-import (
-	"os"
-	"path/filepath"
-	"soft-serve/git"
-
-	gg "github.com/go-git/go-git/v5"
-	"github.com/go-git/go-git/v5/plumbing/object"
-)
-
-const defaultReadme = "# Soft-Serve\n\n Welcome! You can configure your Soft-Serve server by cloning this repo and pushing changes.\n\n## Repos\n\n{{ range .Menu }}* {{ .Name }}{{ if .Note }} - {{ .Note }} {{ end }}\n  - `git clone ssh://{{$.Host}}:{{$.Port}}/{{.Repo}}`\n{{ end }}"
-
-const defaultConfig = `{
-	"name": "Soft-Serve",
-	"show_all_repos": true,
-	"host": "localhost",
-	"port": 23231,
-	"menu": [
-	  {
-			"name": "Home",
-			"repo": "config",
-			"note": "Configuration and content repo for this server"
-		}
-	]
-}`
-
-func createFile(path string, content string) error {
-	f, err := os.Create(path)
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-	_, err = f.WriteString(content)
-	if err != nil {
-		return err
-	}
-	return f.Sync()
-}
-
-func createDefaultConfigRepo(rs *git.RepoSource) error {
-	cn := "config"
-	err := rs.LoadRepos()
-	if err != nil {
-		return err
-	}
-	_, err = rs.GetRepo(cn)
-	if err == git.ErrMissingRepo {
-		cr, err := rs.InitRepo(cn, false)
-		if err != nil {
-			return err
-		}
-
-		rp := filepath.Join(rs.Path, cn, "README.md")
-		err = createFile(rp, defaultReadme)
-		if err != nil {
-			return err
-		}
-		cp := filepath.Join(rs.Path, cn, "config.json")
-		err = createFile(cp, defaultConfig)
-		if err != nil {
-			return err
-		}
-		wt, err := cr.Repository.Worktree()
-		if err != nil {
-			return err
-		}
-		_, err = wt.Add("README.md")
-		if err != nil {
-			return err
-		}
-		_, err = wt.Add("config.json")
-		if err != nil {
-			return err
-		}
-		_, err = wt.Commit("Default init", &gg.CommitOptions{
-			All: true,
-			Author: &object.Signature{
-				Name:  "Soft-Serve Server",
-				Email: "vt100@charm.sh",
-			},
-		})
-		if err != nil {
-			return err
-		}
-		err = rs.LoadRepos()
-		if err != nil {
-			return err
-		}
-	} else if err != nil {
-		return err
-	}
-	return nil
-}

tui/session.go 🔗

@@ -2,9 +2,9 @@ package tui
 
 import (
 	"fmt"
-	"soft-serve/config"
 
 	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/soft/config"
 	"github.com/gliderlabs/ssh"
 )