Skip to content

Commit

Permalink
✨ feat: Write cli executable as a go program (#4)
Browse files Browse the repository at this point in the history
## Description

**What issue are you solving (or what feature are you adding) and how
are you doing it?**
I have opted to go with Golang as it has inbuilt support for writing
cross-platform executables unlike something like JS. And also, it has
support for running docker using the unix socket directly in code.
  • Loading branch information
wanjohiryan authored Jun 25, 2024
1 parent b1634c4 commit d95b4a8
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 21 deletions.
21 changes: 0 additions & 21 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,21 +0,0 @@
MIT License

Copyright (c) 2024 netris

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
20 changes: 20 additions & 0 deletions cmd/nestri.ascii
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
**************************************************
##################################################
##################################################
##################################################
##################################################
::::::::::::::::::::::::::::::::::::::::::::::::::

**************************************************
##################################################
##################################################
##################################################
##################################################
::::::::::::::::::::::::::::::::::::::::::::::::::

**************************************************
##################################################
##################################################
##################################################
##################################################
::::::::::::::::::::::::::::::::::::::::::::::::::
140 changes: 140 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
Copyright © 2024 Nestri <>
*/
package cmd

import (
_ "embed"
"fmt"
"os"
"strings"
"sync"

"github.com/nestriness/cli/pkg/specs"

"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/muesli/termenv"
"github.com/spf13/cobra"
)

//go:embed nestri.ascii
var art string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "nestri",
Short: "A CLI tool to manage your cloud gaming service",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}

var neoFetchCmd = &cobra.Command{
Use: "neofetch",
Short: "Show important system information",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
lipgloss.SetColorProfile(termenv.TrueColor)

// baseStyle := lipgloss.NewStyle().
// MarginTop(1).
// MarginRight(4).
// MarginBottom(1).
// MarginLeft(4)

var (
b strings.Builder
lines = strings.Split(art, "\n")
colors = []string{"#CC3D00", "#CC3D00"}
step = len(lines) / len(colors)
)

for i, l := range lines {
n := clamp(0, len(colors)-1, i/step)
b.WriteString(colorize(colors[n], l))
b.WriteRune('\n')
}

t := table.New().
Border(lipgloss.HiddenBorder()).BorderStyle(lipgloss.NewStyle().Width(3))
//TODO: show this specs
// info := &specs.Specs{}
// infoChan := make(chan specs.Specs, 1)
// var wg sync.WaitGroup
// wg.Add(1)
// go getSpecs(info, infoChan, &wg)
// wg.Wait()
// newInfo := <-infoChan

t.Row(b.String())

fmt.Print(t)

return nil
},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.AddCommand(neoFetchCmd)

// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)")

// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func colorize(c, s string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(s)
}

func clamp(v, low, high int) int {
if high < low {
low, high = high, low
}
return min(high, max(low, v))
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

func getSpecs(info *specs.Specs, infoChan chan specs.Specs, wg *sync.WaitGroup) {
defer wg.Done()
sys := specs.New()
// info.Userhost = getUserHostname()
// info.OS = getOSName()
// info.Kernel = getKernelVersion()
// info.Uptime = getUptime()
// info.Shell = getShell()
// info.CPU = getCPUName()
// info.RAM = getMemStats()
info.GPU, _ = sys.GetGPUInfo()
// info.SystemArch, _ = getSystemArch()
// info.DiskUsage, _ = getDiskUsage()
infoChan <- *info
}
18 changes: 18 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module github.com/nestriness/cli

go 1.22.2

require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.11.0 // indirect
github.com/charmbracelet/x/ansi v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.19.0 // indirect
)
30 changes: 30 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
11 changes: 11 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package main

import "github.com/nestriness/cli/cmd"

func main() {
cmd.Execute()
}
76 changes: 76 additions & 0 deletions pkg/specs/system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package specs

import (
"fmt"
"os/exec"
"runtime"
"strings"
)

type SysSpecs struct {
osx string
}

func New() *SysSpecs {
return &SysSpecs{osx: runtime.GOOS}
}

func (s SysSpecs) GetGPUInfo() (string, error) {
var output []byte
var err error

switch s.osx {
case "windows":
output, err = exec.Command("wmic", "path", "win32_VideoController", "get", "name").Output()
if err != nil {
return "", fmt.Errorf("error retrieving GPU information on Windows: %v", err)
}
case "darwin":
output, err = exec.Command("system_profiler", "SPDisplaysDataType").Output()
if err != nil {
return "", fmt.Errorf("error retrieving GPU information on macOS: %v", err)
}
case "linux":
output, err = exec.Command("lspci", "-vnn").Output()
if err != nil {
return "", fmt.Errorf("error retrieving GPU information on Linux: %v", err)
}
default:
return "", fmt.Errorf("error: GPU information retrieval not implemented for %s", runtime.GOOS)
}

outputStr := strings.TrimSpace(string(output))

if s.osx == "windows" {
lines := strings.Split(outputStr, "\r\n")[1:]
gpuName := strings.TrimSpace(strings.Join(lines, " "))
return gpuName, nil
}

if s.osx == "darwin" {
lines := strings.Split(outputStr, "\n")
for _, line := range lines {
if strings.Contains(line, "Chipset Model:") {
fields := strings.Split(line, ":")
if len(fields) >= 2 {
gpuName := strings.TrimSpace(fields[1])
return gpuName, nil
}
}
}
return "", fmt.Errorf("error parsing GPU information on macOS")
}

lines := strings.Split(outputStr, "\n")

for _, line := range lines {
if strings.Contains(line, "VGA compatible controller") {
fields := strings.Fields(line)
if len(fields) > 2 {
gpuName := strings.Join(fields[2:], " ")
return gpuName, nil
}
}
}
return "", fmt.Errorf("error parsing GPU information on Linux")
}
14 changes: 14 additions & 0 deletions pkg/specs/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package specs

type Specs struct {
Userhost string
OS string
Kernel string
Uptime string
Shell string
CPU string
RAM string
GPU string
SystemArch string
DiskUsage string
}

0 comments on commit d95b4a8

Please sign in to comment.