Compare commits

19 Commits
Author SHA1 Message Date
selimaj-dev 533a2f0d46 cross compatibility 2024-05-24 21:44:59 +02:00
selimaj-dev 3645bfc1c4 change to wpm 2024-05-24 13:43:56 +02:00
Leo dev 231281858e Update install-windows.bat 2024-05-24 04:28:59 -07:00
selimaj-dev 69fffab610 installers 2024-05-24 12:18:22 +02:00
selimaj-dev 68685b53b0 changed to public server 2024-05-23 22:56:36 +02:00
selimaj-dev 3990c620a1 small changes 2024-05-22 21:47:52 +02:00
selimaj-dev 7851d90185 Merge branch 'master' of github.com:wyst-lang/wyst-package-manager 2024-05-22 21:27:42 +02:00
selimaj-dev a3b05d2fbe fixed password input 2024-05-22 21:27:06 +02:00
RudideC 0b65ca4309 Added a clear and fixed issues
Clear is a cross platform function that, as its name implies, clears the terminal/command prompt.
Fixed issue related to sending edit requests even if pms is unavailable
2024-05-22 21:22:20 +02:00
selimaj-dev 81f68b6421 password safety 2024-05-22 20:17:28 +02:00
selimaj-dev 80452c2e19 added create, edit, delete 2024-05-22 12:14:17 +02:00
selimaj-dev 108b4e3266 added create, edit, delete 2024-05-21 19:32:38 +02:00
selimaj-dev 57dfe8a6af fixed github api limit 2024-05-21 17:05:43 +02:00
selimaj-dev 064f30fc39 progress bar fix 2024-05-21 16:44:36 +02:00
selimaj-dev 5c3f352126 progress bar 2024-05-21 16:39:06 +02:00
selimaj-dev 618b472d13 pms support 2024-05-21 15:26:01 +02:00
selimaj-dev 61493c0124 pms support 2024-05-21 15:25:40 +02:00
selimaj-dev 1e5bc0a5e2 Support with pms and gh releases 2024-05-21 14:19:11 +02:00
RudideC d4f61d1a58 Requests to pms
Changed it from using github to using pms
2024-05-20 19:36:27 +02:00
14 changed files with 610 additions and 117 deletions
+3
View File
@@ -6,3 +6,6 @@
# Output # Output
/bin /bin
# Lib
/lib
+23
View File
@@ -1,2 +1,25 @@
# wyst-package-manager # wyst-package-manager
A package manager for the programming language Wyst A package manager for the programming language Wyst
## Usage:
### Installing packages (version is optional)
```console
wpm install <package>[:version]
```
### Version
```console
wpm version
```
### Help
```console
wpm help
```
### Obtaining information from a package
```console
wpm info <package>
```
+6 -1
View File
@@ -1,3 +1,8 @@
module github.com/RudideC/wyst-package-manager module github.com/wyst-lang/wpm
go 1.22.0 go 1.22.0
require (
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // direct
)
+4
View File
@@ -0,0 +1,4 @@
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+4
View File
@@ -0,0 +1,4 @@
sudo cp -rf . ~/wpm
cd ~/wpm
go build -o bin/wpm src/*.go
sudo cp bin/wpm /usr/local/bin/
+16
View File
@@ -0,0 +1,16 @@
@echo off
cd %~dp0
where go >nul 2>&1
if %errorlevel% neq 0 (
echo please install go: https://go.dev/doc/install
start https://go.dev/doc/install
exit /b 1
) else (
@echo off
xcopy /s /e /i /h /y . "%USERPROFILE%\wpm"
cd /d "%USERPROFILE%\wpm\src"
go build -o ..\wpm.exe
copy ..\wpm.exe C:\Windows\System32
)
pause
+149
View File
@@ -0,0 +1,149 @@
package main
import (
"fmt"
"net/http"
"strings"
)
// const URL = "http://localhost:3000"
const URL = "https://pms-xdib.onrender.com"
func showHelp() {
fmt.Println("wpm - Wyst Package Manager")
fmt.Println("Commands:")
fmt.Println(" install <package>[:version] - Install a package (version is optional)")
fmt.Println(" create <package> - Create a package")
fmt.Println(" edit <package> - Edit a package")
fmt.Println(" delete <package> - Delete a package")
fmt.Println(" version - Show the wpm version")
fmt.Println(" help - Show this help message")
fmt.Println(" info <package> - Show package information (author, description, versions)")
}
func parsePackageArg(arg string) (string, string) {
parts := strings.Split(arg, ":")
if len(parts) == 2 {
return parts[0], parts[1]
}
return parts[0], ""
}
func createPackage(packageName string) {
var psw string
var pswV string
var repo string
var version string
fmt.Printf("Enter the password for %s: ", packageName)
ReadPassword(&psw)
fmt.Printf("Verify the password for %s: ", packageName)
ReadPassword(&pswV)
if psw != pswV {
fmt.Printf("Passwords do not match\n\r")
return
}
fmt.Printf("Enter the repo for %s: ", packageName)
fmt.Scanln(&repo)
fmt.Printf("Enter the latest version for %s: ", packageName)
fmt.Scanln(&version)
fmt.Println()
jsonBody := []byte(fmt.Sprintf(`{"name": "%v", "psw": "%v", "repo": "%v", "latest": "%v"}`, packageName, psw, repo, version))
req, err := sendRequest(http.MethodPost, URL, jsonBody)
if err != nil {
fmt.Println(err)
return
}
err = getMessage(req)
if err != nil {
fmt.Printf("Error: %v\n\r", err)
return
} else {
fmt.Printf("Done\n\r")
}
}
func editPackage(packageName string) {
pkgidx, err := getPackage(packageName)
if err != nil {
fmt.Println(err)
return
}
var psw string
var repo string = pkgidx.Repo
var version string = pkgidx.Latest
var newPsw string
var newPackageName string = packageName
fmt.Printf("\rEnter the password for %s: ", packageName)
ReadPassword(&psw)
newPsw = psw
for true {
clear()
fmt.Printf("Choose what you want to modify\n\r")
fmt.Printf(" 1 -> package name : %s\n\r", newPackageName)
fmt.Printf(" 2 -> password : %s\n\r", newPsw)
fmt.Printf(" 3 -> repo : %s\n\r", repo)
fmt.Printf(" 4 -> latest version: %s\n\r", version)
fmt.Printf("Type in 'quit'/'q'/'exit' to discard your changes\n\r")
fmt.Printf("Type in anything else to confirm your changes\n\r")
var option string
fmt.Print("\n> ")
fmt.Scanln(&option)
switch option {
case "1":
fmt.Printf("Enter the new name for for %s: ", packageName)
fmt.Scanln(&newPackageName)
case "2":
fmt.Printf("Enter the new password for %s: ", packageName)
ReadPassword(&newPsw)
case "3":
fmt.Printf("Enter the repo for %s: ", packageName)
fmt.Scanln(&repo)
case "4":
fmt.Printf("Enter the latest version for %s: ", packageName)
fmt.Scanln(&version)
case "q":
return
case "quit":
return
case "exit":
return
default:
jsonBody := []byte(fmt.Sprintf(`{ "new": {"name": "%v", "psw": "%v", "latest": "%v", "repo": "%v"}, "name": "%v", "psw": "%v"}`, newPackageName, newPsw, version, repo, packageName, psw))
req, err := sendRequest(http.MethodPut, URL, jsonBody)
if err != nil {
fmt.Printf("Error while editing %s: %v", packageName, err)
return
}
err = getMessage(req)
if err != nil {
fmt.Printf("Error: %v\n\r", err)
return
} else {
fmt.Printf("Done\n\r")
}
return
}
}
}
func deletePackage(packageName string) {
fmt.Printf("Enter the password for %s: ", packageName)
var psw string
ReadPassword(&psw)
jsonBody := []byte(fmt.Sprintf(`{"name": "%s", "psw": "%v"}`, packageName, psw))
req, err := sendRequest(http.MethodDelete, URL, jsonBody)
if err != nil {
fmt.Println(err)
return
}
err = getMessage(req)
if err != nil {
fmt.Printf("Error: %v\n\r", err)
return
} else {
fmt.Printf("Done\n\r")
}
}
-23
View File
@@ -1,23 +0,0 @@
package main
import (
"fmt"
"strings"
)
func showHelp() {
fmt.Println("wpm - Wyst Package Manager")
fmt.Println("Commands:")
fmt.Println(" install <package>[:version] - Install a package (version is optional)")
fmt.Println(" version - Show the wpm version")
fmt.Println(" help - Show this help message")
fmt.Println(" info <package> - Show package information (author, description, versions)")
}
func parsePackageArg(arg string) (string, string) {
parts := strings.Split(arg, ":")
if len(parts) == 2 {
return parts[0], parts[1]
}
return parts[0], ""
}
+80
View File
@@ -0,0 +1,80 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type Request struct {
Body []byte
StatusCode int
}
type Package struct {
Author string `json:"author"`
Description string `json:"description"`
URL string `json:"url"`
}
type PackageIndex struct {
Name string `json:"name"`
Repo string `json:"repo"`
Latest string `json:"latest"`
Message string `json:"message"`
}
type Message struct {
Message string `json:"message"`
}
func sendRequest(method, url string, jsonBody []byte) (Request, error) {
bodyReader := bytes.NewReader(jsonBody)
req, err := http.NewRequest(method, url, bodyReader)
if err != nil {
return Request{Body: []byte(""), StatusCode: 400}, err
}
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return Request{Body: []byte(""), StatusCode: 400}, err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return Request{Body: []byte(""), StatusCode: 400}, err
}
return Request{Body: resBody, StatusCode: res.StatusCode}, nil
}
func getPackage(packageName string) (PackageIndex, error) {
var pkgidx PackageIndex
jsonBody := []byte(fmt.Sprintf(`{"name": "%s"}`, packageName))
req, err := sendRequest(http.MethodGet, URL, jsonBody)
if err != nil {
fmt.Printf("Fetching error: %s\n", err)
}
err = json.Unmarshal(req.Body, &pkgidx)
if err != nil {
fmt.Println("Json Error: ", err)
return pkgidx, err
}
if req.StatusCode != 200 {
return pkgidx, fmt.Errorf(pkgidx.Message)
}
return pkgidx, nil
}
func getMessage(req Request) error {
var msg Message
err := json.Unmarshal(req.Body, &msg)
if err != nil {
return fmt.Errorf("JsonError: %v", err)
}
if msg.Message != "ok" {
return fmt.Errorf(msg.Message)
}
return nil
}
+140
View File
@@ -0,0 +1,140 @@
package main
import (
"archive/zip"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
)
type Author struct {
Login string `json:"login"`
ID int `json:"id"`
NodeID string `json:"node_id"`
AvatarURL string `json:"avatar_url"`
GravatarID string `json:"gravatar_id"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
FollowersURL string `json:"followers_url"`
FollowingURL string `json:"following_url"`
GistsURL string `json:"gists_url"`
StarredURL string `json:"starred_url"`
SubscriptionsURL string `json:"subscriptions_url"`
OrganizationsURL string `json:"organizations_url"`
ReposURL string `json:"repos_url"`
EventsURL string `json:"events_url"`
ReceivedEventsURL string `json:"received_events_url"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
// Define a struct for the Release
type Release struct {
URL string `json:"url"`
AssetsURL string `json:"assets_url"`
UploadURL string `json:"upload_url"`
HTMLURL string `json:"html_url"`
ID int `json:"id"`
Author Author `json:"author"`
NodeID string `json:"node_id"`
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
Assets []interface{} `json:"assets"` // Assuming assets is an array of unknown types
TarballURL string `json:"tarball_url"`
ZipballURL string `json:"zipball_url"`
Body string `json:"body"`
}
func Unzip(src, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
os.MkdirAll(dest, 0755)
// Closure to address file descriptors issue with all the deferred .Close() methods
extractAndWriteFile := func(f *zip.File) error {
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
path := filepath.Join(dest, f.Name)
// Check for ZipSlip (Directory traversal)
if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", path)
}
if f.FileInfo().IsDir() {
os.MkdirAll(path, f.Mode())
} else {
os.MkdirAll(filepath.Dir(path), f.Mode())
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
return nil
}
for _, f := range r.File {
err := extractAndWriteFile(f)
if err != nil {
return err
}
}
return nil
}
func downloadPackage(repoU, tag string) error {
resp, err := http.Get(fmt.Sprintf("https://github.com/%s/archive/refs/tags/%s.zip", strings.Trim(repoU, "/"), tag))
if err != nil {
return err
}
defer resp.Body.Close()
out, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading body:", err)
return err
}
err = ioutil.WriteFile("temp.zip", out, 0644)
if err != nil {
fmt.Println("Error writing file:", err)
return err
}
return nil
}
+24 -24
View File
@@ -1,36 +1,36 @@
package main package main
import ( import "fmt"
"encoding/json"
"fmt"
"net/http"
)
func showPackageInfo(packageName string) { func showPackageInfo(packageName string) {
url := fmt.Sprintf("https://raw.githubusercontent.com/wyst-lang/index/master/%s/index.json", packageName) progress := ProgressBar{total: 4, length: 20}
resp, err := http.Get(url) progress.change(3, "", "Fetching")
pkgidx, err := getPackage(packageName)
if err != nil { if err != nil {
fmt.Printf("Error fetching package info: %v\n", err) fmt.Printf("\nError getting repo URL: %v\n", err)
return return
} }
defer resp.Body.Close() progress.clean()
if resp.StatusCode != http.StatusOK { // indexURL, err := findIndexURL(repoURL)
fmt.Printf("Error: %s\n", resp.Status) // if err != nil {
return // fmt.Printf("Error finding index URL: %v\n", err)
} // return
// }
var pkgIndex PackageIndex // pkgIndex, err := fetchPackageIndex(indexURL)
if err := json.NewDecoder(resp.Body).Decode(&pkgIndex); err != nil { // if err != nil {
fmt.Printf("Error decoding package info: %v\n", err) // fmt.Printf("Error fetching package index: %v\n", err)
return // return
} // }
fmt.Printf("Package: %s\n", packageName) fmt.Printf("Package: %s\n", packageName)
for version, info := range pkgIndex.Versions { fmt.Printf("Repo: %s\n", pkgidx.Repo)
fmt.Printf("Version: %s\n", version) fmt.Printf("Version: %s\n", pkgidx.Latest)
fmt.Printf(" Author: %s\n", info.Author) // for version, info := range pkgIndex.Versions {
fmt.Printf(" Description: %s\n", info.Description) // fmt.Printf("Version: %s\n", version)
fmt.Println() // fmt.Printf(" Author: %s\n", info.Author)
} // fmt.Printf(" Description: %s\n", info.Description)
// fmt.Println()
// }
} }
+44 -69
View File
@@ -1,98 +1,73 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "net/url"
"net/http"
"os" "os"
"path/filepath" "strings"
) )
type RepoResponse struct {
Message string `json:"message"`
Name string `json:"name"`
Repo string `json:"repo"`
}
type PackageVersion struct { type PackageVersion struct {
Author string `json:"author"` Author string `json:"author"`
Description string `json:"description"` Description string `json:"description"`
URL string `json:"url"` URL string `json:"url"`
} }
type PackageIndex struct {
Versions map[string]PackageVersion `json:"versions"`
}
func installPackage(packageName, packageVersion string) { func installPackage(packageName, packageVersion string) {
url := fmt.Sprintf("https://raw.githubusercontent.com/wyst-lang/index/master/%s/index.json", packageName) progress := ProgressBar{total: 10, length: 20, enabled: true}
resp, err := http.Get(url) progress.change(1, "", "Fetching")
pkgidx, err := getPackage(packageName)
if err != nil { if err != nil {
fmt.Printf("Error fetching package info: %v\n", err) fmt.Printf("Error getting repo URL: %s\n", err)
return return
} }
defer resp.Body.Close() repoU, err := url.Parse(pkgidx.Repo)
if err != nil {
if resp.StatusCode != http.StatusOK { fmt.Printf("Error parsing repo URL: %s\n", err)
fmt.Printf("Error: %s\n", resp.Status)
return
} }
var pkgIndex PackageIndex
if err := json.NewDecoder(resp.Body).Decode(&pkgIndex); err != nil {
fmt.Printf("Error decoding package info: %v\n", err)
return
}
if packageVersion == "" { if packageVersion == "" {
packageVersion = getLatestVersion(pkgIndex) packageVersion = pkgidx.Latest
progress.change(3, "", fmt.Sprintf("Using version %s", packageVersion))
} }
progress.change(6, "", "Downloading")
pkgVersion, exists := pkgIndex.Versions[packageVersion] downloadPackage(repoU.Path, packageVersion)
if !exists { if err := os.Mkdir("wyst_tmp", os.ModePerm); err != nil {
fmt.Printf("Version %s not found for package %s\n", packageVersion, packageName) ERR := fmt.Sprintf("%s", err)
return if !strings.Contains(strings.ToLower(ERR), "file exists") {
} panic(ERR)
downloadAndSavePackage(pkgVersion.URL, packageName, packageVersion)
}
func getLatestVersion(pkgIndex PackageIndex) string {
var latest string
for version := range pkgIndex.Versions {
if version > latest {
latest = version
} }
} }
return latest progress.change(9, "", "Extracting")
} Unzip("temp.zip", "wyst_tmp")
if err := os.RemoveAll("lib/" + packageName); err != nil {
func downloadAndSavePackage(url, packageName, packageVersion string) { panic(err)
resp, err := http.Get(url) }
entries, err := os.ReadDir("./wyst_tmp")
if err != nil { if err != nil {
fmt.Printf("Error downloading package: %v\n", err) panic(err)
return
} }
defer resp.Body.Close() if err := os.Mkdir("lib", os.ModePerm); err != nil {
ERR := fmt.Sprintf("%s", err)
if resp.StatusCode != http.StatusOK { if !strings.Contains(strings.ToLower(ERR), "file exists") {
fmt.Printf("Error: %s\n", resp.Status) panic(ERR)
return
} }
libDir := "lib"
if err := os.MkdirAll(libDir, 0755); err != nil {
fmt.Printf("Error creating lib directory: %v\n", err)
return
} }
for _, e := range entries {
filename := filepath.Base(url) os.Rename("wyst_tmp/"+e.Name(), "lib/"+packageName)
packageFile := filepath.Join(libDir, filename)
out, err := os.Create(packageFile)
if err != nil {
fmt.Printf("Error creating package file: %v\n", err)
return
} }
defer out.Close() if err := os.RemoveAll("wyst_tmp"); err != nil {
panic(err)
if _, err := io.Copy(out, resp.Body); err != nil {
fmt.Printf("Error saving package: %v\n", err)
} else {
fmt.Printf("Package %s version %s downloaded and saved successfully as %s\n", packageName, packageVersion, filename)
} }
if err := os.Remove("temp.zip"); err != nil {
panic(err)
}
progress.change(10, "", "Installed")
progress.clean()
fmt.Printf("%s %s Installed\n\n", packageName, packageVersion)
} }
+22
View File
@@ -8,6 +8,7 @@ import (
const wpmVersion = "1.0.0" const wpmVersion = "1.0.0"
func main() { func main() {
fmt.Println()
if len(os.Args) < 2 { if len(os.Args) < 2 {
showHelp() showHelp()
return return
@@ -32,6 +33,27 @@ func main() {
return return
} }
showPackageInfo(os.Args[2]) showPackageInfo(os.Args[2])
case "create":
if len(os.Args) < 3 {
fmt.Println("Usage: wpm create <package>")
return
}
createPackage(os.Args[2])
case "delete":
if len(os.Args) < 3 {
fmt.Println("Usage: wpm delete <package>")
return
}
deletePackage(os.Args[2])
case "edit":
if len(os.Args) < 3 {
fmt.Println("Usage: wpm edit <package>")
return
}
editPackage(os.Args[2])
default: default:
fmt.Println("Unknown command:", command) fmt.Println("Unknown command:", command)
showHelp() showHelp()
+95
View File
@@ -0,0 +1,95 @@
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"golang.org/x/term"
)
type ProgressBar struct {
total int
length int
last_suffix int
enabled bool
}
func (prb *ProgressBar) change(amount int, prefix, suffix string) {
if prb.enabled {
percent := float64(amount) / float64(prb.total)
filledLength := int(float64(prb.length) * percent)
fill := "█"
end := "█"
if amount == prb.total {
end = "█"
}
for len(suffix) < prb.last_suffix {
suffix += " "
}
bar := strings.Repeat(fill, filledLength) + end + strings.Repeat("-", (prb.length-filledLength))
fmt.Printf("\r%s [%s] %s", prefix, bar, suffix)
// if amount == prb.total {
// fmt.Println()
// }
prb.last_suffix = len(suffix) + 3
}
}
func (prb ProgressBar) clean() {
if prb.enabled {
fmt.Printf("\r" + strings.Repeat(" ", prb.last_suffix+prb.length+2) + "\r")
}
}
func ReadPassword(buff *string) error {
safeChar := "*"
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return err
}
defer term.Restore(int(os.Stdin.Fd()), oldState)
for true {
b := make([]byte, 1)
_, err = os.Stdin.Read(b)
if err != nil {
return err
}
chr := string(b[0])
if chr == "\r" || chr == "\n" {
fmt.Printf("\n\r")
return nil
} else if chr == "\b" || b[0] == 127 {
if len(*buff) > 0 {
fmt.Print("\033[1D \033[1D")
*buff = string(*buff)[:len(*buff)-1]
}
} else if b[0] == 3 {
os.Exit(0)
} else {
*buff += chr
if safeChar == "off" {
fmt.Print(chr)
} else {
fmt.Print(safeChar)
}
}
}
return nil
}
func clear() {
switch runtime.GOOS {
case "windows":
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
default: // Unix-like systems
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
}
}