diff --git a/.devportal/nodekit.md b/.devportal/nodekit.md new file mode 100644 index 00000000..a01d7cf1 --- /dev/null +++ b/.devportal/nodekit.md @@ -0,0 +1,295 @@ +--- +title: "nodekit" +--- +## Synopsis + + +Manage Algorand nodes from the command line + +Overview: +Welcome to NodeKit, a TUI for managing Algorand nodes. +A one stop shop for managing Algorand nodes, including node creation, configuration, and management. + +Note: This is still a work in progress. Expect bugs and rough edges. + +``` +nodekit [flags] +``` + +### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for nodekit + -n, --no-incentives Disable setting incentive eligibility fees +``` + +# Commands +## bootstrap + + +Initialize a fresh node + +Overview: +Get up and running with a fresh Algorand node. +Uses the local package manager to install Algorand, and then starts the node and preforms a Fast-Catchup. + +Note: This command only supports the default data directory, /var/lib/algorand + +``` +nodekit bootstrap [flags] +``` + +#### Options + +``` + -h, --help help for bootstrap +``` + +## catchup + + +Fast-Catchup is a feature that allows your node to catch up to the network faster than normal. + +Overview: +The entire process should sync a node in minutes rather than hours or days. +Actual sync times may vary depending on the number of accounts, number of blocks and the network. + +Note: Not all networks support Fast-Catchup. + +``` +nodekit catchup [flags] +``` + +#### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for catchup +``` + +## catchup debug + + +Display debug information for Fast-Catchup. + +Overview: +This information is useful for debugging fast-catchup issues. + +Note: Not all networks support Fast-Catchup. + +``` +nodekit catchup debug [flags] +``` + +#### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for debug +``` + +## catchup start + + +Catchup the node to the latest catchpoint. + +Overview: +Starting a catchup will sync the node to the latest catchpoint. +Actual sync times may vary depending on the number of accounts, number of blocks and the network. + +Note: Not all networks support Fast-Catchup. + +``` +nodekit catchup start [flags] +``` + +#### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for start +``` + +## catchup stop + + +Stop a fast catchup + +Overview: +Stop an active Fast-Catchup. This will abort the catchup process if one has started + +Note: Not all networks support Fast-Catchup. + +``` +nodekit catchup stop [flags] +``` + +#### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for stop +``` + +## configure + + +Change settings on the system (WIP) + +Overview: +Tool for inspecting and updating the Algorand daemon's config.json and service files + +Note: This is still a work in progress. Expect bugs and rough edges. + +#### Options + +``` + -h, --help help for configure +``` + +## configure service + + +Install service files for the Algorand daemon. + +Overview: +Ensuring that the Algorand daemon is installed and running as a service. + +Note: This is still a work in progress. Expect bugs and rough edges. + +``` +nodekit configure service [flags] +``` + +#### Options + +``` + -h, --help help for service +``` + +## debug + + +Display debugging information + +Overview: +Prints the known state of the nodekit +Checks various paths and configurations to present useful information for bug reports. + + +``` +nodekit debug [flags] +``` + +#### Options + +``` + -d, --datadir string Data directory for the node + -h, --help help for debug +``` + +## install + + +Install the node daemon + +Overview: +Configures the local package manager and installs the algorand daemon on your local machine + + +``` +nodekit install [flags] +``` + +#### Options + +``` + -f, --force forcefully install the node + -h, --help help for install +``` + +## start + + +Start the node daemon + +Overview: +Start the Algorand daemon on your local machine if it is not already running. Optionally, the daemon can be forcefully started. + +This requires the daemon to be installed on your system. + +``` +nodekit start [flags] +``` + +#### Options + +``` + -f, --force forcefully start the node + -h, --help help for start +``` + +## stop + + +Stop the node daemon + +Overview: +Stops the Algorand daemon on your local machine. Optionally, the daemon can be forcefully stopped. + +This requires the daemon to be installed on your system. + +``` +nodekit stop [flags] +``` + +#### Options + +``` + -f, --force forcefully stop the node + -h, --help help for stop +``` + +## uninstall + + +Uninstall the node daemon + +Overview: +Uninstall Algorand node (Algod) and other binaries on your system installed by this tool. + +This requires the daemon to be installed on your system. + +``` +nodekit uninstall [flags] +``` + +#### Options + +``` + -f, --force forcefully uninstall the node + -h, --help help for uninstall +``` + +## upgrade + + +Upgrade the node daemon + +Overview: +Upgrade Algorand packages if it was installed with package manager. + +This requires the daemon to be installed on your system. + +``` +nodekit upgrade [flags] +``` + +#### Options + +``` + -h, --help help for upgrade +``` + +###### Auto generated by spf13/cobra on 28-Jan-2025 diff --git a/README.md b/README.md index 976c7756..ea77a5ce 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ nodekit [flags] * [nodekit uninstall](/man/nodekit_uninstall.md) - Uninstall the node daemon * [nodekit upgrade](/man/nodekit_upgrade.md) - Upgrade the node daemon -###### Auto generated by spf13/cobra on 22-Jan-2025 ### Installing diff --git a/man/nodekit_bootstrap.md b/man/nodekit_bootstrap.md index 111cb2ea..1bb504ba 100644 --- a/man/nodekit_bootstrap.md +++ b/man/nodekit_bootstrap.md @@ -30,4 +30,3 @@ nodekit bootstrap [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_catchup.md b/man/nodekit_catchup.md index c35387b9..4155443d 100644 --- a/man/nodekit_catchup.md +++ b/man/nodekit_catchup.md @@ -34,4 +34,3 @@ nodekit catchup [flags] * [nodekit catchup start](/man/nodekit_catchup_start.md) - Get the latest catchpoint and start catching up. * [nodekit catchup stop](/man/nodekit_catchup_stop.md) - Stop a fast catchup -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_catchup_debug.md b/man/nodekit_catchup_debug.md index b66d6dc3..3cb5792a 100644 --- a/man/nodekit_catchup_debug.md +++ b/man/nodekit_catchup_debug.md @@ -30,4 +30,3 @@ nodekit catchup debug [flags] * [nodekit catchup](/man/nodekit_catchup.md) - Manage Fast-Catchup for your node -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_catchup_start.md b/man/nodekit_catchup_start.md index 92306447..fcd1dc3e 100644 --- a/man/nodekit_catchup_start.md +++ b/man/nodekit_catchup_start.md @@ -31,4 +31,3 @@ nodekit catchup start [flags] * [nodekit catchup](/man/nodekit_catchup.md) - Manage Fast-Catchup for your node -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_catchup_stop.md b/man/nodekit_catchup_stop.md index 249da1d0..e4aa0996 100644 --- a/man/nodekit_catchup_stop.md +++ b/man/nodekit_catchup_stop.md @@ -30,4 +30,3 @@ nodekit catchup stop [flags] * [nodekit catchup](/man/nodekit_catchup.md) - Manage Fast-Catchup for your node -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_configure.md b/man/nodekit_configure.md index 6782d6f0..f4be91be 100644 --- a/man/nodekit_configure.md +++ b/man/nodekit_configure.md @@ -26,4 +26,3 @@ Note: This is still a work in progress. Expect bugs and rough edges. * [nodekit](/README.md) - Manage Algorand nodes from the command line * [nodekit configure service](/man/nodekit_configure_service.md) - Install service files for the Algorand daemon. -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_configure_service.md b/man/nodekit_configure_service.md index d2f555ab..68c56779 100644 --- a/man/nodekit_configure_service.md +++ b/man/nodekit_configure_service.md @@ -29,4 +29,3 @@ nodekit configure service [flags] * [nodekit configure](/man/nodekit_configure.md) - Change settings on the system (WIP) -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_debug.md b/man/nodekit_debug.md index 181b19a9..e2945ed2 100644 --- a/man/nodekit_debug.md +++ b/man/nodekit_debug.md @@ -30,4 +30,3 @@ nodekit debug [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_install.md b/man/nodekit_install.md index cbaeff6b..d5ab1924 100644 --- a/man/nodekit_install.md +++ b/man/nodekit_install.md @@ -29,4 +29,3 @@ nodekit install [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_start.md b/man/nodekit_start.md index 9503a83a..b105c9a5 100644 --- a/man/nodekit_start.md +++ b/man/nodekit_start.md @@ -30,4 +30,3 @@ nodekit start [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_stop.md b/man/nodekit_stop.md index 39dc9efd..b083fe9b 100644 --- a/man/nodekit_stop.md +++ b/man/nodekit_stop.md @@ -30,4 +30,3 @@ nodekit stop [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_uninstall.md b/man/nodekit_uninstall.md index 89134aa7..b4e11b12 100644 --- a/man/nodekit_uninstall.md +++ b/man/nodekit_uninstall.md @@ -30,4 +30,3 @@ nodekit uninstall [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/man/nodekit_upgrade.md b/man/nodekit_upgrade.md index 1de31a52..b5848904 100644 --- a/man/nodekit_upgrade.md +++ b/man/nodekit_upgrade.md @@ -29,4 +29,3 @@ nodekit upgrade [flags] * [nodekit](/README.md) - Manage Algorand nodes from the command line -###### Auto generated by spf13/cobra on 7-Jan-2025 diff --git a/scripts/documentation.go b/scripts/documentation.go index 2b4121df..b8b72378 100644 --- a/scripts/documentation.go +++ b/scripts/documentation.go @@ -10,9 +10,24 @@ import ( "os" "path" "path/filepath" + "sort" "strings" ) +const ( + + // DEVPORTAL_PATH is the root directory path for storing developer portal-related documentation and assets. + DEVPORTAL_PATH = "./.devportal/" + + // MANPAGE_URL defines the base URL path where the manual pages for the CLI commands are hosted. + MANPAGE_URL = "/man/" +) + +// copyFile copies the content of a source file to a destination file; optionally removes the source file if move is true. +// src is the path to the source file. +// dst is the path to the destination file. +// move is a boolean indicating whether the source file should be removed after copying. +// Returns an error if any file operation fails. func copyFile(src, dst string, move bool) error { // Open the source file sourceFile, err := os.Open(src) @@ -45,6 +60,11 @@ func copyFile(src, dst string, move bool) error { return os.Remove(src) } + +// appendString appends the provided content to the file at the given filePath or creates the file if it doesn't exist. +// filePath is the path to the file to append to or create. +// content is the string data to be appended to the file. +// Returns an error if the file cannot be opened, written to, or closed properly. func appendString(filePath, content string) error { // Open the file in append mode, create it if it doesn't exist file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) @@ -62,18 +82,25 @@ func appendString(filePath, content string) error { return nil } +// generateMarkdown generates Markdown documentation for commands in the CLI tool and saves it in the specified directory. +// It uses custom functions for file preprocessing and link handling to customize the output. +// Returns an error if the documentation generation fails. func generateMarkdown() error { + // No need to prefix the file filePrepender := func(filename string) string { return "" } + // Ensure the links are valid for the README linkHandler := func(name string) string { base := strings.TrimSuffix(name, path.Ext(name)) if base == cmd.Name { return "/README.md" } - return "/man/" + strings.ToLower(base) + ".md" + return MANPAGE_URL + strings.ToLower(base) + ".md" } - return doc.GenMarkdownTreeCustom(cmd.RootCmd, "./man", filePrepender, linkHandler) + cmd.RootCmd.DisableAutoGenTag = true + // Generate the documentation + return doc.GenMarkdownTreeCustom(cmd.RootCmd, fmt.Sprintf(".%s", MANPAGE_URL), filePrepender, linkHandler) } // replaceBetweenStrings replaces everything between startString and endString with replacementText in the content of the file @@ -106,12 +133,19 @@ func replaceBetweenStrings(filePath, startString, endString, replacementText str return nil } + +// updateBanner replaces a section of text in the given file with an image tag for displaying a terminal render banner. func updateBanner(filePath string) error { textBanner := ansi.Strip(style.BANNER) textSplit := strings.Split(textBanner, "\n") return replaceBetweenStrings(filePath, textSplit[1], textSplit[len(textSplit)-2], "\"Terminal") } -func updateBanners(dirPath string, starlight bool) error { + +// updateBanners updates markdown files in the given directory by modifying banner sections +// dirPath specifies the directory containing the files to update. +// delete flag to delete the banner instead of injecting it +// Returns an error if processing fails for any file in the directory. +func updateBanners(dirPath string, delete bool) error { // Open the directory dir, err := os.Open(dirPath) if err != nil { @@ -132,8 +166,8 @@ func updateBanners(dirPath string, starlight bool) error { continue } if strings.HasSuffix(file.Name(), ".md") && strings.HasPrefix(file.Name(), cmd.Name) { - if starlight { - err = updateStarlightHeadings(dirPath + file.Name()) + if delete { + err = deleteBanner(dirPath + file.Name()) if err != nil { return err } @@ -150,41 +184,102 @@ func updateBanners(dirPath string, starlight bool) error { return nil } -func updateStarlightHeadings(filePath string) error { +// deleteBanner removes the banner section from a file by replacing text between specific delimiters with empty content. +// filePath specifies the path to the file where the banner will be removed. +// Returns an error if the operation fails. +func deleteBanner(filePath string) error { textBanner := ansi.Strip(style.BANNER) textSplit := strings.Split(textBanner, "\n") return replaceBetweenStrings(filePath, "## nodekit", textSplit[len(textSplit)-2], "## Synopsis") } +// getAllBlocksFromDir reads files from a directory, extracts content between specified strings, and returns as a map. +// dirPath specifies the directory path containing the files. +// startString and endString mark the boundaries of the content to extract from each file. +// Returns a map of file names (modified without `.md`) to their extracted content, or an error if any operation fails. +func getAllBlocksFromDir(dirPath, startString, endString string) (map[string]string, error) { + // Results map to store file names and their extracted content + results := make(map[string]string) + + // Open the directory + dir, err := os.Open(dirPath) + if err != nil { + return nil, err + } + defer dir.Close() + + // Read directory entries + files, err := dir.Readdir(-1) // `-1` reads all entries in the directory + if err != nil { + return nil, err + } + + // Iterate over files in the directory + for _, file := range files { + // Skip directories and files without underscores in their names + if file.IsDir() || !strings.Contains(file.Name(), "_") { + continue + } + + // Build the full file path + filePath := filepath.Join(dirPath, file.Name()) + + // Read the file content + content, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + + // Convert content to string + text := string(content) + + // Find the start and end indices of the desired content + startIndex := strings.Index(text, startString) + endIndex := strings.Index(text, endString) + if startIndex == -1 || endIndex == -1 || startIndex >= endIndex { + return nil, fmt.Errorf("could not find valid boundaries in file '%s' between '%s' and '%s'", file.Name(), startString, endString) + } + + // Extract the content between the start and end strings + extractedContent := text[startIndex+len(startString) : endIndex] + results[strings.Replace(file.Name(), ".md", "", -1)] = extractedContent + } + + return results, nil +} + const fmTemplate = `--- title: "%s" -slug: "%s" --- ` +// generateStarlightMarkdown generates Markdown documentation for CLI commands in the developer portal format. +// It uses custom file preprocessing and link handling to format filenames and links appropriately. +// Outputs files to the DEVPORTAL_PATH directory and returns an error if generation fails. func generateStarlightMarkdown() error { filePrepender := func(filename string) string { name := filepath.Base(filename) base := strings.TrimSuffix(name, path.Ext(name)) - return fmt.Sprintf(fmTemplate, strings.Replace(base, "_", " ", -1), fmt.Sprintf("reference/%s", strings.Replace(base, "_", "/", -1))) + return fmt.Sprintf(fmTemplate, strings.Replace(base, "_", " ", -1)) } linkHandler := func(name string) string { base := strings.TrimSuffix(name, path.Ext(name)) return "/reference/" + strings.Replace(base, "_", "/", -1) } - return doc.GenMarkdownTreeCustom(cmd.RootCmd, "./docs/src/content/docs/reference", filePrepender, linkHandler) + cmd.RootCmd.DisableAutoGenTag = false + return doc.GenMarkdownTreeCustom(cmd.RootCmd, DEVPORTAL_PATH, filePrepender, linkHandler) } func main() { + // Generate Man pages for main repo err := generateMarkdown() if err != nil { panic(err) } - - rootCmdDocPath := fmt.Sprintf("./man/%s.md", cmd.Name) - - err = updateBanners("./man/", false) + // Add the banners to the markdown + rootCmdDocPath := fmt.Sprintf(".%s/%s.md", MANPAGE_URL, cmd.Name) + err = updateBanners(fmt.Sprintf(".%s", MANPAGE_URL), false) if err != nil { panic(err) } @@ -198,15 +293,65 @@ func main() { if err != nil { panic(err) } + // Move the root command to README err = copyFile(fmt.Sprintf("./man/%s.md", cmd.Name), "./README.md", true) if err != nil { panic(err) } + // Generate Devportal Documentation err = generateStarlightMarkdown() if err != nil { panic(err) } + // Delete the banners from the markdown + err = updateBanners(DEVPORTAL_PATH, true) + // Fetch all of the blocks from the directory, assumes subcommands have underscores + blocks, err := getAllBlocksFromDir(DEVPORTAL_PATH, "## Synopsis", "### SEE ALSO") - err = updateBanners("./docs/src/content/docs/reference/", true) + // Sort the keys + keys := make([]string, 0, len(blocks)) + for k := range blocks { + keys = append(keys, k) + } + sort.Strings(keys) + + // Create the new merged documentation + var mergedList string + mergedList += "# Commands \n" + for _, k := range keys { + // Handle the titles + fileNameSplit := strings.Split(k, "_") + if len(fileNameSplit) == 3 { + mergedList += fmt.Sprintf("## %s %s", fileNameSplit[1], fileNameSplit[2]) + } else { + mergedList += fmt.Sprintf("## %s", fileNameSplit[1]) + } + mergedList += strings.Replace(blocks[k], "### Options", "#### Options", -1) + } + // Add back the auto tag + mergedList += "###### Auto" + + // Replace the TOC with the full list of commands + err = replaceBetweenStrings(fmt.Sprintf("%s/nodekit.md", DEVPORTAL_PATH), "### SEE ALSO", "###### Auto", mergedList) + if err != nil { + panic(err) + } + + // Delete all files with underscores in their names in the reference directory + referenceDir := DEVPORTAL_PATH + dirEntries, err := os.ReadDir(referenceDir) + if err != nil { + panic(err) + } + for _, entry := range dirEntries { + if !entry.IsDir() && strings.Contains(entry.Name(), "_") { + filePath := filepath.Join(referenceDir, entry.Name()) + err := os.Remove(filePath) + if err != nil { + panic(fmt.Errorf("failed to delete file '%s': %w", entry.Name(), err)) + } + fmt.Printf("Deleted file: %s\n", filePath) + } + } }