aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/git-leaderboard/main.go128
-rw-r--r--cmd/goget/main.go165
-rw-r--r--cmd/pviz/main.go266
-rw-r--r--cmd/seq-stat/main.go115
4 files changed, 674 insertions, 0 deletions
diff --git a/cmd/git-leaderboard/main.go b/cmd/git-leaderboard/main.go
new file mode 100644
index 0000000..7c846a5
--- /dev/null
+++ b/cmd/git-leaderboard/main.go
@@ -0,0 +1,128 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "sort"
+ "strings"
+)
+
+type Author struct {
+ Name string
+ Count int
+}
+
+func main() {
+ lineFlag := flag.Bool("line", false, "Count lines changed instead of commits")
+ flag.Parse()
+
+ if !isGitRepository() {
+ fmt.Println("Error: Not in a git repository")
+ os.Exit(1)
+ }
+
+ authors, err := getAuthors(*lineFlag)
+ if err != nil {
+ fmt.Printf("Error getting authors: %v\n", err)
+ os.Exit(1)
+ }
+
+ if len(authors) == 0 {
+ fmt.Println("No authors found. The repository might be empty or have no commits.")
+ os.Exit(0)
+ }
+
+ sort.Slice(authors, func(i, j int) bool {
+ return authors[i].Count > authors[j].Count
+ })
+
+ maxCount := authors[0].Count
+
+ fmt.Printf("%-30s %-10s %s\n", "Author", "Count", "Contribution")
+ fmt.Println(strings.Repeat("-", 80))
+
+ for _, author := range authors {
+ bar := generateBar(author.Count, maxCount)
+ fmt.Printf("%-30s %-10d %s\n", author.Name, author.Count, bar)
+ }
+}
+
+func isGitRepository() bool {
+ cmd := exec.Command("git", "rev-parse", "--is-inside-work-tree")
+ err := cmd.Run()
+ return err == nil
+}
+
+func getAuthors(countLines bool) ([]Author, error) {
+ var cmd *exec.Cmd
+
+ if countLines {
+ cmd = exec.Command("git", "log", "--pretty=format:%aN", "--numstat")
+ } else {
+ cmd = exec.Command("git", "shortlog", "-sn", "--no-merges", "HEAD")
+ }
+
+ output, err := cmd.Output()
+ if err != nil {
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ return nil, fmt.Errorf("git command failed: %s. Error output: %s", err, exitErr.Stderr)
+ }
+ return nil, fmt.Errorf("error executing git command: %v", err)
+ }
+
+ lines := strings.Split(string(output), "\n")
+ authors := make(map[string]int)
+
+ if countLines {
+ currentAuthor := ""
+ for _, line := range lines {
+ if line == "" {
+ currentAuthor = ""
+ continue
+ }
+ if currentAuthor == "" {
+ currentAuthor = line
+ continue
+ } else {
+ fields := strings.Fields(line)
+ if len(fields) >= 2 {
+ added, _ := parseInt(fields[0])
+ deleted, _ := parseInt(fields[1])
+ authors[currentAuthor] += added + deleted
+ }
+ }
+ }
+ } else {
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ if len(fields) >= 2 {
+ count, _ := parseInt(fields[0])
+ name := strings.Join(fields[1:], " ")
+ authors[name] = count
+ }
+ }
+ }
+
+ var result []Author
+ for name, count := range authors {
+ result = append(result, Author{Name: name, Count: count})
+ }
+ return result, nil
+}
+
+func parseInt(s string) (int, error) {
+ if s == "-" {
+ return 0, nil
+ }
+ var result int
+ _, err := fmt.Sscanf(s, "%d", &result)
+ return result, err
+}
+
+func generateBar(count, maxCount int) string {
+ maxWidth := 38
+ width := int(float64(count) / float64(maxCount) * float64(maxWidth))
+ return strings.Repeat("▆", width)
+}
diff --git a/cmd/goget/main.go b/cmd/goget/main.go
new file mode 100644
index 0000000..3f17448
--- /dev/null
+++ b/cmd/goget/main.go
@@ -0,0 +1,165 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "os/signal"
+ "slices"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+ "golang.org/x/mod/semver"
+)
+
+const (
+ forgeDomain = "code.fcuny.net"
+ forgeUser = "fcuny"
+ goPkgDomain = "go.fcuny.net"
+)
+
+var (
+ goGetReqs = promauto.NewCounterVec(prometheus.CounterOpts{
+ Name: "goget_requests_total",
+ Help: "go get requests processed, by repository name.",
+ }, []string{"name"})
+
+ modules = []string{"x"}
+)
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
+ defer cancel()
+
+ metricsMux := http.NewServeMux()
+ metricsMux.Handle("/metrics", promhttp.Handler())
+ metricsServer := &http.Server{
+ Addr: ":9091", Handler: metricsMux,
+ ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second,
+ }
+
+ go func() {
+ log.Println("Starting metrics server on :9091")
+ if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Printf("Metrics server error: %v", err)
+ }
+ }()
+
+ s := &http.Server{
+ Addr: ":8070",
+ Handler: handler(),
+ ReadTimeout: 10 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ IdleTimeout: 1 * time.Minute,
+ }
+
+ go func() {
+ log.Println("Starting main server on :8080")
+ if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Printf("Main server error: %v", err)
+ }
+ }()
+
+ <-ctx.Done()
+ log.Println("Shutdown signal received, starting graceful shutdown...")
+
+ shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer shutdownCancel()
+
+ var shutdownErr error
+
+ log.Println("Shutting down main server...")
+ if err := s.Shutdown(shutdownCtx); err != nil {
+ log.Printf("Main server shutdown error: %v", err)
+ shutdownErr = err
+ }
+
+ log.Println("Shutting down metrics server...")
+ if err := metricsServer.Shutdown(shutdownCtx); err != nil {
+ log.Printf("Metrics server shutdown error: %v", err)
+ if shutdownErr == nil {
+ shutdownErr = err
+ }
+ }
+
+ if shutdownErr != nil {
+ log.Printf("Shutdown completed with errors")
+ os.Exit(1)
+ }
+
+ log.Println("Shutdown completed successfully")
+}
+
+func handler() http.Handler {
+ mux := http.NewServeMux()
+
+ mux.Handle(goPkgDomain+"/{name}", siteHandler(modules))
+ mux.Handle(goPkgDomain+"/{name}/", siteHandler(modules))
+
+ goGetMux := http.NewServeMux()
+ for _, name := range modules {
+ module := goPkgDomain + "/" + name
+ goGetMux.Handle(
+ module+"/",
+ goImportHandler(module, "https://"+forgeDomain+"/"+forgeUser+"/"+name),
+ )
+ }
+
+ return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ status := http.StatusMethodNotAllowed
+ http.Error(rw, http.StatusText(status), status)
+ return
+ }
+
+ if r.URL.Query().Get("go-get") == "1" {
+ goGetMux.ServeHTTP(rw, r)
+ return
+ }
+ mux.ServeHTTP(rw, r)
+ })
+}
+
+func goImportHandler(module, repo string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ goGetReqs.WithLabelValues(module).Inc()
+ w.Header().Set("Content-Type", "text/html; charset=UTF-8")
+ if _, err := fmt.Fprintf(w, `<head><meta name="go-import" content="%s git %s">`, module, repo); err != nil {
+ log.Printf("Error writing response: %v", err)
+ }
+ })
+}
+
+func siteHandler(names []string) http.Handler {
+ return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ name := r.PathValue("name")
+ name, version, hasV := strings.Cut(name, "@")
+ if !hasV {
+ name, _, _ = strings.Cut(name, ".")
+ }
+ if hasV && !semver.IsValid(version) || !slices.Contains(names, name) {
+ http.NotFound(rw, r)
+ return
+ }
+ u := &url.URL{
+ Scheme: "https",
+ Host: forgeDomain,
+ Path: forgeUser + r.URL.Path,
+ }
+ if !hasV {
+ path, symbol, hasSymbol := strings.Cut(r.URL.Path, ".")
+ if hasSymbol {
+ u.Path = forgeUser + "/" + path
+ u.Fragment = symbol
+ }
+ }
+ http.Redirect(rw, r, u.String(), http.StatusFound)
+ })
+}
diff --git a/cmd/pviz/main.go b/cmd/pviz/main.go
new file mode 100644
index 0000000..89bd78d
--- /dev/null
+++ b/cmd/pviz/main.go
@@ -0,0 +1,266 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "text/tabwriter"
+
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
+)
+
+type Period string
+
+const (
+ PeriodDay Period = "day"
+ PeriodWeek Period = "week"
+ PeriodMonth Period = "month"
+ PeriodQuarter Period = "quarter"
+ PeriodYear Period = "year"
+)
+
+var periodMinutes = map[Period]float64{
+ PeriodDay: 24 * 60,
+ PeriodWeek: 7 * 24 * 60,
+ PeriodMonth: 30 * 24 * 60,
+ PeriodQuarter: 90 * 24 * 60,
+ PeriodYear: 365 * 24 * 60,
+}
+
+var periodOrder = []Period{PeriodDay, PeriodWeek, PeriodMonth, PeriodQuarter, PeriodYear}
+
+type TimeUnit struct {
+ Minutes float64
+ Formatted string
+}
+
+type AvailabilityCalculator struct {
+ availability float64
+}
+
+func NewAvailabilityCalculator(availabilityPercentage float64) (*AvailabilityCalculator, error) {
+ if availabilityPercentage < 0 || availabilityPercentage > 100 {
+ return nil, fmt.Errorf("availability must be between 0 and 100")
+ }
+ return &AvailabilityCalculator{
+ availability: availabilityPercentage / 100.0,
+ }, nil
+}
+
+func (ac *AvailabilityCalculator) CalculateDowntime(period Period) TimeUnit {
+ totalMinutes := periodMinutes[period]
+ downtimeMinutes := totalMinutes * (1 - ac.availability)
+ return TimeUnit{
+ Minutes: downtimeMinutes,
+ Formatted: formatDuration(downtimeMinutes),
+ }
+}
+
+func (ac *AvailabilityCalculator) CalculateAllPeriods() map[Period]TimeUnit {
+ result := make(map[Period]TimeUnit)
+ for _, period := range periodOrder {
+ result[period] = ac.CalculateDowntime(period)
+ }
+ return result
+}
+
+func formatDuration(minutes float64) string {
+ if minutes < 1 {
+ return fmt.Sprintf("%.1f seconds", minutes*60)
+ }
+
+ hours := int(minutes / 60)
+ remainingMinutes := minutes - float64(hours*60)
+
+ if hours == 0 {
+ return fmt.Sprintf("%.1f minutes", remainingMinutes)
+ } else if remainingMinutes == 0 {
+ return fmt.Sprintf("%d hours", hours)
+ } else {
+ return fmt.Sprintf("%d hours %.1f minutes", hours, remainingMinutes)
+ }
+}
+
+func printTable(headers []string, rows [][]string) {
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+
+ for i, header := range headers {
+ if i > 0 {
+ _, _ = fmt.Fprint(w, "\t")
+ }
+ _, _ = fmt.Fprint(w, header)
+ }
+ _, _ = fmt.Fprintln(w)
+
+ for _, row := range rows {
+ for i, cell := range row {
+ if i > 0 {
+ _, _ = fmt.Fprint(w, "\t")
+ }
+ _, _ = fmt.Fprint(w, cell)
+ }
+ _, _ = fmt.Fprintln(w)
+ }
+
+ _ = w.Flush()
+}
+
+func analyzeSingle(availability float64, focusPeriod string) error {
+ calc, err := NewAvailabilityCalculator(availability)
+ if err != nil {
+ return err
+ }
+
+ var periods []Period
+ if focusPeriod != "" {
+ periods = []Period{Period(focusPeriod)}
+ } else {
+ periods = periodOrder
+ }
+
+ headers := []string{"Time Period", "Maximum Downtime"}
+ var rows [][]string
+
+ caser := cases.Title(language.English)
+ for _, period := range periods {
+ downtime := calc.CalculateDowntime(period)
+ rows = append(rows, []string{
+ caser.String(string(period)),
+ downtime.Formatted,
+ })
+ }
+
+ printTable(headers, rows)
+ return nil
+}
+
+func compareAvailabilities(availability1, availability2 float64) error {
+ if availability1 == availability2 {
+ return fmt.Errorf("cannot compare identical availability values")
+ }
+
+ calc1, err := NewAvailabilityCalculator(availability1)
+ if err != nil {
+ return err
+ }
+
+ calc2, err := NewAvailabilityCalculator(availability2)
+ if err != nil {
+ return err
+ }
+
+ improvement := ((100 - availability1) - (100 - availability2)) / (100 - availability1) * 100
+ fmt.Printf("Downtime reduction: %.1f%%\n\n", improvement)
+
+ headers := []string{
+ "Time Period",
+ fmt.Sprintf("%.2f%%", availability1),
+ fmt.Sprintf("%.2f%%", availability2),
+ }
+ var rows [][]string
+
+ caser := cases.Title(language.English)
+ for _, period := range periodOrder {
+ downtime1 := calc1.CalculateDowntime(period)
+ downtime2 := calc2.CalculateDowntime(period)
+ rows = append(rows, []string{
+ caser.String(string(period)),
+ downtime1.Formatted,
+ downtime2.Formatted,
+ })
+ }
+
+ printTable(headers, rows)
+ return nil
+}
+
+func printUsage() {
+ fmt.Println("Usage:")
+ fmt.Println(" pviz analyze <availability> - Analyze a single availability target")
+ fmt.Println(" pviz analyze <availability> --focus <period> - Focus on a specific time period")
+ fmt.Println(" pviz compare <availability1> <availability2> - Compare two availability targets")
+ fmt.Println()
+ fmt.Println("Examples:")
+ fmt.Println(" pviz analyze 99.99")
+ fmt.Println(" pviz analyze 99.99 --focus quarter")
+ fmt.Println(" pviz compare 99.9 99.95")
+ fmt.Println()
+ fmt.Println("Valid periods: day, week, month, quarter, year")
+}
+
+func main() {
+ if len(os.Args) < 2 {
+ printUsage()
+ os.Exit(1)
+ }
+
+ command := os.Args[1]
+
+ switch command {
+ case "analyze":
+ if len(os.Args) < 3 {
+ fmt.Fprintf(os.Stderr, "Error: analyze command requires availability argument\n")
+ os.Exit(1)
+ }
+
+ availability, err := strconv.ParseFloat(os.Args[2], 64)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: invalid availability value: %v\n", err)
+ os.Exit(1)
+ }
+
+ var focusPeriod string
+ if len(os.Args) >= 5 && os.Args[3] == "--focus" {
+ focusPeriod = os.Args[4]
+ validPeriod := false
+ for _, p := range periodOrder {
+ if string(p) == focusPeriod {
+ validPeriod = true
+ break
+ }
+ }
+ if !validPeriod {
+ fmt.Fprintf(
+ os.Stderr,
+ "Error: invalid period '%s'. Valid periods: day, week, month, quarter, year\n",
+ focusPeriod,
+ )
+ os.Exit(1)
+ }
+ }
+
+ if err := analyzeSingle(availability, focusPeriod); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ os.Exit(1)
+ }
+
+ case "compare":
+ if len(os.Args) < 4 {
+ fmt.Fprintf(os.Stderr, "Error: compare command requires two availability arguments\n")
+ os.Exit(1)
+ }
+
+ availability1, err := strconv.ParseFloat(os.Args[2], 64)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: invalid first availability value: %v\n", err)
+ os.Exit(1)
+ }
+
+ availability2, err := strconv.ParseFloat(os.Args[3], 64)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: invalid second availability value: %v\n", err)
+ os.Exit(1)
+ }
+
+ if err := compareAvailabilities(availability1, availability2); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ os.Exit(1)
+ }
+
+ default:
+ fmt.Fprintf(os.Stderr, "Error: unknown command '%s'\n", command)
+ printUsage()
+ os.Exit(1)
+ }
+}
diff --git a/cmd/seq-stat/main.go b/cmd/seq-stat/main.go
new file mode 100644
index 0000000..75bb0a3
--- /dev/null
+++ b/cmd/seq-stat/main.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+var ticks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
+
+func histogram(sequence []float64) []rune {
+ if len(sequence) == 0 {
+ return []rune{}
+ }
+
+ minVal := sequence[0]
+ maxVal := sequence[0]
+
+ for _, v := range sequence {
+ if v < minVal {
+ minVal = v
+ }
+ if v > maxVal {
+ maxVal = v
+ }
+ }
+
+ scale := (maxVal - minVal) * 256 / float64(len(ticks)-1)
+ if scale < 1 {
+ scale = 1
+ }
+
+ result := make([]rune, len(sequence))
+ for i, v := range sequence {
+ index := int(((v - minVal) * 256) / scale)
+ if index >= len(ticks) {
+ index = len(ticks) - 1
+ }
+ result[i] = ticks[index]
+ }
+
+ return result
+}
+
+func parseNumbers(input string) ([]float64, error) {
+ fields := strings.Fields(input)
+ numbers := make([]float64, 0, len(fields))
+
+ for _, field := range fields {
+ num, err := strconv.ParseFloat(field, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid number: %s", field)
+ }
+ numbers = append(numbers, num)
+ }
+
+ return numbers, nil
+}
+
+func readFromStdin() ([]float64, error) {
+ var numbers []float64
+ scanner := bufio.NewScanner(os.Stdin)
+
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" {
+ continue
+ }
+
+ lineNumbers, err := parseNumbers(line)
+ if err != nil {
+ return nil, err
+ }
+ numbers = append(numbers, lineNumbers...)
+ }
+
+ if err := scanner.Err(); err != nil {
+ return nil, fmt.Errorf("error reading from stdin: %v", err)
+ }
+
+ return numbers, nil
+}
+
+func main() {
+ var numbers []float64
+ var err error
+
+ if len(os.Args) > 1 {
+ numbers = make([]float64, 0, len(os.Args)-1)
+ for _, arg := range os.Args[1:] {
+ num, parseErr := strconv.ParseFloat(arg, 64)
+ if parseErr != nil {
+ fmt.Fprintf(os.Stderr, "Invalid number: %s\n", arg)
+ os.Exit(1)
+ }
+ numbers = append(numbers, num)
+ }
+ } else {
+ numbers, err = readFromStdin()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ os.Exit(1)
+ }
+
+ if len(numbers) == 0 {
+ fmt.Fprintln(os.Stderr, "No numbers provided")
+ os.Exit(1)
+ }
+ }
+
+ h := histogram(numbers)
+ fmt.Println(string(h))
+}