init: core structure & RootFS download mechanism

This commit is contained in:
Erica Marigold 2022-11-25 20:13:14 +05:30
commit b0a57eed59
No known key found for this signature in database
GPG key ID: 23CD97ABBBCC5ED2
5 changed files with 352 additions and 0 deletions

17
go.mod Normal file
View file

@ -0,0 +1,17 @@
module github.com/CompeyDev/wsl-archlinux-manager
go 1.19
require (
github.com/briandowns/spinner v1.19.0
github.com/cavaliergopher/grab/v3 v3.0.1
github.com/gookit/color v1.5.2
)
require (
github.com/fatih/color v1.10.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
)

37
go.sum Normal file
View file

@ -0,0 +1,37 @@
github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E=
github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,76 @@
package checkers
import (
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/briandowns/spinner"
"github.com/cavaliergopher/grab/v3"
"github.com/gookit/color"
)
// TODO:
func VerifySignature(mirrorUrl string) {
bar := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
bar.Prefix = " "
bar.Suffix = " Verifying signature of RootFS..."
bar.Start()
success, _ := pullSig(mirrorUrl)
if !success {
color.Red.Println("\r ❎ Failed to download signature of RootFS. Refusing to continue.")
os.Exit(1)
}
userHomeDir, homeDirErr := os.UserHomeDir()
if homeDirErr != nil {
color.Red.Println("\r ❎ Failed to fetch installation directory, cannot verify authenticity of RootFS.")
os.Exit(1)
}
cwd, dirErr := os.Getwd()
if dirErr != nil {
color.Red.Println("\r ❎ Failed to fetch installation directory, cannot verify authenticity of RootFS.")
os.Exit(1)
}
unixWd := fmt.Sprintf("/mnt/c/%s", strings.ReplaceAll(strings.Split(userHomeDir, `C:\`)[1], `\`, "/")) + strings.ReplaceAll(strings.Split(cwd, userHomeDir)[1], `\`, "/")
// does not work?
getAuthenticity, authenticityErr := exec.Command("powershell.exe", fmt.Sprintf(`wsl bash -c "cd %s && gpg --keyserver-options auto-key-retrieve --verify archlinux-bootstrap-2022.11.01-x86_64.tar.gz.sig"`, unixWd)).Output()
if authenticityErr != nil {
color.Red.Println("\r ❎ Failed to verify authenticity of RootFS. Refusing to continue.")
os.Exit(1)
}
color.Green.Println("\r ✅ Successfully matched checksums and verified authenticity!")
bar.Stop()
fmt.Println(strings.Trim(string(getAuthenticity), "\n\r"))
}
func pullSig(url string) (isSuccessful bool, error error) {
bar := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
bar.Prefix = " "
bar.Suffix = " Downloading Signature..."
var version = strings.Split(strings.Split(url, "iso/")[1], "/")[0]
var structuredUrl = fmt.Sprintf("%s/archlinux-bootstrap-%s-x86_64.tar.gz.sig", url, version)
res, err := grab.Get(".", structuredUrl)
if err != nil {
color.Red.Println("\r ❎ Failed to download Signature.")
return false, err
}
color.Green.Println("\r ✅ Downloaded Signature", res.Filename)
return true, nil
}

125
lib/core/core.go Normal file
View file

@ -0,0 +1,125 @@
package core
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"encoding/json"
"github.com/CompeyDev/wsl-archlinux-manager/lib/checkers"
"github.com/briandowns/spinner"
"github.com/cavaliergopher/grab/v3"
"github.com/gookit/color"
)
// TODO:
func Build() {
fmt.Println("🔃 Setting up RootFS...")
bar := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
bar.Prefix = " "
bar.Suffix = " Determining the fastest mirror..."
bar.Start()
userLocation, err := http.Get("http://ip-api.com/json/")
if err != nil {
print(err)
}
defer userLocation.Body.Close()
body, reqErr := io.ReadAll(userLocation.Body)
if reqErr != nil {
color.Red.Println("\r ❎ An internal error occurred when attempting to pull the RootFS. This is probably a bug; you might want to report this.")
bar.Stop()
os.Exit(1)
}
var resStruct struct {
Status string
Country string
CountryCode string
Region string
RegionName string
City string
Zip string
Lat float64
Lon float64
Timezone string
Isp string
Org string
As string
Query string
}
parseErr := json.Unmarshal([]byte(body), &resStruct)
if parseErr != nil {
color.Red.Println("\r ❎ Failed to parse response body! This is a probably a bug; you might want to report this.")
bar.Stop()
os.Exit(1)
}
mirror := getMirror(resStruct.Country)
bar.Suffix = fmt.Sprintf(" Using mirror %s...", mirror)
time.Sleep(time.Second * 5)
bar.Stop()
isSuccessful_1, _ := pullArchive(mirror)
if !isSuccessful_1 {
color.Yellow.Println("\r ❎ Attempt #1 to pull RootFS failed, trying again with Worldwide...")
bar.Suffix = " Attempt #1 to pull RootFS failed, trying again with Worldwide..."
bar.Start()
globalMirror := getMirror("Worldwide")
isSuccessful_2, _ := pullArchive(globalMirror)
if !isSuccessful_2 {
color.Red.Println("\r ❎ Attempt #2 to pull RootFS failed. Please try again.")
bar.Stop()
os.Exit(1)
} else {
checkers.VerifySignature(globalMirror)
}
} else {
checkers.VerifySignature(mirror)
}
}
func getMirror(country string) string {
resp, err := http.Get("https://archlinux.org/download/")
if err != nil {
color.Red.Println("❎ Failed to download RootFS.")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
color.Red.Println("❎ An internal error occurred when attempting to pull the RootFS. This is probably a bug; you might want to report this.")
}
mirrorLink := strings.Split(strings.Split(strings.Split(strings.Split(strings.Split(strings.Split(string(body), fmt.Sprintf(`title="%s"`, country))[1], `title="Download from`)[0], fmt.Sprintf(`></span> %s</h5>`, country))[1], `<ul>`)[1], `<li><a href="`)[1], `"`)[0]
return mirrorLink
}
func pullArchive(url string) (isSuccessful bool, error error) {
bar := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
bar.Prefix = " "
bar.Suffix = " Downloading RootFS..."
var version = strings.Split(strings.Split(url, "iso/")[1], "/")[0]
var structuredUrl = fmt.Sprintf("%s/archlinux-bootstrap-%s-x86_64.tar.gz", url, version)
res, err := grab.Get(".", structuredUrl)
if err != nil {
color.Red.Println("\r ❎ Failed to download RootFS.")
return false, err
}
color.Bold.Println("\r ✅ Downloaded RootFS", res.Filename)
return true, nil
}

97
src/main.go Normal file
View file

@ -0,0 +1,97 @@
package main
import (
"fmt"
"io/fs"
"os"
"os/exec"
"path"
"runtime"
"strings"
"time"
"github.com/CompeyDev/wsl-archlinux-manager/lib/core"
"github.com/briandowns/spinner"
"github.com/gookit/color"
)
func main() {
if runtime.GOOS == "windows" {
checks()
core.Build()
} else {
fmt.Println("WSL is reserved for windows users only.")
os.Exit(1)
}
}
func build() {
checks()
}
// TODO: Add colors
func checks() {
color.Blueln("======> Pre-installation checks")
fmt.Println("🔃 Running checks...")
_, permsErr := os.Open("\\\\.\\PHYSICALDRIVE0")
if permsErr != nil {
fmt.Println("Please run this command with elevated priveleges.")
os.Exit(1)
}
bar := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
bar.Prefix = " "
bar.Suffix = " Checking for WSL..."
bar.Start()
getAvailability, availabilityErr := exec.Command("powershell.exe", "(Get-WindowsOptionalFeature -Online -FeatureName *Subsystem*).State").Output()
if availabilityErr != nil {
color.Red.Println("\r ❎ Failed to check for WSL availability.")
}
if strings.Trim(string(getAvailability), "\n\r") == "Enabled" {
color.Green.Println("\r ✅ WSL is enabled.")
bar.Stop()
bar.Prefix = " "
bar.Suffix = " Checking for existing WSL distributions..."
bar.Start()
preInstalledDistro, installedDistroErr := exec.Command("powershell.exe", "wsl").Output()
if installedDistroErr != nil {
color.Red.Println("\r ❎ Failed to check for preinstalled distributions.")
bar.Stop()
}
if strings.Contains(string(preInstalledDistro), "no installed distributions") {
color.Red.Println("\r ❎ Preinstalled distributions do exist. (Please make sure the default distribution is Debian-based)")
bar.Stop()
}
}
userHomeDir, homeDirErr := os.UserHomeDir()
bar.Suffix = " Creating install directory..."
bar.Start()
time.Sleep(300000000)
if homeDirErr != nil {
fmt.Println("❎ Failed to initialize installation directory.")
os.Exit(1)
}
installDir := path.Join(userHomeDir, ".wslm")
archDir := path.Join(installDir, "arch")
os.Mkdir(installDir, fs.FileMode(os.O_RDWR))
os.Mkdir(archDir, fs.FileMode(os.O_RDWR))
bar.Stop()
color.Green.Print("\r ✅ Successfully initialized installation directory.")
color.Bold.Println("\n✅ Initialized WSLm.")
color.Blueln("===============================")
}