https://github.com/bytedance/gg
🔥gg is a basic library of generics for Go language developed by ByteDance. It is based on the Go 1.18+ generic features and provides efficient, type-safe and rich generic data structures and tool functions.
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (12.0%) to scientific vocabulary
Keywords
Repository
🔥gg is a basic library of generics for Go language developed by ByteDance. It is based on the Go 1.18+ generic features and provides efficient, type-safe and rich generic data structures and tool functions.
Basic Info
- Host: GitHub
- Owner: bytedance
- License: apache-2.0
- Language: Go
- Default Branch: main
- Homepage: https://pkg.go.dev/github.com/bytedance/gg
- Size: 295 KB
Statistics
- Stars: 243
- Watchers: 5
- Forks: 24
- Open Issues: 1
- Releases: 2
Topics
Metadata Files
README.md
gg: Go Generics
English | 简体中文
🔥bytedance/gg is a basic library of generics for Go language developed by ByteDance. It is based on the Go 1.18+ generic features and provides efficient, type-safe and rich generic data structures and tool functions.
❓Why this name?
Take the first letter of Go Generics, short and simple.
❓Why choose gg?
- Stable and reliable: It is a necessary tool library for ByteDance R&D team, and it has 1w+ repository references inside.
- Easy to use: With the design principle of simplicity and self-consistent, subcontracted according to functions, modular, semantic intuitive and unified, and low learning cost.
- High Performance: Provides high-performance concurrent data structures, with performance 10+ times faster than standard library.
- No three-party dependencies: Generic libraries will not introduce any three-party dependencies.
- Version control: Follow the SemVer, guaranteeing backward compatibility.
🚀 Install
sh
go get github.com/bytedance/gg
🔎 Table of contents
- Generic Functional Programming
- Generic Data Processing
- Generic Standard Wrapper
- gsync:Wrap
sync
- gsync:Wrap
- Generic Data Structures
- tuple:Implementation of tuple provides definition of generic n-ary tuples
- set:Implementation of set based on
map[T]struct{} - list:Implementation of doubly linked list
- skipset:High-performance, scalable, concurrent-safe set based on skip-list, up to 15x faster than the built-in
sync.Mapbelow Go 1.24 - skipmap:High-performance, scalable, concurrent-safe map based on skip-list, up to 10x faster than the built-in
sync.Mapbelow Go 1.24
✨ Generic Functional Programming
goption
Option type, simplifying the processing of (T, bool)
Usage:
go
import (
"github.com/bytedance/gg/goption"
)
Example:
go
goption.Of(1, true).Value()
// 1
goption.Nil[int]().IsNil()
// true
goption.Nil[int]().ValueOr(10)
// 10
goption.OK(1).IsOK()
// true
goption.OK(1).ValueOrZero()
// 1
goption.OfPtr((*int)(nil)).Ptr()
// nil
goption.Map(goption.OK(1), strconv.Itoa).Get()
// "1" true
gresult
Result type, simplifying the processing of (T, error)
Usage:
go
import (
"github.com/bytedance/gg/gresult"
)
Example:
go
gresult.Of(strconv.Atoi("1")).Value()
// 1
gresult.Err[int](io.EOF).IsErr()
// true
gresult.Err[int](io.EOF).ValueOr(10)
// 10
gresult.OK(1).IsOK()
// true
gresult.OK(1).ValueOrZero()
// 1
gresult.Of(strconv.Atoi("x")).Option().Get()
// 0 false
gresult.Map(gresult.OK(1), strconv.Itoa).Get()
// "1" nil
✨ Generic Data Processing
gcond:
Conditional operation
Usage:
go
import (
"github.com/bytedance/gg/gcond"
)
Example:
```go gcond.If(true, 1, 2) // 1 var a *struct{ A int } getA := func() int { return a.A } get1 := func() int { return 1 } gcond.IfLazy(a != nil, getA, get1) // 1 gcond.IfLazyL(a != nil, getA, 1) // 1 gcond.IfLazyR(a == nil, 1, getA) // 1
gcond.Switchstring. Case(1, "1"). CaseLazy(2, func() string { return "3" }). When(3, 4).Then("3/4"). When(5, 6).ThenLazy(func() string { return "5/6" }). Default("other") // 3/4 ```
gvalue
Processing value T
Usage:
go
import (
"github.com/bytedance/gg/gvalue"
)
Example1:Zero Value
go
a := gvalue.Zero[int]()
// 0
gvalue.IsZero(a)
// true
b := gvalue.Zero[*int]()
// nil
gvalue.IsNil(b)
// true
gvalue.Or(0, 1, 2)
// 1
Example2:Math Operation
go
gvalue.Max(1, 2, 3)
// 3
gvalue.Min(1, 2, 3)
// 1
gvalue.MinMax(1, 2, 3)
// 1 3
gvalue.Clamp(5, 1, 10)
// 5
gvalue.Add(1, 2)
// 3
Example3:Comparison
go
gvalue.Equal(1, 1)
// true
gvalue.Between(2, 1, 3)
// true
Example4:Type Assertion
go
gvalue.TypeAssert[int](any(1))
// 1
gvalue.TryAssert[int](any(1))
// 1 true
gptr
Processing pointer *T
Usage:
go
import (
"github.com/bytedance/gg/gptr"
)
Example:
```go a := Of(1) gptr.Indirect(a) // 1
b := OfNotZero(1) gptr.IsNotNil(b) // true gptr.IndirectOr(b, 2) // 1 gptr.Indirect(gptr.Map(b, strconv.Itoa)) // "1"
c := OfNotZero(0) // nil gptr.IsNil(c) // true gptr.IndirectOr(c, 2) // 2 ```
gslice
Processing slice []T
Usage:
go
import (
"github.com/bytedance/gg/gslice"
)
Example1:High-order Function
go
gslice.Map([]int{1, 2, 3, 4, 5}, strconv.Itoa)
// ["1", "2", "3", "4", "5"]
isEven := func(i int) bool { return i%2 == 0 }
gslice.Filter([]int{1, 2, 3, 4, 5}, isEven)
// [2, 4]
gslice.Reduce([]int{1, 2, 3, 4, 5}, gvalue.Add[int].Value())
// 15
gslice.Any([]int{1, 2, 3, 4, 5}, isEven)
// true
gslice.All([]int{1, 2, 3, 4, 5}, isEven)
// false
Example2:CURD Operation
go
gslice.Contains([]int{1, 2, 3, 4, 5}, 2)
// true
gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)
// true
gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)
// false
gslice.Index([]int{1, 2, 3, 4, 5}, 3.Value())
// 2
gslice.Find([]int{1, 2, 3, 4, 5}, isEven).Value()
// 2
gslice.First([]int{1, 2, 3, 4, 5}).Value()
// 1
gslice.Get([]int{1, 2, 3, 4, 5}, 1).Value()
// 2
gslice.Get([]int{1, 2, 3, 4, 5}, -1).Value() // Access element with negative index
// 5
Example3:Partion Operation
go
gslice.Range(1, 5)
// [1, 2, 3, 4]
gslice.RangeWithStep(5, 1, -2)
// [5, 3]
gslice.Take([]int{1, 2, 3, 4, 5}, 2)
// [1, 2]
gslice.Take([]int{1, 2, 3, 4, 5}, -2)
// [4, 5]
gslice.Slice([]int{1, 2, 3, 4, 5}, 1, 3)
// [2, 3]
gslice.Chunk([]int{1, 2, 3, 4, 5}, 2)
// [[1, 2], [3, 4], [5]]
gslice.Divide([]int{1, 2, 3, 4, 5}, 2)
// [[1, 2, 3], [4, 5]]
gslice.Concat([]int{1, 2}, []int{3, 4, 5})
// [1, 2, 3, 4, 5]
gslice.Flatten([][]int{{1, 2}, {3, 4, 5}})
// [1, 2, 3, 4, 5]
gslice.Partition([]int{1, 2, 3, 4, 5}, isEven)
// [2, 4], [1, 3, 5]
Example4:Math Operation
go
gslice.Max([]int{1, 2, 3, 4, 5}).Value()
// 5
gslice.Min([]int{1, 2, 3, 4, 5}).Value()
// 1
gslice.MinMax([]int{1, 2, 3, 4, 5}).Value().Values()
// 1 5
gslice.Sum([]int{1, 2, 3, 4, 5})
// 15
Example5:Convert to map
go
ToMap([]int{1, 2, 3, 4, 5}, func(i int) (string, int) { return strconv.Itoa(i), i })
// {"1":1, "2":2, "3":3, "4":4, "5":5}
ToBoolMap([]int{1, 2, 3, 3, 2})
// {1: true, 2: true, 3: true}
ToMapValues([]int{1, 2, 3, 4, 5}, strconv.Itoa)
// {"1":1, "2":2, "3":3, "4":4, "5":5}
GroupBy([]int{1, 2, 3, 4, 5}, func(i int) string {
if i%2 == 0 {
return "even"
} else {
return "odd"
}
})
// {"even":[2,4], "odd":[1,3,5]}
Example6:Set Operation
go
gslice.Union([]int{1, 2, 3}, []int{3, 4, 5})
// [1, 2, 3, 4, 5]
gslice.Intersect([]int{1, 2, 3}, []int{3, 4, 5})
// [3]
gslice.Diff([]int{1, 2, 3}, []int{3, 4, 5})
// [1, 2]
gslice.Uniq([]int{1, 1, 2, 2, 3})
// [1, 2, 3]
gslice.Dup([]int{1, 1, 2, 2, 3})
// [1, 2]
Example7:Re-order Operation
go
s1 := []int{5, 1, 2, 3, 4}
s2, s3, s4 := Clone(s1), Clone(s1), Clone(s1)
Sort(s1)
// [1, 2, 3, 4, 5]
SortBy(s2, func(i, j int) bool { return i > j })
// [5, 4, 3, 2, 1]
StableSortBy(s3, func(i, j int) bool { return i > j })
// [5, 4, 3, 2, 1]
Reverse(s4)
// [4, 3, 2, 1, 5]
gmap
Processing map map[K]V
Usage:
go
import (
"github.com/bytedance/gg/gmap"
)
Example1:Keys / Values Getter
go
gmap.Keys(map[int]int{1: 2})
// [1]
gmap.Values(map[int]int{1: 2})
// [2]
gmap.Items(map[int]int{1: 2}).Unzip()
// [1] [2]
gmap.OrderedKeys(map[int]int{1: 2, 2: 3, 3: 4})
// [1, 2, 3]
gmap.OrderedValues(map[int]int{1: 2, 2: 3, 3: 4})
// [2, 3, 4]
gmap.OrderedItems(map[int]int{1: 2, 2: 3, 3: 4}).Unzip()
// [1, 2, 3] [2, 3, 4]
f := func(k, v int) string { return strconv.Itoa(k) + ":" + strconv.Itoa(v) }
gmap.ToSlice(map[int]int{1: 2}, f)
// ["1:2"]
gmap.ToOrderedSlice(map[int]int{1: 2, 2: 3, 3: 4}, f)
// ["1:2", "2:3", "3:4"]
Example2:High-order Function
go
gmap.Map(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) (string, string) {
return strconv.Itoa(k), strconv.Itoa(k + 1)
})
// {"1":"2", "2":"3", "3":"4"}
gmap.Filter(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) bool {
return k+v > 3
})
// {"2":2, "3":3}
Example3:CURD Operation
go
gmap.Contains(map[int]int{1: 2, 2: 3, 3: 4}, 1)
// true
gmap.ContainsAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// true
gmap.ContainsAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// false
gmap.Load(map[int]int{1: 2, 2: 3, 3: 4}, 1).Value()
// 2
gmap.LoadAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4).Value()
// 2
gmap.LoadAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// []
gmap.LoadSome(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// [2]
Example4:Partion Operation
go
Chunk(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2)
// possible result: [{1:2, 2:3}, {3:4, 4:5}, {5:6}]
Divide(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2)
// possible result: [{1:2, 2:3, 3:4}, {4:5, 5:6}]
Example5:Math Operation
go
gmap.Max(map[int]int{1: 2, 2: 3, 3: 4}).Value()
// 4
gmap.Min(map[int]int{1: 2, 2: 3, 3: 4}).Value()
// 2
gmap.MinMax(map[int]int{1: 2, 2: 3, 3: 4}).Value().Values()
// 2 4
gmap.Sum(map[int]int{1: 2, 2: 3, 3: 4})
// 9
Example6:Set Operation
go
gmap.Union(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {1:2, 2:3, 3:14, 4:15, 5:16}
gmap.Intersect(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {3:14}
gmap.Diff(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {1:2, 2:3}
gmap.UnionBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]())
// {1:2, 2:3, 3:4, 4:15, 5:16}
gmap.IntersectBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]())
// {3:4}
gfunc
Processing function func
Usage:
go
import (
"github.com/bytedance/gg/gfunc"
)
Example1:Partial Application
go
add := Partial2(gvalue.Add[int]) // convert the Add function into a partial function
add1 := add.Partial(1) // Bind (i.e., "freeze") the first argument to 1
add1(0) // 0 + 1 = 1
// 1
add1(1) // Reuse the partially applied function: 1 + 1 = 2
// 2
add1n2 := add1.PartialR(2) // Bind the remaining (rightmost) argument to 2; all arguments are now fixed
add1n2() // 1 + 2 = 3
gconv
Data type conversion
Usage:
go
import (
"github.com/bytedance/gg/gconv"
)
Example:
```go gconv.Tostring // "1" gconv.Toint // 1 gconv.Toint // 0 gconv.Tobool // true gconv.Tobool // false gconv.Toint // 1 type myInt int type myString string gconv.TomyInt // 1 gconv.TomyString // "1"
gconv.ToEint // 0 strconv.ParseInt: parsing "x": invalid syntax ```
gson
Processing JSON
Usage:
go
import (
"github.com/bytedance/gg/gson"
)
Example:
``go
type testStruct struct {
Name stringjson:"name"
Age intjson:"age"`
}
testcase := testStruct{Name: "test", Age: 10}
gson.Marshal(testcase)
// []byte({"name":"test","age":10}) nil
gson.MarshalString(testcase)
// {"name":"test","age":10} nil
gson.ToString(testcase)
// {"name":"test","age":10}
gson.MarshalIndent(testcase, "", " ")
// "{\n \"name\": \"test\",\n \"age\": 10\n}" nil
gson.ToStringIndent(testcase, "", " ")
// "{\n \"name\": \"test\",\n \"age\": 10\n}"
gson.Valid({"name":"test","age":10})
// true
gson.UnmarshaltestStruct
// {test 10} nil
// Use high-performance JSON codecs such as Sonic or json-iterator, instead of the standard library's encoding/json. import "github.com/bytedance/sonic"
gson.MarshalBy(sonic.ConfigDefault, testcase)
// []byte({"name":"test","age":10}) nil
gson.MarshalString(sonic.ConfigDefault, testcase)
// {"name":"test","age":10}, nil
gson.UnmarshalBy[testStruct](sonic.ConfigDefault,{"name":"test","age":10}`)
// testStruct{Name: "test", Age: 10}, nil
// Example using Json-Iterator: import jsoniter "github.com/json-iterator/go"
gson.MarshalBy(jsoniter.ConfigDefault, testcase)
// []byte({"name":"test","age":10}) nil
gson.MarshalString(jsoniter.ConfigDefault, testcase)
// {"name":"test","age":10}, nil
gson.UnmarshalBy[testStruct](jsoniter.ConfigDefault,{"name":"test","age":10})
// testStruct{Name: "test", Age: 10}, nil
``
✨ Generic Standard Wrapper
gsync
Wrap sync
Usage:
go
import (
"github.com/bytedance/gg/gstd/gsync"
)
Example1:gsync.Map wraps sync.Map
go
sm := gsync.Map[string, int]{}
sm.Store("k", 1)
sm.Load("k")
// 1 true
sm.LoadO("k").Value()
// 1
sm.Store("k", 2)
sm.Load("k")
// 2 true
sm.LoadAndDelete("k")
// 2 true
sm.Load("k")
// 0 false
sm.LoadOrStore("k", 3)
// 3 false
sm.Load("k")
// 3 true
sm.ToMap()
// {"k":3}
Example2:gsync.Pool wraps sync.Pool
go
pool := Pool[*int]{
New: func() *int {
i := 1
return &i
},
}
a := pool.Get()
*a
// 1
*a = 2
pool.Put(a)
*pool.Get()
// possible result: 1 or 2
Example3:gsync.OnceXXX wraps sync.Once
```go onceFunc := gsync.OnceFunc(func() { fmt.Println("OnceFunc") }) onceFunc() // "OnceFunc" onceFunc() // (no output) onceFunc() // (no output)
i := 1 onceValue := gsync.OnceValue(func() int { i++; return i }) onceValue() // 2 onceValue() // 2
onceValues := gsync.OnceValues(func() (int, error) { i++; return i, nil }) onceValues() // 3 nil onceValues() // 3 nil ```
✨ Generic Data Structures
tuple
Implementation of tuple provides definition of generic n-ary tuples
Usage
go
import(
"github.com/bytedance/gg/collection/tuple"
)
Example:
```go addr := Make2("localhost", 8080) fmt.Printf("%s:%d\n", addr.First, addr.Second) // localhost:8080
s := Zip2([]string{"red", "green", "blue"}, []int{14, 15, 16}) for _, v := range s { fmt.Printf("%s:%d\n", v.First, v.Second) } // red:14 // green:15 // blue:16
s.Unzip() // ["red", "green", "blue"] [14, 15, 16] ```
set
Implementation of set based on map[T]struct{}
Usage
go
import (
"github.com/bytedance/gg/collection/set"
)
Example:
```go s := New(10, 10, 12, 15) s.Len() // 3 s.Add(10) // false s.Add(11) // true s.Remove(11) && s.Remove(12) // true
s.ContainsAny(10, 15) // true s.ContainsAny(11, 12) // false s.ContainsAny() // false s.ContainsAll(10, 15) // true s.ContainsAll(10, 11) // false s.ContainsAll() // true
len(s.ToSlice()) // 2 ```
list
Implementation of doubly linked list
Usage
go
import (
"github.com/bytedance/gg/collection/list"
)
Example:
```go l := Newint e1 := l.PushFront(1) // 1 e2 := l.PushBack(2) // 1->2 e3 := l.InsertBefore(3, e2) // 1->3->2 e4 := l.InsertAfter(4, e1) // 1->4->3->2
l.MoveToFront(e4) // 4->1->3->2 l.MoveToBack(e1) // 4->3->2->1 l.MoveAfter(e3, e2) // 4->2->3->1 l.MoveBefore(e4, e1) // 2->3->4->1
l.Len() // 4 l.Front().Value // 2 l.Back().Value // 1
for e := l.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) // 2 3 4 1 } ```
skipset
High-performance, scalable, concurrent-safe set based on skip-list, up to 15x faster than the built-in sync.Map below Go 1.24
⚠️ NOTICE: Go 1.24 or later, please consider using the std sync.Map, which has better performance compared to skipset in about 90% of use cases.
Usage
go
import (
"github.com/bytedance/gg/collection/skipset"
)
Example:
```go s := skipset.Newint s.Add(10) // true s.Add(10) // false s.Add(11) // true s.Add(12) // true s.Len() // 3
s.Contains(10) // true s.Remove(10) // true s.Contains(10) // false
s.ToSlice() // [11, 12]
var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { i := i go func() { defer wg.Done() s.Add(i) }() } wg.Wait() s.Len() // 1000 ```
skipmap
High-performance, scalable, concurrent-safe map based on skip-list, up to 10x faster than the built-in sync.Map below Go 1.24
⚠️ Go 1.24 or later, please consider using the std sync.Map, which has better performance compared to skipmap in about 90% of use cases.
Usage
go
import (
"github.com/bytedance/gg/collection/skipmap"
)
Example:
```go s := Newstring, int s.Store("a", 0) s.Store("a", 1) s.Store("b", 2) s.Store("c", 3) s.Len() // 3
s.Load("a") // 1 true s.LoadAndDelete("a") // 1 true s.LoadOrStore("a", 11) // 11 false
gson.ToString(s.ToMap()) // {"a":11, "b":2, "c": 3}
s.Delete("a") s.Delete("b") s.Delete("c") var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { i := i go func() { defer wg.Done() s.Store(strconv.Itoa(i), i) }() } wg.Wait() s.Len() // 1000 ```
License
gg is licensed under the Apache-2.0 license. See LICENSE for details.
2025 © Bytedance
Owner
- Name: Bytedance Inc.
- Login: bytedance
- Kind: organization
- Location: Singapore
- Website: https://opensource.bytedance.com
- Twitter: ByteDanceOSS
- Repositories: 255
- Profile: https://github.com/bytedance
GitHub Events
Total
- Create event: 2
- Issues event: 2
- Release event: 2
- Watch event: 171
- Issue comment event: 29
- Public event: 1
- Push event: 61
- Pull request review comment event: 28
- Pull request event: 29
- Pull request review event: 45
- Fork event: 18
Last Year
- Create event: 2
- Issues event: 2
- Release event: 2
- Watch event: 171
- Issue comment event: 29
- Public event: 1
- Push event: 61
- Pull request review comment event: 28
- Pull request event: 29
- Pull request review event: 45
- Fork event: 18
Issues and Pull Requests
Last synced: 5 months ago
All Time
- Total issues: 2
- Total pull requests: 19
- Average time to close issues: 21 days
- Average time to close pull requests: 5 days
- Total issue authors: 2
- Total pull request authors: 8
- Average comments per issue: 0.5
- Average comments per pull request: 1.16
- Merged pull requests: 10
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 2
- Pull requests: 19
- Average time to close issues: 21 days
- Average time to close pull requests: 5 days
- Issue authors: 2
- Pull request authors: 8
- Average comments per issue: 0.5
- Average comments per pull request: 1.16
- Merged pull requests: 10
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- vijayvenkat704 (1)
- eddiearc (1)
Pull Request Authors
- XQ-Gang (6)
- 785172550 (4)
- SilverRainZ (3)
- limits220284 (2)
- crazyfrankie (1)
- someblue (1)
- kumakichi (1)
- GarrickZ2 (1)
Top Labels
Issue Labels
Pull Request Labels
Packages
- Total packages: 1
- Total downloads: unknown
- Total dependent packages: 0
- Total dependent repositories: 0
- Total versions: 2
proxy.golang.org: github.com/bytedance/gg
- Homepage: https://github.com/bytedance/gg
- Documentation: https://pkg.go.dev/github.com/bytedance/gg#section-documentation
- License: Apache-2.0
-
Latest release: v1.1.0
published 8 months ago
Rankings
Dependencies
- actions/checkout v4 composite
- apache/skywalking-eyes/header v0.7.0 composite
- actions/cache v4 composite
- actions/checkout v4 composite
- actions/setup-go v5 composite
- codecov/codecov-action v5 composite