devflow

package module
v0.0.61 Latest Latest
Warning

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

Go to latest
Published: Jan 20, 2026 License: MIT Imports: 20 Imported by: 0

README

DevFlow

Project Badges

Complete Go development automation: project init, testing, versioning, updates, and backups. Single-line output optimized for AI agents and terminals.

Commands

  • gonew - Initialize new Go projects
  • gotest - Run tests, vet, race detection, coverage and badges
  • push - Git add, commit, tag, and push
  • gopush - Complete workflow: test + push + update dependents
  • devbackup - Configure and execute automated backups
  • badges - Generate SVG badges for README (test status, coverage, etc.)

Configuration

  • GitHub Auth - Configure GitHub authentication (OAuth, tokens, multi-account)

Installation

go install github.com/tinywasm/devflow/cmd/gonew@latest
go install github.com/tinywasm/devflow/cmd/gotest@latest
go install github.com/tinywasm/devflow/cmd/push@latest
go install github.com/tinywasm/devflow/cmd/gopush@latest
go install github.com/tinywasm/devflow/cmd/devbackup@latest
go install github.com/tinywasm/devflow/cmd/badges@latest

Quick Start

# Test your project
gotest

# Create new project with specific owner
gonew myapp "My application" -owner=cdvelop

# Push changes 
push "fix: bug correction"

# Test + push + update dependents + backup
gopush "feat: new feature"

# Generate badges
badges

Library Usage

import "github.com/tinywasm/devflow"

// Git workflow
git := devflow.NewGit()
summary, _ := git.Push("commit message", "v1.0.0")

// Go project workflow
goHandler := devflow.NewGo(git)
summary, _ := goHandler.Push("commit message", "", false, false, false, false, "..")

// Optional: Enable logging for debugging
git.SetLog(log.Println)
goHandler.SetLog(log.Println)

Features

  • Zero config - Auto-detects tests, project structure, WASM environments
  • Minimal output - Single-line summaries for terminals and LLMs
  • Smart versioning - Auto-increments tags, skips duplicates
  • Multi-account - Switch GitHub orgs easily (cdvelop, veltylabs, tinywasm)
  • Dependency updates - Auto-updates dependent modules in workspace
  • Full testing - Combines vet, tests, race detection, coverage

License

MIT

Documentation

Index

Constants

View Source
const (
	ColorRed    = "\033[0;31m"
	ColorGreen  = "\033[0;32m"
	ColorYellow = "\033[0;33m"
	ColorCyan   = "\033[0;36m"
	ColorNone   = "\033[0m"
)
View Source
const DevFlowRepository = "github.com/tinywasm/devflow"
View Source
const DevflowOAuthClientID = "Ov23lijHU2vxBCpShn1Q"

DevflowOAuthClientID is the OAuth App Client ID for devflow.

IMPORTANT: This Client ID is intentionally hardcoded and is NOT a secret. OAuth Client IDs are public identifiers (like a username, not a password). The Client Secret is NEVER included in the code - Device Flow doesn't need it. This is the standard approach used by CLI tools like gh, goreleaser, hub, etc.

The OAuth App is registered under a personal GitHub account (not organization). Manage the app at: https://github.com/settings/developers -> OAuth Apps -> devflow

Variables

View Source
var ExecCommand = exec.Command

ExecCommand is a variable to allow mocking in tests

Functions

func CompareVersions added in v0.0.49

func CompareVersions(v1, v2 string) int

CompareVersions compares two semantic version strings (e.g., "v1.2.3"). It returns -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2. It handles "v" prefix gracefully.

func FormatCommitMessage added in v0.0.34

func FormatCommitMessage(message string) string

FormatCommitMessage ensures the message is trimmed.

func GenerateGitignore added in v0.0.13

func GenerateGitignore(targetDir string) error

GenerateGitignore generates .gitignore for Go

func GenerateHandlerFile added in v0.0.13

func GenerateHandlerFile(repoName, targetDir string) error

GenerateHandlerFile generates the main handler file

func GenerateLicense added in v0.0.13

func GenerateLicense(ownerName, targetDir string) error

GenerateLicense generates LICENSE (MIT)

func GenerateREADME added in v0.0.13

func GenerateREADME(repoName, description, targetDir string) error

GenerateREADME generates README.md

func PrintError added in v0.0.13

func PrintError(msg string)

PrintError prints an error message in red.

func PrintInfo added in v0.0.13

func PrintInfo(msg string)

PrintInfo prints an informational message in cyan.

func PrintSuccess added in v0.0.13

func PrintSuccess(msg string)

PrintSuccess prints a success message in green.

func PrintWarning added in v0.0.13

func PrintWarning(msg string)

PrintWarning prints a warning message in yellow.

func RunCommand

func RunCommand(name string, args ...string) (string, error)

RunCommand executes a shell command It returns the output (trimmed) and an error if the command fails

func RunCommandInDir added in v0.0.51

func RunCommandInDir(dir, name string, args ...string) (string, error)

RunCommandInDir executes a command in a specific directory

func RunCommandSilent

func RunCommandSilent(name string, args ...string) (string, error)

RunCommandSilent executes a command (alias for RunCommand now, as RunCommand is also silent on success) kept for backward compatibility if needed, or we can remove it. The previous implementation was identical except for logging.

func RunCommandWithRetryInDir added in v0.0.51

func RunCommandWithRetryInDir(dir, name string, args []string, maxRetries int, delay time.Duration) (string, error)

RunCommandWithRetryInDir executes a command in a specific directory with retries

func RunShellCommand added in v0.0.10

func RunShellCommand(command string) (string, error)

RunShellCommand executes a shell command in a cross-platform way On Windows: uses cmd.exe /C On Unix (Linux/macOS): uses sh -c

func RunShellCommandAsync added in v0.0.13

func RunShellCommandAsync(command string) error

RunShellCommandAsync starts a shell command asynchronously (non-blocking) Returns immediately after starting, does not wait for completion

func ValidateCommitMessage added in v0.0.34

func ValidateCommitMessage(message string) error

ValidateCommitMessage ensures that a commit message is provided and is valid. It trims whitespace and returns an error if the message is empty.

func ValidateDescription added in v0.0.13

func ValidateDescription(desc string) error

ValidateDescription validates the repository description

func ValidateRepoName added in v0.0.13

func ValidateRepoName(name string) error

ValidateRepoName validates the repository name Only alphanumeric, dash, and underscore allowed

func ValidateShellSafeMessage added in v0.0.34

func ValidateShellSafeMessage(message string) string

ValidateShellSafeMessage provides a warning if the message contains characters that might need escaping in certain shells (like backticks, dollar signs, or single quotes) if it were to be used in a shell script, even though exec.Command is safe.

Types

type Badge added in v0.0.13

type Badge struct {
	Label string // The text displayed on the left side of the badge.
	Value string // The text displayed on the right side of the badge.
	Color string // The background color for the value part of the badge (e.g., "#4c1" or "green").
}

Badge represents a single badge with a label, value, and color. This is the primary struct used to define a badge's appearance and content.

For example, to create a "Go version" badge, you might use:

b := Badge{
  Label: "Go",
  Value: "1.18",
  Color: "#007d9c",
}

type Badges added in v0.0.13

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

Badges is responsible for creating and managing a collection of badges. It handles parsing input arguments, generating the SVG image, and preparing the necessary markdown to embed the badges in a file.

func NewBadges added in v0.0.13

func NewBadges(args ...string) *Badges

NewBadges creates and initializes a new Badges handler.

func (*Badges) BadgeMarkdown added in v0.0.13

func (h *Badges) BadgeMarkdown() string

BadgeMarkdown generates the markdown snippet for embedding the badge image.

func (*Badges) BuildBadges added in v0.0.13

func (h *Badges) BuildBadges() ([]string, error)

BuildBadges generates the SVG image, writes it to the specified output file, and returns a slice of strings intended for updating a markdown file.

func (*Badges) Err added in v0.0.13

func (h *Badges) Err() error

Err returns any error that occurred during the initialization or processing

func (*Badges) GenerateSVG added in v0.0.13

func (h *Badges) GenerateSVG() ([]byte, int, error)

GenerateSVG creates an SVG image from the configured badges.

It returns the SVG content as a byte slice, the number of badges included, and an error if the generation fails. This method is typically called by BuildBadges, but it can be used directly if you only need the SVG data.

Example of a generated SVG for two badges ("License:MIT:blue" and "Go:1.22:blue"):

<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" width="168" height="20" viewBox="0 0 168 20">

<!-- Badge: License -->
<g transform="translate(0, 0)">
  <rect x="0" y="0" width="58" height="20" fill="#6c757d"/>
  <rect x="58" y="0" width="46" height="20" fill="blue"/>
  <text x="29" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">License</text>
  <text x="81" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">MIT</text>
</g>
<!-- Badge: Go -->
<g transform="translate(109, 0)">
  <rect x="0" y="0" width="34" height="20" fill="#6c757d"/>
  <rect x="34" y="0" width="25" height="20" fill="blue"/>
  <text x="17" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">Go</text>
  <text x="46" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">1.22</text>
</g>

</svg>

func (*Badges) GoHandler added in v0.0.13

func (h *Badges) GoHandler() *Go

GoHandler returns the internal Go handler

func (*Badges) OutputFile added in v0.0.13

func (h *Badges) OutputFile() string

OutputFile returns the configured path for the output SVG file.

func (*Badges) ReadmeFile added in v0.0.13

func (h *Badges) ReadmeFile() string

ReadmeFile returns the configured path for the markdown file to be updated.

func (*Badges) SetLog added in v0.0.23

func (h *Badges) SetLog(fn func(...any))

SetLog sets the logger function

type Bashrc added in v0.0.10

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

Bashrc handles updates to .bashrc file using markers

func NewBashrc added in v0.0.10

func NewBashrc() *Bashrc

NewBashrc creates a new Bashrc handler for ~/.bashrc

func (*Bashrc) Get added in v0.0.10

func (b *Bashrc) Get(key string) (string, error)

Get reads a variable value from .bashrc file

func (*Bashrc) Set added in v0.0.10

func (b *Bashrc) Set(key, value string) error

Set updates or creates a variable in .bashrc If value is empty, removes the variable

type ConsoleFilter

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

func NewConsoleFilter

func NewConsoleFilter(output func(string)) *ConsoleFilter

func (*ConsoleFilter) Add

func (cf *ConsoleFilter) Add(input string)

func (*ConsoleFilter) Flush

func (cf *ConsoleFilter) Flush()

type DevBackup added in v0.0.10

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

DevBackup handles backup operations

func NewDevBackup added in v0.0.10

func NewDevBackup() *DevBackup

NewDevBackup creates a new DevBackup instance

func (*DevBackup) GetCommand added in v0.0.10

func (d *DevBackup) GetCommand() (string, error)

GetCommand retrieves the backup command First checks environment variable, then falls back to .bashrc

func (*DevBackup) Run added in v0.0.10

func (d *DevBackup) Run() (string, error)

Run executes the backup command asynchronously Returns a message for the summary or empty string if not configured

func (*DevBackup) SetCommand added in v0.0.10

func (d *DevBackup) SetCommand(command string) error

SetCommand sets the backup command in .bashrc and current environment

func (*DevBackup) SetLog added in v0.0.23

func (d *DevBackup) SetLog(fn func(...any))

SetLog sets the logger function

type Future added in v0.0.36

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

Future holds the async result of any initialization. It uses any (interface{}) for flexibility without generic syntax.

func NewFuture added in v0.0.36

func NewFuture(initFn func() (any, error)) *Future

NewFuture starts async initialization with the given function.

func NewResolvedFuture added in v0.0.41

func NewResolvedFuture(value any) *Future

NewResolvedFuture creates a Future that is already resolved with the given value. Useful for tests or when the value is already available synchronously.

func (*Future) Get added in v0.0.36

func (f *Future) Get() (any, error)

Get blocks until initialization completes and returns the result.

func (*Future) Ready added in v0.0.36

func (f *Future) Ready() <-chan bool

Ready returns a channel that signals completion.

type Git

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

Git handler for Git operations

func NewGit

func NewGit() (*Git, error)

NewGit creates a new Git handler and verifies git is available

func (*Git) GenerateNextTag

func (g *Git) GenerateNextTag() (string, error)

GenerateNextTag calculates the next semantic version

func (*Git) GetConfigUserEmail added in v0.0.13

func (g *Git) GetConfigUserEmail() (string, error)

GetConfigUserEmail gets the git user.email

func (*Git) GetConfigUserName added in v0.0.13

func (g *Git) GetConfigUserName() (string, error)

GetConfigUserName gets the git user.name

func (*Git) GetLatestTag

func (g *Git) GetLatestTag() (string, error)

GetLatestTag gets the latest tag

func (*Git) GitIgnoreAdd added in v0.0.32

func (g *Git) GitIgnoreAdd(entry string) error

GitIgnoreAdd adds entry to .gitignore if shouldWrite allows and entry not present. Creates .gitignore if it doesn't exist.

func (*Git) InitRepo added in v0.0.13

func (g *Git) InitRepo(dir string) error

InitRepo initializes a new git repository

func (*Git) Push

func (g *Git) Push(message, tag string) (string, error)

Push executes the complete push workflow (add, commit, tag, push) Returns a summary of operations and error if any.

func (*Git) SetLog

func (g *Git) SetLog(fn func(...any))

SetLog sets the logger function

func (*Git) SetRootDir added in v0.0.32

func (g *Git) SetRootDir(path string)

SetRootDir sets the root directory for git operations

func (*Git) SetShouldWrite added in v0.0.32

func (g *Git) SetShouldWrite(f func() bool)

SetShouldWrite sets a function that determines if Git write operations (like updating .gitignore) should be allowed.

func (*Git) SetUserConfig added in v0.0.13

func (g *Git) SetUserConfig(name, email string) error

SetUserConfig sets git user name and email

type GitHub added in v0.0.13

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

GitHub handler for GitHub operations

func NewGitHub added in v0.0.13

func NewGitHub(logFn func(...any)) (*GitHub, error)

NewGitHub creates handler and verifies gh CLI availability. logFn is used to display authentication messages during Device Flow. If not authenticated, it initiates OAuth Device Flow automatically.

func (*GitHub) CreateRepo added in v0.0.13

func (gh *GitHub) CreateRepo(owner, name, description, visibility string) error

CreateRepo creates a new empty repository on GitHub If owner is provided, creates repo under that organization

func (*GitHub) DeleteRepo added in v0.0.39

func (gh *GitHub) DeleteRepo(owner, name string) error

DeleteRepo deletes a repository on GitHub. WARNING: This permanently deletes the repository and cannot be undone. Use with caution, primarily for test cleanup.

func (*GitHub) GetCurrentUser added in v0.0.13

func (gh *GitHub) GetCurrentUser() (string, error)

GetCurrentUser gets the current authenticated user

func (*GitHub) GetHelpfulErrorMessage added in v0.0.13

func (gh *GitHub) GetHelpfulErrorMessage(err error) string

GetHelpfulErrorMessage returns a helpful message for common errors

func (*GitHub) IsNetworkError added in v0.0.13

func (gh *GitHub) IsNetworkError(err error) bool

IsNetworkError checks if an error is likely a network error

func (*GitHub) RepoExists added in v0.0.13

func (gh *GitHub) RepoExists(owner, name string) (bool, error)

RepoExists checks if a repository exists

func (*GitHub) SetLog added in v0.0.13

func (gh *GitHub) SetLog(fn func(...any))

SetLog sets the logger function

type GitHubAuth added in v0.0.23

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

GitHubAuth handles GitHub authentication and token management

func NewGitHubAuth added in v0.0.23

func NewGitHubAuth() *GitHubAuth

NewGitHubAuth creates a new GitHub authentication handler

func (*GitHubAuth) DeviceFlowAuth added in v0.0.23

func (a *GitHubAuth) DeviceFlowAuth(kr *Keyring) (string, error)

DeviceFlowAuth initiates GitHub OAuth Device Flow and returns an access token

func (*GitHubAuth) EnsureGitHubAuth added in v0.0.23

func (a *GitHubAuth) EnsureGitHubAuth() error

EnsureGitHubAuth checks if GitHub is authenticated via keyring, and if not, initiates Device Flow

func (*GitHubAuth) SetLog added in v0.0.23

func (a *GitHubAuth) SetLog(fn func(...any))

SetLog sets the logger function

type Go

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

Go handler for Go operations

func NewGo

func NewGo(gitHandler *Git) (*Go, error)

NewGo creates a new Go handler and verifies Go installation

func (*Go) DetectGoExecutable added in v0.0.13

func (g *Go) DetectGoExecutable() (string, error)

DetectGoExecutable returns the path to the go executable

func (*Go) GetCurrentVersion added in v0.0.49

func (g *Go) GetCurrentVersion(moduleDir, dependencyPath string) (string, error)

GetCurrentVersion returns the current version of a dependency in a module

func (*Go) GoVersion added in v0.0.13

func (g *Go) GoVersion() (string, error)

GoVersion reads the Go version from the go.mod file in the current directory. It returns the version string (e.g., "1.18") or an empty string if not found.

func (*Go) ModExistsInCurrentOrParent added in v0.0.33

func (g *Go) ModExistsInCurrentOrParent() bool

ModExistsInCurrentOrParent checks if go.mod exists in the rootDir or one directory up.

func (*Go) ModInit added in v0.0.13

func (g *Go) ModInit(modulePath, targetDir string) error

ModInit initializes a new go module

func (*Go) Push

func (g *Go) Push(message, tag string, skipTests, skipRace, skipDependents, skipBackup bool, searchPath string) (string, error)

Push executes the complete workflow for Go projects Parameters:

message: Commit message
tag: Optional tag
skipTests: If true, skips tests
skipRace: If true, skips race tests
skipDependents: If true, skips updating dependent modules
skipBackup: If true, skips backup
searchPath: Path to search for dependent modules (default: "..")

func (*Go) SetLog

func (g *Go) SetLog(fn func(...any))

SetLog sets the logger function

func (*Go) SetRootDir added in v0.0.33

func (g *Go) SetRootDir(path string)

SetRootDir sets the root directory for Go operations

func (*Go) Test

func (g *Go) Test() (string, error)

Test executes the test suite for the project

func (*Go) UpdateDependentModule added in v0.0.45

func (g *Go) UpdateDependentModule(depDir, modulePath, version string) (string, error)

UpdateDependentModule updates a dependent module and optionally pushes it This is called for each module that depends on the one we just published UpdateDependentModule updates a specific dependent module It modifies go.mod to require the new version and runs go mod tidy

type GoModFile added in v0.0.45

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

GoModFile represents a parsed go.mod file for efficient operations

func NewGoModFile added in v0.0.45

func NewGoModFile(gomodPath string) (*GoModFile, error)

NewGoModFile reads and parses a go.mod file

func (*GoModFile) HasOtherReplaces added in v0.0.45

func (m *GoModFile) HasOtherReplaces(exceptModule string) bool

HasOtherReplaces returns true if there are replace directives other than the specified module

func (*GoModFile) RemoveReplace added in v0.0.45

func (m *GoModFile) RemoveReplace(modulePath string) bool

RemoveReplace removes a replace directive for the given module Returns true if a replace was found and removed

func (*GoModFile) RunTidy added in v0.0.45

func (m *GoModFile) RunTidy() error

RunTidy executes 'go mod tidy' in the directory of the go.mod file

func (*GoModFile) Save added in v0.0.45

func (m *GoModFile) Save() error

Save writes changes back to the file if modified

type GoNew added in v0.0.13

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

GoNew orchestrator

func NewGoNew added in v0.0.13

func NewGoNew(git *Git, github *Future, goHandler *Go) *GoNew

NewGoNew creates orchestrator (all handlers must be initialized)

func (*GoNew) AddRemote added in v0.0.13

func (gn *GoNew) AddRemote(projectPath, visibility, owner string) (string, error)

AddRemote adds GitHub remote to existing local project

func (*GoNew) Create added in v0.0.13

func (gn *GoNew) Create(opts NewProjectOptions) (string, error)

Create executes full workflow with remote (or local-only fallback)

func (*GoNew) GetSteps added in v0.0.36

func (gn *GoNew) GetSteps() []*wizard.Step

GetSteps returns the sequence of steps to create a new Go project

func (*GoNew) SetLog added in v0.0.13

func (gn *GoNew) SetLog(fn func(...any))

SetLog sets the logger function

type Keyring added in v0.0.23

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

Keyring provides secure credential storage using the system keyring

func NewKeyring added in v0.0.23

func NewKeyring() (*Keyring, error)

NewKeyring creates a keyring handler and ensures dependencies are installed

func (*Keyring) Delete added in v0.0.23

func (k *Keyring) Delete(key string) error

Delete removes a secret from the keyring

func (*Keyring) Get added in v0.0.23

func (k *Keyring) Get(key string) (string, error)

Get retrieves a secret from the keyring

func (*Keyring) Set added in v0.0.23

func (k *Keyring) Set(key, value string) error

Set stores a secret in the keyring

func (*Keyring) SetLog added in v0.0.23

func (k *Keyring) SetLog(fn func(...any))

SetLog sets the logging function for the keyring handler

type MarkDown added in v0.0.13

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

func NewMarkDown added in v0.0.13

func NewMarkDown(rootDir, destination string, writerFile func(name string, data []byte) error) *MarkDown

NewMarkDown creates a new MarkDown instance with the root directory. Destination (output directory) and input must be set via methods.

func (*MarkDown) Extract added in v0.0.13

func (m *MarkDown) Extract(outputFile string) error

Extract extracts code blocks from the configured input and writes to outputFile The output file extension determines which code type to extract (.go, .js, .css)

func (*MarkDown) InputByte added in v0.0.13

func (m *MarkDown) InputByte(content []byte) *MarkDown

InputByte sets the input as a byte slice (markdown content)

func (*MarkDown) InputEmbed added in v0.0.13

func (m *MarkDown) InputEmbed(path string, readerFile func(name string) ([]byte, error)) *MarkDown

InputEmbed sets the input as any ReaderFile implementation and a relative path inside it

func (*MarkDown) InputPath added in v0.0.13

func (m *MarkDown) InputPath(pathFile string, readerFile func(name string) ([]byte, error)) *MarkDown

InputPath sets the input as a file path (relative to rootDir)

func (*MarkDown) SetLog added in v0.0.23

func (m *MarkDown) SetLog(fn func(...any))

SetLog sets a custom logger function

func (*MarkDown) UpdateSection added in v0.0.13

func (m *MarkDown) UpdateSection(sectionID, content string, afterLine ...string) error

UpdateSection updates or creates a section in the input file based on identifier. sectionID: The identifier for the section (e.g., "BADGES"). content: The new content to insert. afterLine: Optional. Implementation tries to insert after this line number if section doesn't exist.

type NewProjectOptions added in v0.0.13

type NewProjectOptions struct {
	Name        string // Required, must be valid (alphanumeric, dash, underscore only)
	Description string // Required, max 350 chars
	Owner       string // GitHub owner/organization (default: detected from gh or git config)
	Visibility  string // "public" or "private" (default: "public")
	Directory   string // Supports ~/path, ./path, /abs/path (default: ./{Name})
	LocalOnly   bool   // If true, skip remote creation
	License     string // Default "MIT"
}

NewProjectOptions options for creating a new project

Directories

Path Synopsis
cmd
badges command
devbackup command
gonew command
gopush command
gotest command
push command

Jump to

Keyboard shortcuts

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