mirror of
https://github.com/packwiz/packwiz.git
synced 2025-04-12 01:16:30 +02:00
feat: begin github support
This commit is contained in:
parent
ef049968b1
commit
07033023af
@ -4,12 +4,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/exp/slices"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const UserAgent = "packwiz/packwiz"
|
||||
|
70
github/github.go
Normal file
70
github/github.go
Normal file
@ -0,0 +1,70 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"github.com/packwiz/packwiz/cmd"
|
||||
"github.com/packwiz/packwiz/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var githubCmd = &cobra.Command{
|
||||
Use: "github",
|
||||
Aliases: []string{"gh"},
|
||||
Short: "Manage github-based mods",
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.Add(githubCmd)
|
||||
core.Updaters["github"] = ghUpdater{}
|
||||
}
|
||||
|
||||
type ModReleases 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 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"`
|
||||
} `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 []Asset `json:"assets"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
ZipballURL string `json:"zipball_url"`
|
||||
Body string `json:"body"`
|
||||
Reactions struct {
|
||||
URL string `json:"url"`
|
||||
TotalCount int `json:"total_count"`
|
||||
Num1 int `json:"+1"`
|
||||
Num10 int `json:"-1"`
|
||||
Laugh int `json:"laugh"`
|
||||
Hooray int `json:"hooray"`
|
||||
Confused int `json:"confused"`
|
||||
Heart int `json:"heart"`
|
||||
Rocket int `json:"rocket"`
|
||||
Eyes int `json:"eyes"`
|
||||
} `json:"reactions"`
|
||||
}
|
504
github/install.go
Normal file
504
github/install.go
Normal file
@ -0,0 +1,504 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/packwiz/packwiz/core"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var modSiteRegex = regexp.MustCompile("modrinth\\.com/mod/([^/]+)/?$")
|
||||
var versionSiteRegex = regexp.MustCompile("modrinth\\.com/mod/([^/]+)/version/([^/]+)/?$")
|
||||
|
||||
// installCmd represents the install command
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install [mod]",
|
||||
Short: "Install a mod from a github URL",
|
||||
Aliases: []string{"add", "get"},
|
||||
Args: cobra.ArbitraryArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
pack, err := core.LoadPack()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(args) == 0 || len(args[0]) == 0 {
|
||||
fmt.Println("You must specify a mod.")
|
||||
os.Exit(1)
|
||||
}
|
||||
if strings.HasSuffix(args[0], "/") {
|
||||
fmt.Println("Url cant have a leading slash!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
slug := strings.Replace(args[0], "https://github.com/", "", 1)
|
||||
|
||||
mod, err := fetchMod(slug)
|
||||
|
||||
installMod(mod, pack)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
githubCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
const githubApiUrl = "https://api.github.com/"
|
||||
|
||||
func fetchMod(slug string) (Mod, error) {
|
||||
var modReleases []ModReleases
|
||||
var mod Mod
|
||||
resp, err := http.Get(githubApiUrl + "repos/" + slug + "/releases")
|
||||
if err != nil {
|
||||
return mod, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 404 {
|
||||
return mod, fmt.Errorf("mod not found (for URL %v)", githubApiUrl+"repos/"+slug+"/releases")
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return mod, fmt.Errorf("invalid response status %v for URL %v", resp.Status, githubApiUrl+"repos/"+slug+"/releases")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return mod, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &modReleases)
|
||||
if err != nil {
|
||||
return mod, err
|
||||
}
|
||||
|
||||
var repoData Repo
|
||||
|
||||
repoResp, err := http.Get(githubApiUrl + "repos/" + slug)
|
||||
|
||||
defer repoResp.Body.Close()
|
||||
repoBody, err := ioutil.ReadAll(repoResp.Body)
|
||||
if err != nil {
|
||||
return mod, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(repoBody, &repoData)
|
||||
if err != nil {
|
||||
return mod, err
|
||||
}
|
||||
|
||||
release := modReleases[0]
|
||||
|
||||
mod = Mod{
|
||||
ID: repoData.Name,
|
||||
Slug: slug,
|
||||
Team: repoData.Owner.Login,
|
||||
Title: repoData.Name,
|
||||
Description: repoData.Description,
|
||||
Published: repoData.CreatedAt,
|
||||
Updated: release.CreatedAt,
|
||||
License: repoData.License,
|
||||
ClientSide: "unknown",
|
||||
ServerSide: "unknown",
|
||||
Categories: repoData.Topics,
|
||||
}
|
||||
if mod.ID == "" {
|
||||
return mod, errors.New("invalid json whilst fetching mod: " + slug)
|
||||
}
|
||||
|
||||
return mod, nil
|
||||
|
||||
}
|
||||
|
||||
func installMod(mod Mod, pack core.Pack) error {
|
||||
fmt.Printf("Found mod %s: '%s'.\n", mod.Title, mod.Description)
|
||||
|
||||
latestVersion, err := getLatestVersion(mod.Slug, pack)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get latest version: %v", err)
|
||||
}
|
||||
if latestVersion.URL == "" {
|
||||
return errors.New("mod is not available for this Minecraft version (use the acceptable-game-versions option to accept more) or mod loader")
|
||||
}
|
||||
|
||||
return installVersion(mod, latestVersion, pack)
|
||||
}
|
||||
|
||||
func getLatestVersion(slug string, pack core.Pack) (ModReleases, error) {
|
||||
var modReleases []ModReleases
|
||||
var release ModReleases
|
||||
|
||||
resp, err := http.Get(githubApiUrl + "repos/" + slug + "/releases")
|
||||
if err != nil {
|
||||
return release, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 404 {
|
||||
return release, fmt.Errorf("mod not found (for URL %v)", githubApiUrl+"repos/"+slug+"/releases")
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return release, fmt.Errorf("invalid response status %v for URL %v", resp.Status, githubApiUrl+"repos/"+slug+"/releases")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return release, err
|
||||
}
|
||||
err = json.Unmarshal(body, &modReleases)
|
||||
if err != nil {
|
||||
return release, err
|
||||
}
|
||||
|
||||
return modReleases[0], nil
|
||||
}
|
||||
|
||||
func installVersion(mod Mod, version ModReleases, pack core.Pack) error {
|
||||
var files = version.Assets
|
||||
|
||||
if len(files) == 0 {
|
||||
return errors.New("version doesn't have any files attached")
|
||||
}
|
||||
|
||||
// TODO: add some way to allow users to pick which file to install?
|
||||
var file = files[0]
|
||||
for _, v := range version.Assets {
|
||||
if strings.HasSuffix(v.Name, ".jar") {
|
||||
file = v
|
||||
}
|
||||
}
|
||||
|
||||
//Install the file
|
||||
fmt.Printf("Installing %s from version %s\n", file.URL, version.Name)
|
||||
index, err := pack.LoadIndex()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updateMap := make(map[string]map[string]interface{})
|
||||
|
||||
updateMap["github"], err = ghUpdateData{
|
||||
ModID: mod.ID,
|
||||
InstalledVersion: version.TagName,
|
||||
}.ToMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// side := mod.getSide()
|
||||
// if side == "" {
|
||||
// return errors.New("version doesn't have a side that's supported. Server: " + mod.ServerSide + " Client: " + mod.ClientSide)
|
||||
// }
|
||||
|
||||
hash, error := file.getSha256()
|
||||
if error != nil || hash == "" {
|
||||
return errors.New("file doesn't have a hash")
|
||||
}
|
||||
|
||||
modMeta := core.Mod{
|
||||
Name: mod.Title,
|
||||
FileName: file.Name,
|
||||
Side: "unknown",
|
||||
Download: core.ModDownload{
|
||||
URL: file.BrowserDownloadURL,
|
||||
HashFormat: "sha256",
|
||||
Hash: hash,
|
||||
},
|
||||
Update: updateMap,
|
||||
}
|
||||
var path string
|
||||
folder := viper.GetString("meta-folder")
|
||||
if folder == "" {
|
||||
folder = "mods"
|
||||
}
|
||||
if mod.Slug != "" {
|
||||
path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, mod.Slug+core.MetaExtension))
|
||||
} else {
|
||||
path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, mod.Title+core.MetaExtension))
|
||||
}
|
||||
|
||||
// If the file already exists, this will overwrite it!!!
|
||||
// TODO: Should this be improved?
|
||||
// Current strategy is to go ahead and do stuff without asking, with the assumption that you are using
|
||||
// VCS anyway.
|
||||
|
||||
format, hash, err := modMeta.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = index.RefreshFileWithHash(path, format, hash, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = index.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = pack.UpdateIndexHash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = pack.Write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u ghUpdateData) ToMap() (map[string]interface{}, error) {
|
||||
newMap := make(map[string]interface{})
|
||||
err := mapstructure.Decode(u, &newMap)
|
||||
return newMap, err
|
||||
}
|
||||
|
||||
type License struct {
|
||||
Id string `json:"id"` //The license id of a mod, retrieved from the licenses get route
|
||||
Name string `json:"name"` //The long for name of a license
|
||||
Url string `json:"url"` //The URL to this license
|
||||
}
|
||||
|
||||
type Mod struct {
|
||||
ID string `json:"id"` //The ID of the mod, encoded as a base62 string
|
||||
Slug string `json:"slug"` //The slug of a mod, used for vanity URLs
|
||||
Team string `json:"team"` //The id of the team that has ownership of this mod
|
||||
Title string `json:"title"` //The title or name of the mod
|
||||
Description string `json:"description"` //A short description of the mod
|
||||
// Body string `json:"body"` //A long form description of the mod.
|
||||
// BodyUrl string `json:"body_url"` //DEPRECATED The link to the long description of the mod (Optional)
|
||||
Published string `json:"published"` //The date at which the mod was first published
|
||||
Updated string `json:"updated"` //The date at which the mod was updated
|
||||
License struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
SpdxID string `json:"spdx_id"`
|
||||
URL string `json:"url"`
|
||||
NodeID string `json:"node_id"`
|
||||
} `json:"license"`
|
||||
ClientSide string `json:"client_side"` //The support range for the client mod - required, optional, unsupported, or unknown
|
||||
ServerSide string `json:"server_side"` //The support range for the server mod - required, optional, unsupported, or unknown
|
||||
// Downloads int `json:"downloads"` //The total number of downloads the mod has
|
||||
Categories []string `json:"categories"` //A list of the categories that the mod is in
|
||||
Versions []string `json:"versions"` //A list of ids for versions of the mod
|
||||
IconUrl string `json:"icon_url"` //The URL of the icon of the mod (Optional)
|
||||
IssuesUrl string `json:"issues_url"` //An optional link to where to submit bugs or issues with the mod (Optional)
|
||||
SourceUrl string `json:"source_url"` //An optional link to the source code for the mod (Optional)
|
||||
WikiUrl string `json:"wiki_url"` //An optional link to the mod's wiki page or other relevant information (Optional)
|
||||
DiscordUrl string `json:"discord_url"` //An optional link to the mod's discord (Optional)
|
||||
}
|
||||
|
||||
type Asset struct {
|
||||
URL string `json:"url"`
|
||||
ID int `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
Name string `json:"name"`
|
||||
Label interface{} `json:"label"`
|
||||
Uploader 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"`
|
||||
} `json:"uploader"`
|
||||
ContentType string `json:"content_type"`
|
||||
State string `json:"state"`
|
||||
Size int `json:"size"`
|
||||
DownloadCount int `json:"download_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
BrowserDownloadURL string `json:"browser_download_url"`
|
||||
}
|
||||
|
||||
func (u Asset) getSha256() (string, error) {
|
||||
// TODO potentionally cache downloads to speed things up and avoid getting ratelimited by github!
|
||||
file, err := os.CreateTemp("", "download")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
mainHasher, err := core.GetHashImpl("sha256")
|
||||
resp, err := http.Get(u.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode == 404 {
|
||||
return "", fmt.Errorf("Asset not found")
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("Invalid response code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
file.Write(body)
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, file); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
hash := h.Sum(nil)
|
||||
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
return mainHasher.HashToString(hash), nil
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
ID int `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
Name string `json:"name"`
|
||||
FullName string `json:"full_name"`
|
||||
Private bool `json:"private"`
|
||||
Owner 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"`
|
||||
} `json:"owner"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Description string `json:"description"`
|
||||
Fork bool `json:"fork"`
|
||||
URL string `json:"url"`
|
||||
ForksURL string `json:"forks_url"`
|
||||
KeysURL string `json:"keys_url"`
|
||||
CollaboratorsURL string `json:"collaborators_url"`
|
||||
TeamsURL string `json:"teams_url"`
|
||||
HooksURL string `json:"hooks_url"`
|
||||
IssueEventsURL string `json:"issue_events_url"`
|
||||
EventsURL string `json:"events_url"`
|
||||
AssigneesURL string `json:"assignees_url"`
|
||||
BranchesURL string `json:"branches_url"`
|
||||
TagsURL string `json:"tags_url"`
|
||||
BlobsURL string `json:"blobs_url"`
|
||||
GitTagsURL string `json:"git_tags_url"`
|
||||
GitRefsURL string `json:"git_refs_url"`
|
||||
TreesURL string `json:"trees_url"`
|
||||
StatusesURL string `json:"statuses_url"`
|
||||
LanguagesURL string `json:"languages_url"`
|
||||
StargazersURL string `json:"stargazers_url"`
|
||||
ContributorsURL string `json:"contributors_url"`
|
||||
SubscribersURL string `json:"subscribers_url"`
|
||||
SubscriptionURL string `json:"subscription_url"`
|
||||
CommitsURL string `json:"commits_url"`
|
||||
GitCommitsURL string `json:"git_commits_url"`
|
||||
CommentsURL string `json:"comments_url"`
|
||||
IssueCommentURL string `json:"issue_comment_url"`
|
||||
ContentsURL string `json:"contents_url"`
|
||||
CompareURL string `json:"compare_url"`
|
||||
MergesURL string `json:"merges_url"`
|
||||
ArchiveURL string `json:"archive_url"`
|
||||
DownloadsURL string `json:"downloads_url"`
|
||||
IssuesURL string `json:"issues_url"`
|
||||
PullsURL string `json:"pulls_url"`
|
||||
MilestonesURL string `json:"milestones_url"`
|
||||
NotificationsURL string `json:"notifications_url"`
|
||||
LabelsURL string `json:"labels_url"`
|
||||
ReleasesURL string `json:"releases_url"`
|
||||
DeploymentsURL string `json:"deployments_url"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
PushedAt string `json:"pushed_at"`
|
||||
GitURL string `json:"git_url"`
|
||||
SSHURL string `json:"ssh_url"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
SvnURL string `json:"svn_url"`
|
||||
Homepage string `json:"homepage"`
|
||||
Size int `json:"size"`
|
||||
StargazersCount int `json:"stargazers_count"`
|
||||
WatchersCount int `json:"watchers_count"`
|
||||
Language string `json:"language"`
|
||||
HasIssues bool `json:"has_issues"`
|
||||
HasProjects bool `json:"has_projects"`
|
||||
HasDownloads bool `json:"has_downloads"`
|
||||
HasWiki bool `json:"has_wiki"`
|
||||
HasPages bool `json:"has_pages"`
|
||||
ForksCount int `json:"forks_count"`
|
||||
MirrorURL interface{} `json:"mirror_url"`
|
||||
Archived bool `json:"archived"`
|
||||
Disabled bool `json:"disabled"`
|
||||
OpenIssuesCount int `json:"open_issues_count"`
|
||||
License struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
SpdxID string `json:"spdx_id"`
|
||||
URL string `json:"url"`
|
||||
NodeID string `json:"node_id"`
|
||||
} `json:"license"`
|
||||
AllowForking bool `json:"allow_forking"`
|
||||
IsTemplate bool `json:"is_template"`
|
||||
Topics []string `json:"topics"`
|
||||
Visibility string `json:"visibility"`
|
||||
Forks int `json:"forks"`
|
||||
OpenIssues int `json:"open_issues"`
|
||||
Watchers int `json:"watchers"`
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
TempCloneToken interface{} `json:"temp_clone_token"`
|
||||
Organization 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"`
|
||||
} `json:"organization"`
|
||||
NetworkCount int `json:"network_count"`
|
||||
SubscribersCount int `json:"subscribers_count"`
|
||||
}
|
102
github/updater.go
Normal file
102
github/updater.go
Normal file
@ -0,0 +1,102 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/packwiz/packwiz/core"
|
||||
)
|
||||
|
||||
type ghUpdateData struct {
|
||||
ModID string `mapstructure:"mod-id"` // The slug of the repo but named modId for consistency reasons
|
||||
InstalledVersion string `mapstructure:"version"`
|
||||
}
|
||||
|
||||
type ghUpdater struct{}
|
||||
|
||||
func (u ghUpdater) ParseUpdate(updateUnparsed map[string]interface{}) (interface{}, error) {
|
||||
var updateData ghUpdateData
|
||||
err := mapstructure.Decode(updateUnparsed, &updateData)
|
||||
return updateData, err
|
||||
}
|
||||
|
||||
type cachedStateStore struct {
|
||||
ModID string
|
||||
Version ModReleases
|
||||
}
|
||||
|
||||
func (u ghUpdater) CheckUpdate(mods []core.Mod, mcVersion string, pack core.Pack) ([]core.UpdateCheck, error) {
|
||||
results := make([]core.UpdateCheck, len(mods))
|
||||
|
||||
for i, mod := range mods {
|
||||
rawData, ok := mod.GetParsedUpdateData("modrinth")
|
||||
if !ok {
|
||||
results[i] = core.UpdateCheck{Error: errors.New("couldn't parse mod data")}
|
||||
continue
|
||||
}
|
||||
|
||||
data := rawData.(ghUpdateData)
|
||||
|
||||
newVersion, err := getLatestVersion(data.ModID, pack)
|
||||
if err != nil {
|
||||
results[i] = core.UpdateCheck{Error: fmt.Errorf("failed to get latest version: %v", err)}
|
||||
continue
|
||||
}
|
||||
|
||||
// if newVersion.ID == "" { //There is no version available for this minecraft version or loader.
|
||||
// results[i] = core.UpdateCheck{UpdateAvailable: false}
|
||||
// continue
|
||||
// }
|
||||
|
||||
if newVersion.TagName == data.InstalledVersion { //The latest version from the site is the same as the installed one
|
||||
results[i] = core.UpdateCheck{UpdateAvailable: false}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(newVersion.Assets) == 0 {
|
||||
results[i] = core.UpdateCheck{Error: errors.New("new version doesn't have any files")}
|
||||
continue
|
||||
}
|
||||
|
||||
newFilename := newVersion.Assets[0].Name
|
||||
|
||||
results[i] = core.UpdateCheck{
|
||||
UpdateAvailable: true,
|
||||
UpdateString: mod.FileName + " -> " + newFilename,
|
||||
CachedState: cachedStateStore{data.ModID, newVersion},
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (u ghUpdater) DoUpdate(mods []*core.Mod, cachedState []interface{}) error {
|
||||
for i, mod := range mods {
|
||||
modState := cachedState[i].(cachedStateStore)
|
||||
var version = modState.Version
|
||||
|
||||
var file = version.Assets[0]
|
||||
for _, v := range version.Assets {
|
||||
if strings.HasSuffix(v.Name, ".jar") {
|
||||
file = v
|
||||
}
|
||||
}
|
||||
|
||||
hash, error := file.getSha256()
|
||||
if error != nil || hash == "" {
|
||||
return errors.New("file doesn't have a hash")
|
||||
}
|
||||
|
||||
mod.FileName = file.Name
|
||||
mod.Download = core.ModDownload{
|
||||
URL: file.BrowserDownloadURL,
|
||||
HashFormat: "sha256",
|
||||
Hash: hash,
|
||||
}
|
||||
mod.Update["modrinth"]["version"] = version.ID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user