modrank package - github.com/goccy/go-modrank - Go Packages

modrank

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 28, 2025 License: MIT Imports: 29 Imported by: 0

README

go-modrank

PkgGoDev Go

A tool to identify the Go modules that truly matter to you

Motivation

Most of us rely on open-source software (OSS) for our business. However, the OSS that is crucial to our business is not always properly recognized. For example, the number of GitHub stars is a useful metric for popularity, but it does not necessarily indicate how important a piece of software is to us.

If star counts were to increase according to importance, then libraries that applications and frameworks depend on should have more stars than the applications and frameworks themselves, which are closer to users. Unfortunately, this is not the case today.

To address this issue, I wanted to create a tool that quantitatively evaluates the importance of software, visualizes it, and connects it with other systems.

Use Case

This tool can score the Go software used by repositories associated with a specific GitHub organization. If you want to understand which Go-based software is critical to your organization, this tool will be useful.

A special feature of this tool is that it retrieves repositories hosting Go modules, allowing you to identify which repositories are essential to your needs.

Since this tool can also be used as a Go library, it can be applied to various other use cases, such as visualizing contributors to the repositories that are important to your organization.

Scoring Strategy

The most critical aspect of this tool is how it quantitatively evaluates importance. Below is an explanation of the scoring rules.

Assigning Scores to Repositories Based on Importance (Default: 1)

Organizations often contain repositories used for personal tool development as well as repositories used in production environments. The latter should be considered more important.

If your organization has a way to quantitatively assess repository importance, this tool allows you to reflect that value in its scoring.

Detecting go.mod and Retrieving Dependency Graphs

The tool clones repositories, navigates to paths containing go.mod, and executes go mod graph. It then analyzes the results to construct a dependency graph of Go modules. If multiple go.mod files are found, a graph will be created for each one.

Scoring Modules Based on the Dependency Graph

Modules that are not depended on by any other modules are called Root Modules. Their score is determined by the score assigned to their respective repositories.

As dependencies go deeper in the hierarchy, their score increases by 1 at each level. In other words, the modules located at the deepest level of the dependency hierarchy will have the highest scores.

Since root modules correspond to paths containing go.mod files, modules used in multiple go.mod files will naturally have higher scores.

If a module is recursively referenced within the dependency graph, its score will not be counted.

Installation

To use this tool as a standalone application, run the following command:

go install github.com/goccy/go-modrank/cmd/go-modrank@latest

For example, you can use the tool by executing the following command:

go-modrank run --repository https://github.com/goccy/go-modrank.git

For more details, use the command help:

go-modrank -h
Usage:
  main [OPTIONS] <run | update>

Help Options:
  -h, --help  Show this help message

Available commands:
  run     Scan all repositories and output ranking data
  update  Update repository status using the GitHub API to improve performance

Prerequisites

In order to use the go mod graph command, you will need to have the Go binary installed in your execution environment.

Synopsis

To use this tool as a library, you can follow the example below. By default, SQLite is used for data storage, but other databases can also be used. The behavior can be fine-tuned using options. For more details, refer to the API Reference.

package main

import (
	"context"
	"fmt"

	"github.com/goccy/go-modrank"
	"github.com/goccy/go-modrank/repository"
)

func main() {
	if err := run(context.Background()); err != nil {
		panic(err)
	}
}

func run(ctx context.Context) error {
	r, err := modrank.New(ctx)
	if err != nil {
		return err
	}
	repo, err := repository.New("https://github.com/goccy/go-modrank.git")
	if err != nil {
		return err
	}

	mods, err := r.Run(ctx, repo)
	if err != nil {
		return err
	}

	for idx, mod := range mods {
		fmt.Printf("- [%d] %s (%s): %d\n", idx+1, mod.Name, mod.Repository, mod.Score)
	}
	return nil
}

Features

  • Concurrent Scanning
    • You can specify workers to enable concurrent processing
  • Interruptible
    • By analyzing while saving scan results to the database, already searched items can be skipped even if the process is interrupted
  • Efficient API Calls Considering GitHub API Rate Limits
  • Automatically detects repositories hosting Go modules and includes them in the results

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Database     string   `yaml:"database"`
	Organization string   `yaml:"organization"`
	Repositories []string `yaml:"repositories"`
	ClonePath    string   `yaml:"clonePath"`
}

func LoadConfig

func LoadConfig(path string) (*Config, error)

type GitAccessToken added in v0.9.0

type GitAccessToken struct {
	// contains filtered or unexported fields
}

func GitStaticAccessToken added in v0.9.0

func GitStaticAccessToken(tk string) *GitAccessToken

type GitHubAccessToken added in v0.9.0

type GitHubAccessToken = GitAccessToken

func GitHubStaticAccessToken added in v0.9.0

func GitHubStaticAccessToken(tk string) *GitHubAccessToken

type GitHubClient

type GitHubClient struct {
	// contains filtered or unexported fields
}

func NewGitHubClient

func NewGitHubClient(ctx context.Context, token *GitHubAccessToken) *GitHubClient

func (*GitHubClient) CreateGitHubRepositoryCache

func (c *GitHubClient) CreateGitHubRepositoryCache(ctx context.Context, repos []*repository.Repository) error

func (*GitHubClient) ExistsGoMod

func (c *GitHubClient) ExistsGoMod(ctx context.Context, owner, repo string) (bool, error)

func (*GitHubClient) FindRepositoriesByOwner added in v0.2.0

func (c *GitHubClient) FindRepositoriesByOwner(ctx context.Context, owner string) ([]string, error)

func (*GitHubClient) GetHeadCommit

func (c *GitHubClient) GetHeadCommit(ctx context.Context, owner, repo string) (string, error)

func (*GitHubClient) IsArchived

func (c *GitHubClient) IsArchived(ctx context.Context, owner, repo string) (bool, error)

type GitHubRepository

type GitHubRepository struct {
	Repository *repository.Repository
	IsArchived bool
	HeadCommit string
}

type GoModule

type GoModule struct {
	// ID to uniquely identify a GoModule, hashed from Repository/GoModPath/Name/Version.
	ID string
	// Repository name of the repository using this Go module.
	Repository string
	// GoModPath path to the go.mod on the repository using this Go module.
	GoModPath string
	// Name is the Go module name. e.g.) github.com/goccy/go-modrank
	Name string
	// Version is the version text for the Go module. e.g.) v1.2.3
	Version string
	// HostedRepository is the hosted repository name of the Go module.
	HostedRepository string
	// Refers is the list of Modules this Go module depends on.
	Refers []*GoModule
	// Referers is th list of Modules on which this Go module is dependent.
	Referers []*GoModule
	// contains filtered or unexported fields
}

GoModule represents of the state of the Go module used in a given repository.

func (*GoModule) IsRoot

func (m *GoModule) IsRoot() bool

IsRoot returns whether this Go module is first dependent module or not.

func (*GoModule) ModPath

func (m *GoModule) ModPath() string

ModPath returns "Name@Version" format.

type GoModuleScore

type GoModuleScore struct {
	Name       string `json:"name"`
	Repository string `json:"repository"`
	Score      int    `json:"score"`
}

type GoModuleStorage

type GoModuleStorage interface {
	CreateGoModuleStorageIfNotExists(ctx context.Context) error
	FindRootGoModules(ctx context.Context) ([]*GoModule, error)
	FindGoModuleByID(ctx context.Context, id string) (*GoModule, error)
	InsertOrUpdateGoModules(ctx context.Context, nameWithOwner string, mods []*GoModule) error
}

type ModRank

type ModRank struct {
	// contains filtered or unexported fields
}

func New

func New(ctx context.Context, opts ...Option) (*ModRank, error)

func (*ModRank) Run

func (r *ModRank) Run(ctx context.Context, repos ...*repository.Repository) ([]*GoModuleScore, error)

Run compute and return the Go module score for each specified repository. If UpdateRepositoryStatusByGitHubAPI has been called previously, precomputed statuses can be used to reduce processing time.

func (*ModRank) Score added in v0.3.0

func (r *ModRank) Score(ctx context.Context, repos ...*repository.Repository) ([]*GoModuleScore, error)

Score compute and return the Go module score for each specified repository. This method uses the data already stored in the database and calculates only the Score. If you have not yet registered your data, use the Run method to register your data in advance.

func (*ModRank) UpdateRepositoryStatusByGitHubAPI

func (r *ModRank) UpdateRepositoryStatusByGitHubAPI(ctx context.Context, repos ...*repository.Repository) error

UpdateRepositoryStatusByGitHubAPI if you are working with a large number of repositories and they are all on GitHub, it is useful to skip the process of cloning the repositories by checking in advance whether they have been archived or whether they have a go.mod file, and thus shorten the process. This API checks for these things and saves them in the database.

type Option

type Option func(r *ModRank) error

func WithCleanupRepository added in v0.8.0

func WithCleanupRepository() Option

WithCleanupRepository delete the cloned repository after scanning is complete.

func WithGitAccessToken added in v0.6.0

func WithGitAccessToken(issuer TokenIssuer) Option

WithGitAccessToken if you want to access a private module when running go mod graph command, you need permission to access the repository hosting the module. Specifically, since `git ls-remote` command is used, access rights need to be set in gitconfig. This library allows you to specify the WithGitAuthToken option, which allows access to the repository using the specified token with temporary gitconfig.

func WithGitHubAPICache

func WithGitHubAPICache() Option

WithGitHubAPICache use the GitHub API to reduce the time spent scanning repositories as much as possible. If you are trying to scan private repositories, you need to set the access token in the GITHUB_TOKEN environment variable or specify the token directly in the WithGitHubToken() option.

func WithGitHubToken

func WithGitHubToken(issuer TokenIssuer) Option

WithGitHubToken specify the token for using the GitHub API. If this option is not specified, the value of the GITHUB_TOKEN environment variable is used.

func WithLogLevel

func WithLogLevel(v slog.Level) Option

WithLogLevel set log level. If you configure your logger with WithLogger() option, this option is ignored.

func WithLogger added in v0.3.0

func WithLogger(v *slog.Logger) Option

WithLogger set your logger.

func WithSQLiteDSN

func WithSQLiteDSN(dsn string) Option

WithSQLiteDSN set SQLite dsn. If this option is not specified, it is stored in the file os.TempDir()/go-modrank/tmp.db. If the WithStorage() option is specified, this option is ignored.

func WithStorage

func WithStorage(s Storage) Option

WithStorage specify the storage for storing the scan results. By default, SQLite is used, but if you want to use another database, you can change this option.

func WithTempDir added in v0.7.0

func WithTempDir(dir string) Option

WithTempDir specifies the directory to which the temporary file is written Use this directory if the database file is not specified or if the gitconfig file is written.

func WithWorker

func WithWorker(v int) Option

WithWorker set the number of workers scanning the repository in concurrent. Default is 1 (sequential).

type RepositoryStatus

type RepositoryStatus struct {
	NameWithOwner  string
	HeadCommitHash string
	IsArchived     bool
	ExistsGoMod    bool
}

type RepositoryStorage

type RepositoryStorage interface {
	CreateRepositoryStorageIfNotExists(ctx context.Context) error
	FindRepositoryByName(ctx context.Context, nameWithOwner string) (*RepositoryStatus, error)
	InsertOrUpdateRepository(ctx context.Context, st *RepositoryStatus) error
}

type SQLiteStorage

type SQLiteStorage struct {
	// contains filtered or unexported fields
}

func NewSQLiteStorage

func NewSQLiteStorage(dsn string) (*SQLiteStorage, error)

func (*SQLiteStorage) CreateGoModuleStorageIfNotExists

func (s *SQLiteStorage) CreateGoModuleStorageIfNotExists(ctx context.Context) error

func (*SQLiteStorage) CreateRepositoryStorageIfNotExists

func (s *SQLiteStorage) CreateRepositoryStorageIfNotExists(ctx context.Context) error

func (*SQLiteStorage) FindGoModuleByID

func (s *SQLiteStorage) FindGoModuleByID(ctx context.Context, id string) (*GoModule, error)

func (*SQLiteStorage) FindRepositoryByName

func (s *SQLiteStorage) FindRepositoryByName(ctx context.Context, nameWithOwner string) (*RepositoryStatus, error)

func (*SQLiteStorage) FindRootGoModules

func (s *SQLiteStorage) FindRootGoModules(ctx context.Context) ([]*GoModule, error)

func (*SQLiteStorage) InsertOrUpdateGoModules

func (s *SQLiteStorage) InsertOrUpdateGoModules(ctx context.Context, nameWithOwner string, mods []*GoModule) error

func (*SQLiteStorage) InsertOrUpdateRepository

func (s *SQLiteStorage) InsertOrUpdateRepository(ctx context.Context, st *RepositoryStatus) error

type Storage

type Storage interface {
	RepositoryStorage
	GoModuleStorage
}

type TokenIssuer added in v0.9.0

type TokenIssuer func(context.Context) (string, error)

Directories

Path Synopsis
cmd
go-modrank command
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL