diff --git a/,gitattributes b/,gitattributes new file mode 100644 index 0000000..a0717e4 --- /dev/null +++ b/,gitattributes @@ -0,0 +1 @@ +*.go text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index b4ef2b5..d298506 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ # I use GoLand now .idea/ +# I no longer have student GoLand +.vscode/ + # Nix build output result diff --git a/README.md b/README.md index d9ed1d1..315040e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,11 @@ Join my Discord server if you need help [here](https://discord.gg/Csh8zbbhCt)! - Creation of remote file metadata from JAR files for CurseForge mods ## Installation -Prebuilt binaries are available from [GitHub Actions](https://github.com/packwiz/packwiz/actions) - the UI is a bit terrible, but essentially select the top build, then download the artifact ZIP for your system at the bottom of the page. To run the executable, add the folder where you downloaded it to your PATH environment variable ([see tutorial for Windows here](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)) or move it to where you want to use it. +Prebuilt binaries are available from [GitHub Actions](https://github.com/packwiz/packwiz/actions) - the UI is a bit terrible, but essentially select the top build, then download the artifact ZIP for your system at the bottom of the page. + +Another option is to use [nightly.link](https://nightly.link/packwiz/packwiz/workflows/go/main). Just go to the page, and download the artifact for your system. + +To run the executable, first extract it, then add the folder where you extracted it to your PATH environment variable ([see tutorial for Windows here](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)) or move it to where you want to use it. In future I will have a lot more installation options, but you can also compile from source: @@ -32,4 +36,4 @@ In future I will have a lot more installation options, but you can also compile 2. Run `go install github.com/packwiz/packwiz@latest`. Be patient, it has to download and compile dependencies as well! ## Documentation -See https://packwiz.infra.link/ for the full packwiz documentation! \ No newline at end of file +See https://packwiz.infra.link/ for the full packwiz documentation! diff --git a/cmd/init.go b/cmd/init.go index 9516093..45edd5d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -222,6 +222,10 @@ func init() { func initReadValue(prompt string, def string) string { fmt.Print(prompt) + if viper.GetBool("non-interactive") { + fmt.Printf("%s\n", def) + return def + } value, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { fmt.Printf("Error reading input: %s\n", err) diff --git a/core/download.go b/core/download.go index 4fe6558..60b5311 100644 --- a/core/download.go +++ b/core/download.go @@ -123,6 +123,7 @@ func reuseExistingFile(cacheHandle *CacheIndexHandle, hashesToObtain []string, m file, err := cacheHandle.Open() if err == nil { remainingHashes := cacheHandle.GetRemainingHashes(hashesToObtain) + var warnings []error if len(remainingHashes) > 0 { err = teeHashes(remainingHashes, cacheHandle.Hashes, io.Discard, file) if err != nil { @@ -134,13 +135,14 @@ func reuseExistingFile(cacheHandle *CacheIndexHandle, hashesToObtain []string, m _ = file.Close() return CompletedDownload{}, fmt.Errorf("failed to seek file %s in cache: %w", cacheHandle.Path(), err) } - cacheHandle.UpdateIndex() + warnings = cacheHandle.UpdateIndex() } return CompletedDownload{ - File: file, - Mod: mod, - Hashes: cacheHandle.Hashes, + File: file, + Mod: mod, + Hashes: cacheHandle.Hashes, + Warnings: warnings, }, nil } else { return CompletedDownload{}, fmt.Errorf("failed to read file %s from cache: %w", cacheHandle.Path(), err) @@ -229,7 +231,10 @@ func getHashListsForDownload(hashesToObtain []string, validateHashFormat string, hashes := make(map[string]string) hashes[validateHashFormat] = validateHash - cl := []string{cacheHashFormat} + var cl []string + if cacheHashFormat != validateHashFormat { + cl = append(cl, cacheHashFormat) + } for _, v := range hashesToObtain { if v != validateHashFormat && v != cacheHashFormat { cl = append(cl, v) @@ -552,6 +557,33 @@ func (h *CacheIndexHandle) Remove() { return } +func removeIndices(hashList []string, indices []int) []string { + i := 0 + for _, v := range hashList { + if len(indices) > 0 && i == indices[0] { + indices = indices[1:] + } else { + hashList[i] = v + i++ + } + } + return hashList[:i] +} + +func removeEmpty(hashList []string) ([]string, []int) { + var indices []int + i := 0 + for oldIdx, v := range hashList { + if v == "" { + indices = append(indices, oldIdx) + } else { + hashList[i] = v + i++ + } + } + return hashList[:i], indices +} + func CreateDownloadSession(mods []*Mod, hashesToObtain []string) (DownloadSession, error) { // Load cache index cacheIndex := CacheIndex{Version: 1, Hashes: make(map[string][]string)} @@ -588,6 +620,18 @@ func CreateDownloadSession(mods []*Mod, hashesToObtain []string) (DownloadSessio cacheIndex.Hashes[cacheHashFormat] = make([]string, 0) } cacheIndex.cachePath = cachePath + + // Clean up empty entries in index + var removedEntries []int + cacheIndex.Hashes[cacheHashFormat], removedEntries = removeEmpty(cacheIndex.Hashes[cacheHashFormat]) + if len(removedEntries) > 0 { + for hashFormat, v := range cacheIndex.Hashes { + if hashFormat != cacheHashFormat { + cacheIndex.Hashes[hashFormat] = removeIndices(v, removedEntries) + } + } + } + cacheIndex.nextHashIdx = len(cacheIndex.Hashes[cacheHashFormat]) // Create import folder diff --git a/curseforge/cursedir_other.go b/curseforge/cursedir_other.go index 8d0134d..822cac2 100644 --- a/curseforge/cursedir_other.go +++ b/curseforge/cursedir_other.go @@ -1,4 +1,4 @@ -// +build !windows +//go:build !windows package curseforge diff --git a/flake.lock b/flake.lock index 63ea6b3..cbc8415 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1689444953, - "narHash": "sha256-0o56bfb2LC38wrinPdCGLDScd77LVcr7CrH1zK7qvDg=", + "lastModified": 1703013332, + "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8acef304efe70152463a6399f73e636bcc363813", + "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 099840c..31f46ec 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,7 @@ { - inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + # This maps to https://github.com/NixOS/nixpkgs/tree/nixos-unstable + # The `url` option is the pattern of `github:USER_OR_ORG/REPO/BRANCH` outputs = { self, @@ -32,7 +34,9 @@ packwiz = pkgs.callPackage ./nix { version = substring 0 8 self.rev or "dirty"; vendorSha256 = readFile ./nix/vendor-sha256; - buildGoModule = pkgs.buildGo119Module; + buildGoModule = pkgs.buildGoModule; + # As of writing, `pkgs.buildGoModule` is aliased to + # `pkgs.buildGo121Module` in Nixpkgs. }; # Build packwiz by default when no package name is specified default = packwiz; diff --git a/modrinth/export.go b/modrinth/export.go index 295fe62..183b2cb 100644 --- a/modrinth/export.go +++ b/modrinth/export.go @@ -9,6 +9,7 @@ import ( "golang.org/x/exp/slices" "net/url" "os" + "sort" "strconv" "github.com/packwiz/packwiz/core" @@ -172,6 +173,10 @@ var exportCmd = &cobra.Command{ } } } + // sort by `path` property before serialising to ensure reproducibility + sort.Slice(manifestFiles, func(i, j int) bool { + return manifestFiles[i].Path < manifestFiles[j].Path + }) err = session.SaveIndex() if err != nil { diff --git a/modrinth/modrinth.go b/modrinth/modrinth.go index 2e920bd..fa38bbc 100644 --- a/modrinth/modrinth.go +++ b/modrinth/modrinth.go @@ -186,7 +186,7 @@ func getProjectTypeFolder(projectType string, fileLoaders []string, packLoaders var urlRegexes = [...]*regexp.Regexp{ // Slug/version number regex from https://github.com/modrinth/labrinth/blob/1679a3f844497d756d0cf272c5374a5236eabd42/src/util/validate.rs#L8 - regexp.MustCompile("^https?://modrinth\\.com/(?P[^/]+)/(?P[a-zA-Z0-9!@$()`.+,_\"-]{3,64})(?:/version/(?P[a-zA-Z0-9!@$()`.+,_\"-]{1,32}))?"), + regexp.MustCompile("^https?://(www.)?modrinth\\.com/(?P[^/]+)/(?P[a-zA-Z0-9!@$()`.+,_\"-]{3,64})(?:/version/(?P[a-zA-Z0-9!@$()`.+,_\"-]{1,32}))?"), // Version/project IDs are more restrictive: [a-zA-Z0-9]+ (base62) regexp.MustCompile("^https?://cdn\\.modrinth\\.com/data/(?P[a-zA-Z0-9]+)/versions/(?P[a-zA-Z0-9]+)/(?P[^/]+)$"), regexp.MustCompile("^(?P[a-zA-Z0-9!@$()`.+,_\"-]{3,64})$"), diff --git a/nix/prefetcher.nix b/nix/prefetcher.nix index 3f8fb2d..18ee3f6 100644 --- a/nix/prefetcher.nix +++ b/nix/prefetcher.nix @@ -3,7 +3,12 @@ pkgs ? import {}, }: pkgs.callPackage (import ./.) { - buildGoModule = pkgs.buildGo118Module; + + buildGoModule = pkgs.buildGoModule; + ## As of writing, `pkgs.buildGoModule` is aliased to + ## `pkgs.buildGo121Module` in Nixpkgs. + ## `buildGoModule` is set as `pkgs.buildGoModule` to try and work around + ## `vendorHash` issues in the future. vendorSha256 = sha256; } // { diff --git a/nix/vendor-sha256 b/nix/vendor-sha256 index b59ccb9..e69de29 100644 --- a/nix/vendor-sha256 +++ b/nix/vendor-sha256 @@ -1 +0,0 @@ -sha256-yL5pWbVqf6mEpgYsItLnv8nwSmoMP+SE0rX/s7u2vCg= diff --git a/settings/acceptable_versions.go b/settings/acceptable_versions.go index fce06ed..4853aaf 100644 --- a/settings/acceptable_versions.go +++ b/settings/acceptable_versions.go @@ -5,7 +5,6 @@ import ( "github.com/packwiz/packwiz/cmdshared" "github.com/packwiz/packwiz/core" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/unascribed/FlexVer/go/flexver" "golang.org/x/exp/slices" "os" @@ -42,8 +41,7 @@ var acceptableVersionsCommand = &cobra.Command{ } } // Check our flags to see if we're adding or removing - if viper.GetBool("settings.acceptable-versions.add") { - // Adding + if flagAdd { acceptableVersion := args[0] // Check if the version is already in the list if slices.Contains(currentVersions, acceptableVersion) { @@ -63,9 +61,9 @@ var acceptableVersionsCommand = &cobra.Command{ } // Print success message prettyList := strings.Join(currentVersions, ", ") + prettyList += ", " + modpack.Versions["minecraft"] fmt.Printf("Added %s to acceptable versions list, now %s\n", acceptableVersion, prettyList) - } else if viper.GetBool("settings.acceptable-versions.remove") { - // Removing + } else if flagRemove { acceptableVersion := args[0] // Check if the version is in the list if !slices.Contains(currentVersions, acceptableVersion) { @@ -87,6 +85,7 @@ var acceptableVersionsCommand = &cobra.Command{ } // Print success message prettyList := strings.Join(currentVersions, ", ") + prettyList += ", " + modpack.Versions["minecraft"] fmt.Printf("Removed %s from acceptable versions list, now %s\n", acceptableVersion, prettyList) } else { // Overwriting @@ -131,17 +130,19 @@ var acceptableVersionsCommand = &cobra.Command{ } // Print success message prettyList := strings.Join(acceptableVersionsDeduped, ", ") + prettyList += ", " + modpack.Versions["minecraft"] fmt.Printf("Set acceptable versions to %s\n", prettyList) } }, } +var flagAdd bool +var flagRemove bool + func init() { settingsCmd.AddCommand(acceptableVersionsCommand) // Add and remove flags for adding or removing specific versions - acceptableVersionsCommand.Flags().BoolP("add", "a", false, "Add a version to the list") - acceptableVersionsCommand.Flags().BoolP("remove", "r", false, "Remove a version from the list") - _ = viper.BindPFlag("settings.acceptable-versions.add", acceptableVersionsCommand.Flags().Lookup("add")) - _ = viper.BindPFlag("settings.acceptable-versions.remove", acceptableVersionsCommand.Flags().Lookup("remove")) + acceptableVersionsCommand.Flags().BoolVarP(&flagAdd, "add", "a", false, "Add a version to the list") + acceptableVersionsCommand.Flags().BoolVarP(&flagRemove, "remove", "r", false, "Remove a version from the list") }