mukan-ignite/ignite/pkg/gomodule/gomodule.go
Mukan Erkin Törük 26b204bd04
Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
feat: fork Ignite CLI v29 as Mukan Ignite — remove cosmos-sdk restrictions
2026-05-11 03:31:37 +03:00

202 lines
4.8 KiB
Go

package gomodule
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"slices"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"github.com/ignite/cli/v29/ignite/pkg/cache"
"github.com/ignite/cli/v29/ignite/pkg/cmdrunner/exec"
"github.com/ignite/cli/v29/ignite/pkg/cmdrunner/step"
"github.com/ignite/cli/v29/ignite/pkg/errors"
"github.com/ignite/cli/v29/ignite/pkg/gocmd"
)
const pathCacheNamespace = "gomodule.path"
var (
// ErrGoModNotFound returned when go.mod file cannot be found for an app.
ErrGoModNotFound = errors.New("go.mod not found")
// ErrModuleNotFound is returned when a Go module is not found.
ErrModuleNotFound = errors.New("module not found")
)
// Version is an alias to the module version type.
type Version = module.Version
// Module contains Go module info.
type Module struct {
// Path is the Go module path.
Path string
// Version is the module version.
Version string
// Dir is the absolute path to the Go module.
Dir string
}
// ParseAt finds and parses go.mod at app's path.
func ParseAt(path string) (*modfile.File, error) {
gomod, err := os.ReadFile(filepath.Join(path, "go.mod"))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, ErrGoModNotFound
}
return nil, err
}
return modfile.Parse("", gomod, nil)
}
// FilterVersions filters dependencies under require section by their paths.
func FilterVersions(dependencies []Version, paths ...string) []Version {
var filtered []Version
for _, dep := range dependencies {
if slices.Contains(paths, dep.Path) {
filtered = append(filtered, dep)
}
}
return filtered
}
// ResolveDependencies resolves dependencies from go.mod file.
// It replaces direct dependencies with their replacements.
func ResolveDependencies(f *modfile.File, includeIndirect bool) ([]Version, error) {
var versions []Version
isReplacementAdded := func(rv Version) bool {
for _, rep := range f.Replace {
if rv.Path == rep.Old.Path {
versions = append(versions, rep.New)
return true
}
}
return false
}
for _, req := range f.Require {
if req.Indirect && !includeIndirect {
continue
}
if !isReplacementAdded(req.Mod) {
versions = append(versions, req.Mod)
}
}
return versions, nil
}
// LocatePath locates pkg's absolute path managed by 'go mod' on the local filesystem.
func LocatePath(ctx context.Context, cacheStorage cache.Storage, src string, pkg Version) (path string, err error) {
// can be a local package.
if pkg.Version == "" { // indicates that this is a local package.
if filepath.IsAbs(pkg.Path) {
return pkg.Path, nil
}
return filepath.Join(src, pkg.Path), nil
}
pathCache := cache.New[string](cacheStorage, pathCacheNamespace)
cacheKey := cache.Key(pkg.Path, pkg.Version)
path, err = pathCache.Get(cacheKey)
if err != nil && !errors.Is(err, cache.ErrorNotFound) {
return "", err
}
if !errors.Is(err, cache.ErrorNotFound) {
return path, nil
}
// otherwise, it is hosted.
m, err := FindModule(ctx, src, pkg.String())
if err != nil {
return "", err
}
if err = pathCache.Put(cacheKey, m.Dir); err != nil {
return "", err
}
return m.Dir, nil
}
// SplitPath splits a Go import path into an URI path and version.
// Version is an empty string when the path doesn't contain a version suffix.
// Versioned paths use the "path@version" format.
func SplitPath(path string) (string, string) {
if len(path) == 0 || path[0] == '@' {
return "", ""
}
parts := strings.SplitN(path, "@", 2)
if len(parts) == 2 {
return parts[0], parts[1]
}
return parts[0], ""
}
// JoinPath joins a Go import path URI to a version.
// The result path have the "path@version" format.
func JoinPath(path, version string) string {
if path == "" {
return ""
}
if version == "" {
return path
}
return fmt.Sprintf("%s@%s", path, version)
}
// FindModule returns the Go module info for an import path.
// The module is searched within the dependencies of the module defined in root dir.
// If a local module path is passed, it returns the local module info.
func FindModule(ctx context.Context, rootDir, path string) (Module, error) {
// can be a local module.
if filepath.IsAbs(path) || strings.HasPrefix(path, ".") { // indicates that this is a local module.
return Module{
Path: path,
Version: "",
Dir: path,
}, nil
}
var stdout bytes.Buffer
err := gocmd.ModDownload(ctx, rootDir, true, exec.StepOption(step.Stdout(&stdout)))
if err != nil {
return Module{}, err
}
dec := json.NewDecoder(&stdout)
p, version := SplitPath(path)
for dec.More() {
var m Module
if err := dec.Decode(&m); err != nil {
if errors.Is(err, io.EOF) {
break
}
return Module{}, err
}
if m.Path == p && (version == "" || version == m.Version) {
return m, nil
}
}
return Module{}, errors.Errorf("%w: %s", ErrModuleNotFound, path)
}