mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
* Sonic: Add sonic_on build tag Thinking about other ways to do this, but they amount to the same thing. It's messy, but I don't have another idea. * Okx: Remove redundant useAsIs on slices Slices are automatically used-as-is, so passing true for these types was unnecessary. Removing the field simplifies rationalising inverting the flag * Okx: Unify timestamp response types * Okx: Change ResetRFQMMPStatus to return a types.Time * Okx: Move withdrawData type to types * Okx: Fix AccountConfiguration slice return * Okx: Improve SendHTTPRequest documentation * Okx: Extend tests for UseAsIs non-defaults * Okx: Fix GetPublicUnderlying with sonic 1.12.9 Using **result for slices with useAsItIs causes sonic to fail. This might be addressed upstream, but it's also not clear what the unmarshal behaviour for an untyped reference to a typed reference should be in the RFC, so we could get a golang.org/encoding/json regression on this too. There's no harm in fixing this, for consistency, to match our handling for non-slice []any wrapping to just use the pointer as is. Note: As of today this requires sonic:main for this to work, because of the other bug: ``` M go.mod - github.com/bytedance/sonic v1.12.9 + github.com/bytedance/sonic v1.12.10-0.20250224121557-e30ac4f2e4fe ``` * Okx: Remove redundant slice check This code didn't work, and if I make it look at rv.Elem().Kind() it errors. Haven't dug too deeply but right now I think we just remove it. * Okx: Simplify SendHTTPRequest by removing UseAsIs Looks to be entirely derivable * Okx: Remove http check that resps must contain data GetAnnouncementTyeps failing in US because of empty response. But also any situation where there really is no data. e.g. GetCandlesticks might return no candlesticks for a period and instrument, because there aren't any. That shouldn't be an error. More saliently if you request orders, or something similar. So, since that check wasn't working before, and it's causing issues now, I'm going to remove it. * Okx: Fix TestGetAnnouncementTypes failing in US announcement-types returns empty in the US, where our github actions run. That's kinda okay. Just don't test we get any back * Sonic: Default to sonic off We've seen too many fatal panics and races with sonic, both in GCT runs and being reported in sonic, to default to it being turned on right now. Whilst we have faith sonic will get through these with time, for now the sensible thing to do for our users is make sonic opt-in. This also removes any of the conditions around 386, etc. If someone wants to run with sonic, they can. Most notably if they're trying out an experimental sonic branch that supports 386, etc.
631 lines
16 KiB
Go
631 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/common/file"
|
|
"github.com/thrasher-corp/gocryptotrader/core"
|
|
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
const (
|
|
// DefaultRepo is the main example repository
|
|
DefaultRepo = "https://api.github.com/repos/thrasher-corp/gocryptotrader"
|
|
|
|
// GithubAPIEndpoint allows the program to query your repository
|
|
// contributor list
|
|
GithubAPIEndpoint = "/contributors"
|
|
|
|
// LicenseFile defines a license file
|
|
LicenseFile = "LICENSE"
|
|
|
|
// ContributorFile defines contributor file
|
|
ContributorFile = "CONTRIBUTORS"
|
|
)
|
|
|
|
var (
|
|
// DefaultExcludedDirectories defines the basic directory exclusion list for GCT
|
|
DefaultExcludedDirectories = []string{
|
|
".github",
|
|
".git",
|
|
"node_modules",
|
|
".vscode",
|
|
".idea",
|
|
"cmd_templates",
|
|
"common_templates",
|
|
"communications_templates",
|
|
"config_templates",
|
|
"currency_templates",
|
|
"events_templates",
|
|
"exchanges_templates",
|
|
"portfolio_templates",
|
|
"root_templates",
|
|
"sub_templates",
|
|
"testdata_templates",
|
|
"tools_templates",
|
|
"web_templates",
|
|
}
|
|
|
|
// global flag for verbosity
|
|
verbose bool
|
|
// current tool directory to specify working templates
|
|
toolDir string
|
|
// exposes root directory if outside of document tool directory
|
|
repoDir string
|
|
// is a broken down version of the documentation tool dir for cross platform
|
|
// checking
|
|
ref = []string{"gocryptotrader", "cmd", "documentation"}
|
|
engineFolder = "engine"
|
|
)
|
|
|
|
// Contributor defines an account associated with this code base by doing
|
|
// contributions
|
|
type Contributor struct {
|
|
Login string `json:"login"`
|
|
URL string `json:"html_url"`
|
|
Contributions int `json:"contributions"`
|
|
}
|
|
|
|
// Config defines the running config to deploy documentation across a github
|
|
// repository including exclusion lists for files and directories
|
|
type Config struct {
|
|
GithubRepo string `json:"githubRepo"`
|
|
Exclusions Exclusions `json:"exclusionList"`
|
|
RootReadme bool `json:"rootReadmeActive"`
|
|
LicenseFile bool `json:"licenseFileActive"`
|
|
ContributorFile bool `json:"contributorFileActive"`
|
|
}
|
|
|
|
// Exclusions defines the exclusion list so documents are not generated
|
|
type Exclusions struct {
|
|
Files []string `json:"Files"`
|
|
Directories []string `json:"Directories"`
|
|
}
|
|
|
|
// DocumentationDetails defines parameters to update documentation
|
|
type DocumentationDetails struct {
|
|
Directories []string
|
|
Tmpl *template.Template
|
|
Contributors []Contributor
|
|
Config *Config
|
|
}
|
|
|
|
// Attributes defines specific documentation attributes when a template is
|
|
// executed
|
|
type Attributes struct {
|
|
Name string
|
|
Contributors []Contributor
|
|
NameURL string
|
|
Year int
|
|
CapitalName string
|
|
DonationAddress string
|
|
}
|
|
|
|
func main() {
|
|
flag.BoolVar(&verbose, "v", false, "Verbose output")
|
|
flag.StringVar(&toolDir, "tooldir", "", "Pass in the documentation tool directory if outside tool folder")
|
|
flag.Parse()
|
|
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
fmt.Println("Documentation tool error cannot get working dir:", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if strings.Contains(wd, filepath.Join(ref...)) {
|
|
rootDir := filepath.Dir(filepath.Dir(wd))
|
|
repoDir = rootDir
|
|
toolDir = wd
|
|
} else {
|
|
if toolDir == "" {
|
|
fmt.Println("Please set documentation tool directory via the tooldir flag if working outside of tool directory")
|
|
os.Exit(1)
|
|
}
|
|
repoDir = wd
|
|
}
|
|
|
|
fmt.Print(core.Banner)
|
|
fmt.Println("This will update and regenerate documentation for the different packages in your repo.")
|
|
fmt.Println()
|
|
|
|
if verbose {
|
|
fmt.Println("Fetching configuration...")
|
|
}
|
|
|
|
config, err := GetConfiguration()
|
|
if err != nil {
|
|
log.Fatalf("Documentation Generation Tool - GetConfiguration error %s",
|
|
err)
|
|
}
|
|
|
|
if verbose {
|
|
fmt.Println("Fetching project directory tree...")
|
|
}
|
|
|
|
dirList, err := GetProjectDirectoryTree(&config)
|
|
if err != nil {
|
|
log.Fatalf("Documentation Generation Tool - GetProjectDirectoryTree error %s",
|
|
err)
|
|
}
|
|
|
|
var contributors []Contributor
|
|
if config.ContributorFile {
|
|
if verbose {
|
|
fmt.Println("Fetching repository contributor list...")
|
|
}
|
|
contributors, err = GetContributorList(config.GithubRepo, verbose)
|
|
if err != nil {
|
|
log.Fatalf("Documentation Generation Tool - GetContributorList error %s",
|
|
err)
|
|
}
|
|
|
|
// Github API missing contributors
|
|
contributors = append(contributors, []Contributor{
|
|
{
|
|
Login: "andreygrehov",
|
|
URL: "https://github.com/andreygrehov",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "azhang",
|
|
URL: "https://github.com/azhang",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "bretep",
|
|
URL: "https://github.com/bretep",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "Christian-Achilli",
|
|
URL: "https://github.com/Christian-Achilli",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "cornelk",
|
|
URL: "https://github.com/cornelk",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "gam-phon",
|
|
URL: "https://github.com/gam-phon",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "if1live",
|
|
URL: "https://github.com/if1live",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "lozdog245",
|
|
URL: "https://github.com/lozdog245",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "MarkDzulko",
|
|
URL: "https://github.com/MarkDzulko",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "blombard",
|
|
URL: "https://github.com/blombard",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "cavapoo2",
|
|
URL: "https://github.com/cavapoo2",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "CodeLingoTeam",
|
|
URL: "https://github.com/CodeLingoTeam",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "CodeLingoBot",
|
|
URL: "https://github.com/CodeLingoBot",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "Daanikus",
|
|
URL: "https://github.com/Daanikus",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "daniel-cohen",
|
|
URL: "https://github.com/daniel-cohen",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "DirectX",
|
|
URL: "https://github.com/DirectX",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "frankzougc",
|
|
URL: "https://github.com/frankzougc",
|
|
Contributions: 1,
|
|
},
|
|
// idoall's contributors were forked and merged, so his contributions
|
|
// aren't automatically retrievable
|
|
{
|
|
Login: "idoall",
|
|
URL: "https://github.com/idoall",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "Jimexist",
|
|
URL: "https://github.com/Jimexist",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "lookfirst",
|
|
URL: "https://github.com/lookfirst",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "m1kola",
|
|
URL: "https://github.com/m1kola",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "mattkanwisher",
|
|
URL: "https://github.com/mattkanwisher",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "merkeld",
|
|
URL: "https://github.com/merkeld",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "mKurrels",
|
|
URL: "https://github.com/mKurrels",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "soxipy",
|
|
URL: "https://github.com/soxipy",
|
|
Contributions: 2,
|
|
},
|
|
{
|
|
Login: "starit",
|
|
URL: "https://github.com/starit",
|
|
Contributions: 1,
|
|
},
|
|
{
|
|
Login: "zeldrinn",
|
|
URL: "https://github.com/zeldrinn",
|
|
Contributions: 1,
|
|
},
|
|
}...)
|
|
|
|
if verbose {
|
|
fmt.Println("Contributor List Fetched")
|
|
for i := range contributors {
|
|
fmt.Println(contributors[i].Login)
|
|
}
|
|
}
|
|
} else {
|
|
fmt.Println("Contributor list file disabled skipping fetching details")
|
|
}
|
|
|
|
if verbose {
|
|
fmt.Println("Fetching template files...")
|
|
}
|
|
|
|
tmpl, err := GetTemplateFiles()
|
|
if err != nil {
|
|
log.Fatalf("Documentation Generation Tool - GetTemplateFiles error %s",
|
|
err)
|
|
}
|
|
|
|
if verbose {
|
|
fmt.Println("All core systems fetched, updating documentation...")
|
|
}
|
|
|
|
UpdateDocumentation(DocumentationDetails{
|
|
dirList,
|
|
tmpl,
|
|
contributors,
|
|
&config,
|
|
})
|
|
|
|
fmt.Println("\nDocumentation Generation Tool - Finished")
|
|
}
|
|
|
|
// GetConfiguration retrieves the documentation configuration
|
|
func GetConfiguration() (Config, error) {
|
|
var c Config
|
|
configFilePath := filepath.Join(toolDir, "config.json")
|
|
|
|
if file.Exists(configFilePath) {
|
|
config, err := os.ReadFile(configFilePath)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
err = json.Unmarshal(config, &c)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
if c.GithubRepo == "" {
|
|
return c, errors.New("repository not set in config.json file, please change")
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
fmt.Println("Creating configuration file, please check to add a different github repository path and change preferences")
|
|
|
|
// Set default params for configuration
|
|
c.GithubRepo = DefaultRepo
|
|
c.ContributorFile = true
|
|
c.LicenseFile = true
|
|
c.RootReadme = true
|
|
c.Exclusions.Directories = DefaultExcludedDirectories
|
|
|
|
data, err := json.MarshalIndent(c, "", " ")
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
if err := os.WriteFile(configFilePath, data, file.DefaultPermissionOctal); err != nil {
|
|
return c, err
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// IsExcluded returns if the file path is included in the exclusion list
|
|
func IsExcluded(path string, exclusion []string) bool {
|
|
for i := range exclusion {
|
|
if path == exclusion[i] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetProjectDirectoryTree uses filepath walk functions to get each individual
|
|
// directory name and path to match templates with
|
|
func GetProjectDirectoryTree(c *Config) ([]string, error) {
|
|
var directoryData []string
|
|
if c.RootReadme { // Projects root README.md
|
|
directoryData = append(directoryData, repoDir)
|
|
}
|
|
|
|
if c.LicenseFile { // Standard license file
|
|
directoryData = append(directoryData, filepath.Join(repoDir, LicenseFile))
|
|
}
|
|
|
|
if c.ContributorFile { // Standard contributor file
|
|
directoryData = append(directoryData, filepath.Join(repoDir, ContributorFile))
|
|
}
|
|
|
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
// Bypass what is contained in config.json directory exclusion
|
|
if IsExcluded(info.Name(), c.Exclusions.Directories) {
|
|
if verbose {
|
|
fmt.Println("Excluding Directory:", info.Name())
|
|
}
|
|
return filepath.SkipDir
|
|
}
|
|
// Don't append parent directory
|
|
if strings.EqualFold(info.Name(), "..") {
|
|
return nil
|
|
}
|
|
directoryData = append(directoryData, path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return directoryData, filepath.Walk(repoDir, walkFn)
|
|
}
|
|
|
|
// GetTemplateFiles parses and returns all template files in the documentation
|
|
// tree
|
|
func GetTemplateFiles() (*template.Template, error) {
|
|
tmpl := template.New("")
|
|
|
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
if path == "." || path == ".." {
|
|
return nil
|
|
}
|
|
|
|
var tmplExt *template.Template
|
|
tmplExt, err = tmpl.ParseGlob(filepath.Join(path, "*.tmpl"))
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
if strings.Contains(err.Error(), "pattern matches no files") {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
tmpl = tmplExt
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return tmpl, filepath.Walk(toolDir, walkFn)
|
|
}
|
|
|
|
// GetContributorList fetches a list of contributors from the github api
|
|
// endpoint
|
|
func GetContributorList(repo string, verbose bool) ([]Contributor, error) {
|
|
contents, err := common.SendHTTPRequest(context.TODO(),
|
|
http.MethodGet,
|
|
repo+GithubAPIEndpoint,
|
|
nil,
|
|
nil,
|
|
verbose)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var resp []Contributor
|
|
return resp, json.Unmarshal(contents, &resp)
|
|
}
|
|
|
|
// GetDocumentationAttributes returns specific attributes for a file template
|
|
func GetDocumentationAttributes(packageName string, contributors []Contributor) Attributes {
|
|
return Attributes{
|
|
Name: GetPackageName(packageName, false),
|
|
Contributors: contributors,
|
|
NameURL: GetGoDocURL(packageName),
|
|
Year: time.Now().Year(),
|
|
CapitalName: GetPackageName(packageName, true),
|
|
DonationAddress: core.BitcoinDonationAddress,
|
|
}
|
|
}
|
|
|
|
// GetPackageName returns the package name after cleaning path as a string
|
|
func GetPackageName(name string, capital bool) string {
|
|
newStrings := strings.Split(name, " ")
|
|
var i int
|
|
if len(newStrings) > 1 {
|
|
// retrieve the latest spacing to define the most childish package name
|
|
i = len(newStrings) - 1
|
|
}
|
|
if capital {
|
|
return strings.Replace(cases.Title(language.English).String(newStrings[i]), "_", " ", -1)
|
|
}
|
|
return newStrings[i]
|
|
}
|
|
|
|
// GetGoDocURL returns a string for godoc package names
|
|
func GetGoDocURL(name string) string {
|
|
if strings.Contains(name, " ") {
|
|
return strings.Join(strings.Split(name, " "), "/")
|
|
}
|
|
if name == "testdata" ||
|
|
name == "tools" ||
|
|
name == ContributorFile ||
|
|
name == LicenseFile {
|
|
return ""
|
|
}
|
|
return name
|
|
}
|
|
|
|
// UpdateDocumentation generates or updates readme/documentation files across
|
|
// the codebase
|
|
func UpdateDocumentation(details DocumentationDetails) {
|
|
for i := range details.Directories {
|
|
cutSet := details.Directories[i][len(repoDir):]
|
|
if cutSet != "" && cutSet[0] == os.PathSeparator {
|
|
cutSet = cutSet[1:]
|
|
}
|
|
|
|
data := strings.Split(cutSet, string(os.PathSeparator))
|
|
|
|
var temp []string
|
|
for x := range data {
|
|
if data[x] == ".." {
|
|
continue
|
|
}
|
|
if data[x] == "" {
|
|
break
|
|
}
|
|
temp = append(temp, data[x])
|
|
}
|
|
|
|
var name string
|
|
if len(temp) == 0 {
|
|
name = "root"
|
|
} else {
|
|
name = strings.Join(temp, " ")
|
|
}
|
|
|
|
if IsExcluded(name, details.Config.Exclusions.Files) {
|
|
if verbose {
|
|
fmt.Println("Excluding file:", name)
|
|
}
|
|
continue
|
|
}
|
|
if strings.Contains(name, engineFolder) {
|
|
d, err := os.ReadDir(details.Directories[i])
|
|
if err != nil {
|
|
fmt.Println("Excluding file:", err)
|
|
}
|
|
for x := range d {
|
|
nameSplit := strings.Split(d[x].Name(), ".go")
|
|
engineTemplateName := engineFolder + " " + nameSplit[0]
|
|
if details.Tmpl.Lookup(engineTemplateName) == nil {
|
|
fmt.Printf("Template not found for path %s create new template with {{define \"%s\" -}} TEMPLATE HERE {{end}}\n",
|
|
details.Directories[i],
|
|
name)
|
|
continue
|
|
}
|
|
err = runTemplate(details, filepath.Join(details.Directories[i], nameSplit[0]+".md"), engineTemplateName)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if details.Tmpl.Lookup(name) == nil {
|
|
fmt.Printf("Template not found for path %s create new template with {{define \"%s\" -}} TEMPLATE HERE {{end}}\n",
|
|
details.Directories[i],
|
|
name)
|
|
continue
|
|
}
|
|
var mainPath string
|
|
switch {
|
|
case name == LicenseFile || name == ContributorFile:
|
|
mainPath = details.Directories[i]
|
|
default:
|
|
mainPath = filepath.Join(details.Directories[i], "README.md")
|
|
}
|
|
|
|
if err := runTemplate(details, mainPath, name); err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func runTemplate(details DocumentationDetails, mainPath, name string) error {
|
|
err := os.Remove(mainPath)
|
|
if err != nil && !(strings.Contains(err.Error(), "no such file or directory") ||
|
|
strings.Contains(err.Error(), "The system cannot find the file specified.")) {
|
|
return err
|
|
}
|
|
|
|
f, err := os.Create(mainPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(f *os.File) {
|
|
err := f.Close()
|
|
if err != nil {
|
|
log.Printf("could not close file %s: %v", mainPath, err)
|
|
}
|
|
}(f)
|
|
|
|
attr := GetDocumentationAttributes(name, details.Contributors)
|
|
return details.Tmpl.ExecuteTemplate(f, name, attr)
|
|
}
|