Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: theopenlane/utils
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.3.1
Choose a base ref
...
head repository: theopenlane/utils
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
31 changes: 10 additions & 21 deletions .buildkite/pipeline.yaml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ steps:
key: "lint"
plugins:
- docker#v5.12.0:
image: "registry.hub.docker.com/golangci/golangci-lint:v1.60.3"
image: "registry.hub.docker.com/golangci/golangci-lint:v1.63.4"
command: ["golangci-lint", "run", "-v", "--timeout", "10m", "--config", ".golangci.yaml", "--concurrency", "0"]
environment:
- "GOTOOLCHAIN=auto"
@@ -19,30 +19,18 @@ steps:
cancel_on_build_failing: true
plugins:
- docker#v5.12.0:
image: golang:1.23.1
image: golang:1.23.5
always-pull: true
command: ["go", "test", "-coverprofile=coverage.out", "./..."]
artifact_paths: ["coverage.out"]
- group: ":closed_lock_with_key: Security Checks"
depends_on: "tests"
key: "security"
steps:
- label: ":closed_lock_with_key: gosec"
key: "gosec"
plugins:
- docker#v5.12.0:
image: "securego/gosec:2.20.0"
command: ["-no-fail", "-exclude-generated", "-fmt sonarqube", "-out", "results.txt", "./..."]
environment:
- "GOTOOLCHAIN=auto"
artifact_paths: ["results.txt"]
- label: ":github: upload PR reports"
key: "scan-upload-pr"
if: build.pull_request.id != null
depends_on: ["gosec", "go_test"]
depends_on: ["go_test"]
plugins:
- artifacts#v1.9.4:
download: "results.txt"
- cluster-secrets#v1.0.0:
variables:
SONAR_TOKEN: SONAR_TOKEN
- artifacts#v1.9.4:
download: "coverage.out"
step: "go_test"
@@ -55,10 +43,11 @@ steps:
- label: ":github: upload reports"
key: "scan-upload"
if: build.branch == "main"
depends_on: ["gosec", "go_test"]
depends_on: ["go_test"]
plugins:
- artifacts#v1.9.4:
download: results.txt
- cluster-secrets#v1.0.0:
variables:
SONAR_TOKEN: SONAR_TOKEN
- artifacts#v1.9.4:
download: coverage.out
step: "go_test"
6 changes: 3 additions & 3 deletions .github/workflows/releaser.yml
Original file line number Diff line number Diff line change
@@ -45,9 +45,9 @@ jobs:
go-version-file: 'go.mod'
cache: true
- name: Install Syft
uses: anchore/sbom-action/download-syft@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
- name: Install Cosign
uses: sigstore/cosign-installer@v3.7.0
uses: sigstore/cosign-installer@v3.8.1
- name: Run GoReleaser
id: run-goreleaser
uses: goreleaser/goreleaser-action@v6
@@ -95,7 +95,7 @@ jobs:
permissions: read-all
steps:
- name: Install the SLSA verifier
uses: slsa-framework/slsa-verifier/actions/installer@v2.6.0
uses: slsa-framework/slsa-verifier/actions/installer@v2.7.0
- name: Download assets
env:
GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -5,15 +5,15 @@ default_language_version:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: detect-private-key
- repo: https://github.com/google/yamlfmt
rev: v0.13.0
rev: v0.15.0
hooks:
- id: yamlfmt
- repo: https://github.com/crate-ci/typos
rev: v1.24.1
rev: v1.29.4
hooks:
- id: typos
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2024 The Open Lane, Inc.
Copyright 2025 theopenlane, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
<div align="center">

[![Build status](https://badge.buildkite.com/a3a38b934ca2bb7fc771e19bc5a986a1452fa2962e4e1c63bf.svg?branch=main)](https://buildkite.com/theopenlane/utils)
[![Go Reference](https://pkg.go.dev/badge/github.com/theopenlane/utils.svg)](https://pkg.go.dev/github.com/theopenlane/utils)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache2.0-brightgreen.svg)](https://opensource.org/licenses/Apache-2.0)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=theopenlane_utils&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=theopenlane_util

</div>

# utils

Utilities for working within the openlane ecosystem
Utilities for working within the openlane ecosystem
6 changes: 6 additions & 0 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
@@ -12,6 +12,12 @@ tasks:
cmds:
- golangci-lint run --config=.golangci.yaml --verbose --fast --fix

go:lint:ci:
desc: runs golangci-lint, the most annoying opinionated linter ever, for CI
## do not use --fast or --fix in CI
cmds:
- golangci-lint run --config=.golangci.yaml --verbose

go:fmt:
desc: format all go code
cmds:
54 changes: 54 additions & 0 deletions contextx/contextx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package contextx

import (
"context"
)

// key is a unique type that we can use as a key in a context
type key[T any] struct{}

// With returns a copy of parent that contains the given value which can be retrieved by calling From with the resulting context
// The function uses a generic key type to ensure that the stored value is type-safe and can be uniquely identified and retrieved without
// risk of key collisions
func With[T any](ctx context.Context, v T) context.Context {
return context.WithValue(ctx, key[T]{}, v)
}

// From returns the value associated with the wanted type from the context
// It performs a type assertion to convert the value to the desired type T
// If the type assertion is successful, it returns the value and true
// If the type assertion fails, it returns the zero value of type T and false
func From[T any](ctx context.Context) (T, bool) {
v, ok := ctx.Value(key[T]{}).(T)

return v, ok
}

// MustFrom is similar to from, except that it panics if the type assertion fails / the value is not in the context
func MustFrom[T any](ctx context.Context) T {
return ctx.Value(key[T]{}).(T)
}

// FromOr returns the value associated with the wanted type or the given default value if the type is not found
// This function is useful when you want to ensure that a value is always returned from the context, even if the
// context does not contain a value of the desired type. By providing a default value, you can avoid handling
// the case where the value is missing and ensure that your code has a fallback value to use
func FromOr[T any](ctx context.Context, def T) T {
v, ok := From[T](ctx)
if !ok {
return def
}

return v
}

// FromOrFunc returns the value associated with the wanted type or the result of the given function if the type is not found
// This function is useful when the default value is expensive to compute or when the default value depends on some runtime conditions
func FromOrFunc[T any](ctx context.Context, f func() T) T {
v, ok := From[T](ctx)
if !ok {
return f()
}

return v
}
102 changes: 102 additions & 0 deletions contextx/contextx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package contextx

import (
"context"
"reflect"
"testing"
)

func TestNormalOperation(t *testing.T) {
ctx := context.Background()
ctx = With(ctx, 10)

if MustFrom[int](ctx) != 10 {
t.FailNow()
}

if _, ok := From[float64](ctx); ok {
t.FailNow()
}
}

func TestIsolatedFromExplicitTypeReflection(t *testing.T) {
ctx := context.Background()

ctx = With(ctx, 10)

ctx = context.WithValue(ctx, reflect.TypeOf(20), 20)

if MustFrom[int](ctx) != 10 {
t.FailNow()
}
}

func TestPanicIfNoValue(t *testing.T) {
defer func() {
if recover() == nil {
t.FailNow()
}
}()

MustFrom[int](context.Background())
}

type x interface {
a()
}

type y struct{ v int }

func (y) a() {}

type z struct{ f func() }

func (z z) a() { z.f() }

func TestShouldWorkOnInterface(t *testing.T) {
var a x = y{10}

ctx := context.Background()
ctx = With(ctx, a)

b := MustFrom[x](ctx)
if b.(y).v != 10 {
t.FailNow()
}

r := ""
a = z{func() { r = "hello" }}

ctx = With(ctx, a)

MustFrom[x](ctx).a()

if r != "hello" {
t.FailNow()
}
}
func TestFromOr(t *testing.T) {
ctx := context.Background()
ctx = With(ctx, 10)

if FromOr(ctx, 20) != 10 {
t.FailNow()
}

if FromOr(context.Background(), 20) != 20 {
t.FailNow()
}
}

func TestFromOrFunc(t *testing.T) {
ctx := context.Background()
ctx = With(ctx, 10)

if FromOrFunc(ctx, func() int { return 20 }) != 10 {
t.FailNow()
}

if FromOrFunc(context.Background(), func() int { return 20 }) != 20 {
t.FailNow()
}
}
8 changes: 8 additions & 0 deletions contextx/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Package contextx is a helper package for managing context values
// Most **request-scoped data** is a singleton per request
// That is, it doesn't make sense for a request to carry around multiple loggers, users, traces
// you want to carry the _same one_ with you from function call to function call
// the way we've handled this historically is a separate context key per type you want to carry in the struct
// but with generics, instead of having to make a new zero-sized type for every struct
// we can just make a single generic type and use it for everything which is what this helper package is intended to do
package contextx
39 changes: 14 additions & 25 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,64 +1,53 @@
module github.com/theopenlane/utils

go 1.23.1
go 1.23.5

require (
entgo.io/ent v0.14.1
github.com/brianvoe/gofakeit/v7 v7.1.2
github.com/cenkalti/backoff/v4 v4.3.0
entgo.io/ent v0.14.3
github.com/brianvoe/gofakeit/v7 v7.2.1
github.com/cenkalti/backoff/v5 v5.0.2
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.24
github.com/oklog/ulid/v2 v2.1.0
github.com/olekukonko/tablewriter v0.0.5
github.com/ory/dockertest v3.3.5+incompatible
github.com/redis/go-redis/v9 v9.7.0
github.com/redis/go-redis/v9 v9.7.1
github.com/rs/zerolog v1.33.0
github.com/stoewer/go-strcase v1.3.0
github.com/stretchr/testify v1.9.0
github.com/theopenlane/echox v0.2.0
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d
golang.org/x/crypto v0.29.0
modernc.org/sqlite v1.34.1
github.com/stretchr/testify v1.10.0
github.com/theopenlane/echox v0.2.1
github.com/zalando/go-keyring v0.2.6
golang.org/x/crypto v0.35.0
)

require (
al.essio.dev/pkg/shellescape v1.5.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.1.14 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)
Loading