Git Repository

Stackyard

Stackyard scannt lokale Entwicklungsordner und bündelt Projekte, Git-Status, TODOs, Toolchains, Docker-Compose-Setups, Datei- und Launcher-Funktionen sowie Operations-Informationen in einer lokalen WebUI.

Projektseite ↗
HTTPShttps://zanvex.de/git/stackyard.git

internal/project/discovery.go

Zum Verzeichnis
package project

import (
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	miseLinePattern    = regexp.MustCompile(`(?m)^\s*([A-Za-z0-9._-]+)\s*=\s*["']?([^"'\n]+)["']?\s*$`)
	portPattern        = regexp.MustCompile(`(?m)(\d{2,5})\s*:\s*(\d{2,5})`)
	exposedPortPattern = regexp.MustCompile(`(?m)\b(?:PORT|EXPOSE_PORT|VITE_PORT|DEV_PORT)\s*[:=]\s*["']?(\d{2,5})["']?`)
)

func DiscoverMiseTools(projectDir string) []string {
	path := filepath.Join(projectDir, ".mise.toml")
	data, err := os.ReadFile(path)
	if err != nil {
		return nil
	}

	lines := strings.Split(string(data), "\n")
	inTools := false
	tools := make([]string, 0)
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" || strings.HasPrefix(trimmed, "#") {
			continue
		}
		if strings.HasPrefix(trimmed, "[") {
			inTools = trimmed == "[tools]"
			continue
		}
		if !inTools {
			continue
		}
		matches := miseLinePattern.FindStringSubmatch(trimmed)
		if len(matches) != 3 {
			continue
		}
		tools = append(tools, matches[1]+" "+strings.TrimSpace(matches[2]))
	}
	sort.Strings(tools)
	return tools
}

func DiscoverComposeFiles(projectDir string) []string {
	candidates := []string{"docker-compose.yml", "compose.yml"}
	files := make([]string, 0, len(candidates))
	for _, name := range candidates {
		if _, err := os.Stat(filepath.Join(projectDir, name)); err == nil {
			files = append(files, name)
		}
	}
	return files
}

func DiscoverPorts(projectDir string) []int {
	paths := []string{
		filepath.Join(projectDir, "docker-compose.yml"),
		filepath.Join(projectDir, "compose.yml"),
		filepath.Join(projectDir, ".env"),
		filepath.Join(projectDir, "README.md"),
	}

	ports := make(map[int]struct{})
	for _, path := range paths {
		data, err := os.ReadFile(path)
		if err != nil {
			continue
		}
		for _, match := range portPattern.FindAllStringSubmatch(string(data), -1) {
			if len(match) < 2 {
				continue
			}
			port, err := strconv.Atoi(match[1])
			if err == nil && port > 0 && port <= 65535 {
				ports[port] = struct{}{}
			}
		}
		for _, match := range exposedPortPattern.FindAllStringSubmatch(string(data), -1) {
			if len(match) < 2 {
				continue
			}
			port, err := strconv.Atoi(match[1])
			if err == nil && port > 0 && port <= 65535 {
				ports[port] = struct{}{}
			}
		}
	}

	values := make([]int, 0, len(ports))
	for port := range ports {
		values = append(values, port)
	}
	sort.Ints(values)
	return values
}