mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
* fix: respect cache backend type for registry cache import/export * docs: update local development build instructions - Update local image building to use the provided project task. - Document the workflow for building with a local registry. - Add examples for configuring buildctl with caching and debugging flags. Generated-by: aiautocommit * refactor(buildkit): clean up code comments and formatting - Remove redundant comments and improve internal documentation. - Fix minor formatting inconsistencies in code blocks and comment descriptions. Generated-by: aiautocommit * feat: add ParseBool utility to handle common boolean string variants Generated-by: aiautocommit * feat: integrate local registry and buildkit configuration - Add buildkitd configuration to enable HTTP/insecure access to the local registry. - Introduce a local registry service container on port 7890. - Update the buildkit setup to mount the new registry configuration. - Add tasks for building the frontend image and running project builds using the local registry and buildctl. Generated-by: aiautocommit * docs: update CLAUDE.md with architecture and style guidance Generated-by: aiautocommit * feat: add support for multiple cache-from and cache-to build flags - Allow users to configure external cache sources and destinations via the `build` subcommand. - Update cache parsing logic to support multiple entries matching the Docker buildx CLI format. - Remove hardcoded default cache type to provide better control over BuildKit cache configurations. Generated-by: aiautocommit * test: add integration tests for cache-import parsing Generated-by: aiautocommit * refactor(buildkit): extract cache entry parsing logic - Centralize cache configuration logic by moving loop-based parsing into a reusable `cacheEntriesFromFlags` helper. - Improve code maintainability and readability by reducing duplication in `BuildWithBuildkitClient`. - Add comprehensive unit tests for cache entry processing, including edge cases for flag parsing and empty inputs. Generated-by: aiautocommit * fix(test): remove unused buildkit cache flags from integration tests * Remove unused flag variables that were meant for remote BuildKit caching. * Ensure integration tests avoid remote cache (e.g., type=gha) to prevent exceeding GitHub Actions cache limits and excessive eviction. Generated-by: aiautocommit * test(buildkit): consolidate cache import tests - Move TestParseCacheImports from its own file to frontend_test.go to consolidate related buildkit frontend tests. Generated-by: aiautocommit * style: fix indentation in build test helper Generated-by: aiautocommit * docs: document support for BuildKit cache backends Generated-by: aiautocommit * docs(developing-locally): add information on Docker, BuildKit, and caching - Add missing setup prerequisites. - Include debugging and configuration guidance for BuildKit/Docker. - Clarify cache export and import implementation expectations in Railpack. Generated-by: aiautocommit * docs: clarify cache flag parsing and pnpm version logic Generated-by: aiautocommit --------- Co-authored-by: Matt Van Horn <[email protected]> Co-authored-by: Michael Bianco <[email protected]> Co-authored-by: Hanzo Dev <[email protected]>
192 lines
4.7 KiB
Go
192 lines
4.7 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/tailscale/hujson"
|
|
)
|
|
|
|
func RemoveDuplicates[T comparable](sliceList []T) []T {
|
|
allKeys := make(map[T]bool)
|
|
list := []T{}
|
|
for _, item := range sliceList {
|
|
if _, value := allKeys[item]; !value {
|
|
allKeys[item] = true
|
|
list = append(list, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
// Parses common true/false string values (true/1/yes/on and false/0/no/off).
|
|
// The second return is false when the string is empty or unrecognized.
|
|
func ParseBool(s string) (value bool, ok bool) {
|
|
switch strings.ToLower(strings.TrimSpace(s)) {
|
|
case "true", "1", "yes", "on":
|
|
return true, true
|
|
case "false", "0", "no", "off":
|
|
return false, true
|
|
default:
|
|
return false, false
|
|
}
|
|
}
|
|
|
|
// MergeStringSlicePointers combines multiple string slice pointers, deduplicates values, and sorts them
|
|
func MergeStringSlicePointers(slices ...*[]string) *[]string {
|
|
if len(slices) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var allStrings []string
|
|
for _, slice := range slices {
|
|
if slice != nil {
|
|
allStrings = append(allStrings, *slice...)
|
|
}
|
|
}
|
|
|
|
if len(allStrings) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Deduplicate and sort
|
|
seen := make(map[string]bool)
|
|
var uniqueStrings []string
|
|
for _, s := range allStrings {
|
|
if !seen[s] {
|
|
seen[s] = true
|
|
uniqueStrings = append(uniqueStrings, s)
|
|
}
|
|
}
|
|
sort.Strings(uniqueStrings)
|
|
return &uniqueStrings
|
|
}
|
|
|
|
// CapitalizeFirst converts the first character of a string to uppercase.
|
|
// The rest of the string remains unchanged.
|
|
// Examples:
|
|
// - "hello" -> "Hello"
|
|
// - "world" -> "World"
|
|
// - "" -> ""
|
|
// - "already Capitalized" -> "Already Capitalized"
|
|
func CapitalizeFirst(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
runes := []rune(s)
|
|
runes[0] = []rune(strings.ToUpper(string(runes[0])))[0]
|
|
return string(runes)
|
|
}
|
|
|
|
// ParsePackageWithVersion parses a slice of package specifications in the format "name@version"
|
|
// and returns a map of package names to their versions.
|
|
// If a package has no version specified (no @ symbol), it defaults to "latest".
|
|
// Examples:
|
|
// - ["[email protected]"] -> {"node": "14.2"}
|
|
// - ["python"] -> {"python": "latest"}
|
|
// - ["[email protected]", "go"] -> {"ruby": "3.0.0", "go": "latest"}
|
|
// - ["node@^14.3", "python@>=3.9"] -> {"node": "^14.3", "python": ">=3.9"}
|
|
func ParsePackageWithVersion(versions []string) map[string]string {
|
|
parsedVersions := make(map[string]string)
|
|
|
|
for _, version := range versions {
|
|
parts := strings.Split(version, "@")
|
|
if len(parts) == 1 {
|
|
parsedVersions[parts[0]] = "latest"
|
|
} else {
|
|
parsedVersions[parts[0]] = parts[1]
|
|
}
|
|
}
|
|
|
|
return parsedVersions
|
|
}
|
|
|
|
// ExtractSemverVersion extracts the first version number found in a string.
|
|
// It supports full semver (major.minor.patch) as well as partial versions (major or major.minor).
|
|
// The version can appear anywhere in the string and can have prefixes or suffixes.
|
|
// Examples:
|
|
// - "1.2.3" -> "1.2.3"
|
|
// - "v1.2.3" -> "1.2.3"
|
|
// - "python-3.10.7" -> "3.10.7"
|
|
// - "version1.2.3-beta" -> "1.2.3"
|
|
// - "requires node 14.2" -> "14.2"
|
|
// - "python 3" -> "3"
|
|
//
|
|
// Returns an empty string if no version number is found.
|
|
func ExtractSemverVersion(version string) string {
|
|
semverRe := regexp.MustCompile(`(\d+(?:\.\d+)?(?:\.\d+)?)`)
|
|
matches := semverRe.FindStringSubmatch(strings.TrimSpace(version))
|
|
if len(matches) > 1 {
|
|
return matches[1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// ParseSemver parses a semantic version string and returns a Semver struct.
|
|
func ParseSemver(version string) (*Semver, error) {
|
|
// Handle corepack version format like "[email protected]"
|
|
if idx := strings.Index(version, "@"); idx != -1 {
|
|
version = version[idx+1:]
|
|
}
|
|
|
|
// Remove potential prefixes like "v"
|
|
version = strings.TrimPrefix(version, "v")
|
|
|
|
// Split by dots
|
|
parts := strings.Split(version, ".")
|
|
|
|
// Parse major version
|
|
major, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid major version: %s", parts[0])
|
|
}
|
|
|
|
minor := 0
|
|
patch := 0
|
|
if len(parts) > 1 {
|
|
// Parse minor version
|
|
minorVer, err := strconv.Atoi(parts[1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid minor version: %s", parts[1])
|
|
}
|
|
minor = minorVer
|
|
}
|
|
|
|
if len(parts) > 2 {
|
|
// Parse patch version - take care of potential suffixes like "-alpha", "-beta", etc.
|
|
patchStr := parts[2]
|
|
patchParts := strings.Split(patchStr, "-")
|
|
patchVer, err := strconv.Atoi(patchParts[0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid patch version: %s", patchParts[0])
|
|
}
|
|
patch = patchVer
|
|
}
|
|
|
|
return &Semver{
|
|
Major: major,
|
|
Minor: minor,
|
|
Patch: patch,
|
|
}, nil
|
|
}
|
|
|
|
type Semver struct {
|
|
Major int
|
|
Minor int
|
|
Patch int
|
|
}
|
|
|
|
// convert hujson/extended JSON into standardized json
|
|
func StandardizeJSON(b []byte) ([]byte, error) {
|
|
ast, err := hujson.Parse(b)
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
ast.Standardize()
|
|
return ast.Pack(), nil
|
|
}
|