From 84344f896fa5b322450800e3de978ccfea27962c Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:22:40 -0500 Subject: [PATCH 1/6] feat: remove unreferenced trafficmesh --- trafficmesh/signature.go | 71 ------------------------------ trafficmesh/signature_test.go | 82 ----------------------------------- 2 files changed, 153 deletions(-) delete mode 100644 trafficmesh/signature.go delete mode 100644 trafficmesh/signature_test.go diff --git a/trafficmesh/signature.go b/trafficmesh/signature.go deleted file mode 100644 index e4b39716..00000000 --- a/trafficmesh/signature.go +++ /dev/null @@ -1,71 +0,0 @@ -package trafficmesh - -import ( - "errors" - "fmt" - "net/http" - "net/url" - - "github.com/dgrijalva/jwt-go" -) - -const signatureHeader = "X-NF-Mesh-Signature" - -// SignatureDecoder decodes a signed traffic-mesh header. -type SignatureDecoder struct { - secret string -} - -// SignaturePayload represents the fields in a traffic-mesh signature. -type SignaturePayload struct { - jwt.StandardClaims - SiteID string `json:"sid,omitempty"` - DeployID string `json:"did,omitempty"` - AccountID string `json:"aid,omitempty"` - URL string `json:"url,omitempty"` - Remapped bool `json:"remapped,omitempty"` -} - -// NewSignatureDecoder constructs a new SignatureDecoder. When secret is an empty string, -// DecodeSignature is a no-op. -func NewSignatureDecoder(secret string) *SignatureDecoder { - return &SignatureDecoder{ - secret: secret, - } -} - -// DecodeSignature decodes a traffic-mesh signature. When either the secret or the header is an -// empty string, this method returns a nil payload and nil error. -func (d *SignatureDecoder) DecodeSignature(req *http.Request) (*SignaturePayload, error) { - if d.secret == "" || req.Header.Get(signatureHeader) == "" { - return nil, nil - } - - payload := new(SignaturePayload) - token, err := jwt.ParseWithClaims(req.Header.Get(signatureHeader), payload, func(token *jwt.Token) (interface{}, error) { - alg, ok := token.Method.(*jwt.SigningMethodHMAC) - if !ok || alg != jwt.SigningMethodHS256 { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte(d.secret), nil - }) - if err != nil { - return nil, fmt.Errorf("failed to decode traffic mesh signature: %w", err) - } - if !token.Valid { - return nil, errors.New("invalid token") - } - - payloadURL, err := url.Parse(payload.URL) - if err != nil { - return nil, err - } - if payloadURL.Host != req.Host { - return nil, fmt.Errorf("token host %s doesn't match request host: %s", payloadURL.Host, req.Host) - } - if payloadURI, reqURI := payloadURL.RequestURI(), req.URL.RequestURI(); payloadURI != reqURI { - return nil, fmt.Errorf("token uri %s doesn't match request uri: %s", payloadURI, reqURI) - } - - return payload, nil -} diff --git a/trafficmesh/signature_test.go b/trafficmesh/signature_test.go deleted file mode 100644 index ece88fcd..00000000 --- a/trafficmesh/signature_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package trafficmesh - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/dgrijalva/jwt-go" - "github.com/stretchr/testify/require" -) - -func TestSignatureDecoder(t *testing.T) { - const url = "https://example.org/index.html" - - var makeReq = func() *http.Request { - req := httptest.NewRequest(http.MethodGet, "https://192.0.2.1/index.html", nil) - req.Host = "example.org" - return req - } - - var addToken = func(req *http.Request, secret, url string) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "sid": "1", - "did": "2", - "url": url, - "remapped": true, - }) - sig, err := token.SignedString([]byte(secret)) - require.NoError(t, err) - req.Header.Set(signatureHeader, sig) - } - - dec := NewSignatureDecoder("secret") - - t.Run("no token", func(t *testing.T) { - req := makeReq() - payload, err := dec.DecodeSignature(req) - require.NoError(t, err) - require.Nil(t, payload) - }) - - t.Run("valid token", func(t *testing.T) { - req := makeReq() - addToken(req, "secret", url) - - payload, err := dec.DecodeSignature(req) - require.NoError(t, err) - - require.Equal(t, payload.SiteID, "1") - require.Equal(t, payload.DeployID, "2") - }) - - t.Run("wrong secret", func(t *testing.T) { - req := makeReq() - addToken(req, "wrong-secret", url) - - _, err := dec.DecodeSignature(req) - require.Error(t, err) - }) - - t.Run("wrong path", func(t *testing.T) { - req := makeReq() - addToken(req, "secret", "http://example.org/stuff") - _, err := dec.DecodeSignature(req) - require.Error(t, err) - }) - - t.Run("wrong host", func(t *testing.T) { - req := makeReq() - addToken(req, "secret", "http://example.net/index.html") - _, err := dec.DecodeSignature(req) - require.Error(t, err) - }) - - t.Run("remapped flag", func(t *testing.T) { - req := makeReq() - addToken(req, "secret", url) - payload, err := dec.DecodeSignature(req) - require.NoError(t, err) - require.True(t, payload.Remapped) - }) -} From 071e7625d369030206c0f7bedf4fd1d5bb76938f Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:22:51 -0500 Subject: [PATCH 2/6] feat: remove unreferenced http --- http/banlist/banlist.go | 119 ------------------------- http/banlist/banlist_test.go | 101 --------------------- http/http.go | 167 ----------------------------------- http/http_test.go | 101 --------------------- 4 files changed, 488 deletions(-) delete mode 100644 http/banlist/banlist.go delete mode 100644 http/banlist/banlist_test.go delete mode 100644 http/http.go delete mode 100644 http/http_test.go diff --git a/http/banlist/banlist.go b/http/banlist/banlist.go deleted file mode 100644 index 2ea38878..00000000 --- a/http/banlist/banlist.go +++ /dev/null @@ -1,119 +0,0 @@ -package banlist - -import ( - "encoding/json" - "net/http" - "os" - "os/signal" - "strings" - "sync" - "sync/atomic" - "syscall" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type Config struct { - Domains []string - URLs []string -} - -type Banlist struct { - domainHolder atomic.Value - urlHolder atomic.Value - mtx sync.Mutex - ch chan os.Signal - log logrus.FieldLogger - path string -} - -func New(log logrus.FieldLogger, filepath string) *Banlist { - bl := newBanlist(log, filepath) - bl.listen() - bl.runUpdate() - return bl -} - -func newBanlist(log logrus.FieldLogger, path string) *Banlist { - bl := &Banlist{log: log, path: path} - bl.domainHolder.Store(make(map[string]struct{})) - bl.urlHolder.Store(make(map[string]struct{})) - return bl -} - -func (b *Banlist) listen() { - b.ch = make(chan os.Signal, 1) - signal.Notify(b.ch, syscall.SIGHUP) - go func() { - for range b.ch { - b.runUpdate() - } - b.log.Info("No longer listening for SIGHUP") - }() -} - -func (b *Banlist) runUpdate() { - if err := b.update(); err != nil { - b.log.WithError(err).Warn("error updating banlist") - } else { - b.log.Info("banlist updated") - } -} - -func (b *Banlist) update() error { - b.mtx.Lock() - defer b.mtx.Unlock() - - f, err := os.Open(b.path) - if err != nil { - return errors.Wrap(err, "error opening banlist config") - } - defer f.Close() - - c := new(Config) - if err := json.NewDecoder(f).Decode(c); err != nil { - return errors.Wrap(err, "error decoding banlist config") - } - - domains := make(map[string]struct{}) - urls := make(map[string]struct{}) - for _, el := range c.Domains { - domains[strings.ToLower(el)] = struct{}{} - } - for _, el := range c.URLs { - urls[strings.ToLower(el)] = struct{}{} - } - - b.domainHolder.Store(domains) - b.urlHolder.Store(urls) - return nil -} - -// CheckRequest will check if the domain is blocked or the path is blocked -func (b *Banlist) CheckRequest(r *http.Request) bool { - domain := strings.SplitN(r.Host, ":", 2)[0] - if _, ok := b.domains()[strings.ToLower(domain)]; ok { - return true - } - - url := domain + r.URL.Path - if _, ok := b.urls()[strings.ToLower(url)]; ok { - return true - } - - return false -} - -func (b *Banlist) Close() { - signal.Stop(b.ch) - close(b.ch) -} - -func (b *Banlist) domains() map[string]struct{} { - return b.domainHolder.Load().(map[string]struct{}) -} - -func (b *Banlist) urls() map[string]struct{} { - return b.urlHolder.Load().(map[string]struct{}) -} diff --git a/http/banlist/banlist_test.go b/http/banlist/banlist_test.go deleted file mode 100644 index 838f3697..00000000 --- a/http/banlist/banlist_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package banlist - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestBanlistMissingFile(t *testing.T) { - bl := newBanlist(tl(t), "not a path") - require.Error(t, bl.update()) -} - -func TestBanlistInvalidFileContents(t *testing.T) { - path, err := ioutil.TempFile("", "") - require.NoError(t, err) - defer os.Remove(path.Name()) - - _, err = path.WriteString("this isn't valid json") - require.NoError(t, err) - - bl := newBanlist(tl(t), path.Name()) - require.Error(t, bl.update()) -} - -func TestBanlistNoPaths(t *testing.T) { - bl := testList(t, &Config{ - Domains: []string{"something.com"}, - }) - - assert.Empty(t, bl.urls()) - domains := bl.domains() - assert.Len(t, domains, 1) - _, ok := domains["something.com"] - assert.True(t, ok) -} - -func TestBanlistNoDomains(t *testing.T) { - bl := testList(t, &Config{ - URLs: []string{"something.com/path/to/thing"}, - }) - - urls := bl.urls() - assert.Len(t, urls, 1) - _, ok := urls["something.com/path/to/thing"] - assert.True(t, ok) - - assert.Empty(t, bl.domains()) -} -func TestBanlistBanning(t *testing.T) { - bl := testList(t, &Config{ - URLs: []string{"villians.com/the/joker"}, - Domains: []string{"sick.com"}, - }) - - tests := []struct { - url string - isBanned bool - name string - }{ - {"http://heros.com", false, "completely unbanned"}, - {"http://sick.com:12345", true, "banned domain with port"}, - {"http://sick.com", true, "banned domain without port"}, - {"http://siCK.com", true, "banned domain mixed case"}, - {"http://villians.com:12354/the/joker", true, "banned path with port"}, - {"http://villians.com/the/joker", true, "banned path without port"}, - {"http://villians.com/the/Joker", true, "banned path mixed case"}, - {"http://villians.com/the/joker?query=param", true, "banned path with query params"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, test.url, nil) - assert.Equal(t, test.isBanned, bl.CheckRequest(req)) - }) - } -} - -func tl(t *testing.T) logrus.FieldLogger { - l := logrus.New() - l.SetLevel(logrus.DebugLevel) - return l.WithField("test", t.Name()) -} - -func testList(t *testing.T, config *Config) *Banlist { - path, err := ioutil.TempFile("", "") - require.NoError(t, err) - defer os.Remove(path.Name()) - - require.NoError(t, json.NewEncoder(path).Encode(config)) - - bl := newBanlist(tl(t), path.Name()) - require.NoError(t, bl.update()) - return bl -} diff --git a/http/http.go b/http/http.go deleted file mode 100644 index 6d3e4374..00000000 --- a/http/http.go +++ /dev/null @@ -1,167 +0,0 @@ -package http - -import ( - "context" - "errors" - "fmt" - "net" - "net/http" - "net/http/httptrace" - - "github.com/sirupsen/logrus" -) - -var privateIPBlocks []*net.IPNet - -func init() { - for _, cidr := range []string{ - "127.0.0.0/8", // IPv4 loopback - "10.0.0.0/8", // RFC1918 - "100.64.0.0/10", // RFC6598 - "172.16.0.0/12", // RFC1918 - "192.0.0.0/24", // RFC6890 - "192.168.0.0/16", // RFC1918 - "169.254.0.0/16", // RFC3927 - "::1/128", // IPv6 loopback - "fe80::/10", // IPv6 link-local - "fc00::/7", // IPv6 unique local addr - } { - _, block, _ := net.ParseCIDR(cidr) - privateIPBlocks = append(privateIPBlocks, block) - } -} - -func blocksContainsAny(blocks []*net.IPNet, ips []net.IP) bool { - for _, block := range blocks { - for _, ip := range ips { - if block.Contains(ip) { - return true - } - } - } - return false -} - -func containsPrivateIP(ips []net.IP) bool { - return blocksContainsAny(privateIPBlocks, ips) -} - -type noLocalTransport struct { - inner http.RoundTripper - errlog logrus.FieldLogger - allowedBlocks []*net.IPNet -} - -func (no noLocalTransport) RoundTrip(req *http.Request) (*http.Response, error) { - ctx, cancel := context.WithCancel(req.Context()) - - ctx = httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{ - GetConn: func(hostPort string) { - host, _, err := net.SplitHostPort(hostPort) - if err != nil { - cancel() - no.errlog.WithError(err).Error("Cancelled request due to error in address parsing") - return - } - - ips, err := net.LookupIP(host) - if err != nil || len(ips) == 0 { - cancel() - no.errlog.WithError(err).Error("Cancelled request due to error in host lookup") - return - } - - if blocksContainsAny(no.allowedBlocks, ips) { - return - } - - if containsPrivateIP(ips) { - cancel() - no.errlog.Error("Cancelled attempted request to ip in private range") - return - } - }, - }) - - req = req.WithContext(ctx) - return no.inner.RoundTrip(req) -} - -// SafeRoundtripper blocks requests to private ip ranges -// Deprecated: use SafeTransport instead -func SafeRoundtripper(trans http.RoundTripper, log logrus.FieldLogger, allowedBlocks ...*net.IPNet) http.RoundTripper { - if trans == nil { - trans = http.DefaultTransport - } - - ret := &noLocalTransport{ - inner: trans, - errlog: log.WithField("transport", "local_blocker"), - allowedBlocks: allowedBlocks, - } - - return ret -} - -// SafeHTTPClient blocks requests to private ip ranges -// Deprecated: use SafeTransport instead -func SafeHTTPClient(client *http.Client, log logrus.FieldLogger, allowedBlocks ...*net.IPNet) *http.Client { - client.Transport = SafeRoundtripper(client.Transport, log, allowedBlocks...) - - return client -} - -// SafeTransport blocks requests to private ip ranges -func SafeTransport(allowedBlocks ...*net.IPNet) *http.Transport { - return &http.Transport{ - DialContext: SafeDial(&net.Dialer{}, allowedBlocks...), - } -} - -type DialFunc func(ctx context.Context, network, address string) (net.Conn, error) - -// SafeDial wraps a *net.Dialer and restricts connections to private ip ranges. -func SafeDial(dialer *net.Dialer, allowedBlocks ...*net.IPNet) DialFunc { - d := &safeDialer{dialer: dialer, allowedBlocks: allowedBlocks} - return d.dialContext -} - -type safeDialer struct { - allowedBlocks []*net.IPNet - dialer *net.Dialer -} - -func (d *safeDialer) dialContext(ctx context.Context, network, address string) (net.Conn, error) { - conn, err := d.dialer.DialContext(ctx, network, address) - if err != nil { - return nil, err - } - addr := conn.RemoteAddr().String() - - host, _, err := net.SplitHostPort(addr) - if err != nil { - _ = conn.Close() - return nil, fmt.Errorf("safe dialer: invalid address: %w", err) - } - - ip := net.ParseIP(host) - if ip == nil { - _ = conn.Close() - return nil, fmt.Errorf("safe dialer: invalid ip: %v", host) - } - - for _, block := range d.allowedBlocks { - if block.Contains(ip) { - return conn, nil - } - } - - for _, block := range privateIPBlocks { - if block.Contains(ip) { - _ = conn.Close() - return nil, errors.New("safe dialer: private ip not allowed") - } - } - - return conn, nil -} diff --git a/http/http_test.go b/http/http_test.go deleted file mode 100644 index 712d7809..00000000 --- a/http/http_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package http - -import ( - "net" - "net/http" - "net/http/httptest" - "net/url" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestIsPrivateIP(t *testing.T) { - tests := []struct { - ip string - expected bool - }{ - {"216.58.194.206", false}, - {"127.0.0.1", true}, - {"10.0.0.1", true}, - {"192.168.0.1", true}, - {"172.16.0.0", true}, - {"fd00:ec2::254", true}, - {"::ffff:169.254.169.254", true}, - {"0:0:0:0:0:ffff:169.254.169.254", true}, - } - - for _, tt := range tests { - ip := net.ParseIP(tt.ip) - if ip == nil { - require.Fail(t, "failed to parse IP") - } - assert.Equal(t, tt.expected, containsPrivateIP([]net.IP{ip})) - } -} - -func TestBlockList(t *testing.T) { - t.Run("safe http client", func(t *testing.T) { - client := SafeHTTPClient(&http.Client{}, logrus.New()) - testBlockList(t, client) - }) - t.Run("safe dial", func(t *testing.T) { - client := &http.Client{Transport: SafeTransport()} - testBlockList(t, client) - }) -} - -func TestAllowList(t *testing.T) { - _, local, err := net.ParseCIDR("127.0.0.1/8") - require.NoError(t, err) - - t.Run("safe http client", func(t *testing.T) { - client := SafeHTTPClient(&http.Client{}, logrus.New(), local) - testAllowList(t, client) - }) - t.Run("safe dial", func(t *testing.T) { - client := &http.Client{Transport: SafeTransport(local)} - testAllowList(t, client) - }) -} - -func testBlockList(t *testing.T, client *http.Client) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Done")) - })) - defer ts.Close() - tsURL, err := url.Parse(ts.URL) - if err != nil { - t.Fatal(err) - } - - // It allows accessing non-local addresses - _, err = client.Get("https://google.com") - require.Nil(t, err) - - // It blocks the local IP. - _, err = client.Get(ts.URL) - require.NotNil(t, err) - - // It blocks localhost. - _, err = client.Get("http://localhost:" + tsURL.Port()) - require.NotNil(t, err) - - // It works when reusing pooled connections. - for i := 0; i < 50; i++ { - res, err := client.Get("http://localhost:" + tsURL.Port()) - assert.Nil(t, res) - assert.NotNil(t, err) - } -} - -func testAllowList(t *testing.T, client *http.Client) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Done")) - })) - defer ts.Close() - _, err := client.Get(ts.URL) - assert.NoError(t, err) -} From d9cdfd7512e99c8c4802fc0c4c10bc7dc21cfa72 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:23:04 -0500 Subject: [PATCH 3/6] feat: remove unreferenced instrument --- instrument/config.go | 8 --- instrument/doc.go | 30 ---------- instrument/global.go | 61 -------------------- instrument/instrument.go | 103 ---------------------------------- instrument/instrument_test.go | 43 -------------- instrument/mock.go | 55 ------------------ 6 files changed, 300 deletions(-) delete mode 100644 instrument/config.go delete mode 100644 instrument/doc.go delete mode 100644 instrument/global.go delete mode 100644 instrument/instrument.go delete mode 100644 instrument/instrument_test.go delete mode 100644 instrument/mock.go diff --git a/instrument/config.go b/instrument/config.go deleted file mode 100644 index ac32c738..00000000 --- a/instrument/config.go +++ /dev/null @@ -1,8 +0,0 @@ -package instrument - -type Config struct { - // Write Key for the Segment source - Key string `json:"key" yaml:"key"` - // If this is false, instead of sending the event to Segment, emits verbose log to logger - Enabled bool `json:"enabled" yaml:"enabled" default:"false"` -} diff --git a/instrument/doc.go b/instrument/doc.go deleted file mode 100644 index eaf38311..00000000 --- a/instrument/doc.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Package instrument provides the tool to emit the events to the instrumentation -destination. Currently it supports Segment or logger as a destination. - -When enabling, make sure to follow the guideline specified in https://github.com/netlify/segment-events - -In the config file, you can define the API key, as well as if it's enabled (use -Segment) or not (use logger). - INSTRUMENT_ENABLED=true - INSTRUMENT_KEY=segment_api_key - -To use, you can import this package: - import "github.com/netlify/netlify-commons/instrument" - -You will likely need to import the Segment's analytics package as well, to -create new traits and properties. - import "gopkg.in/segmentio/analytics-go.v3" - -Then call the functions: - instrument.Track("userid", "service:my_event", analytics.NewProperties().Set("color", "green")) - -For testing, you can create your own mock instrument and use it: - func TestSomething (t *testing.T) { - log := testutil.TL(t) - old := instrument.GetGlobalClient() - t.Cleanup(func(){ instrument.SetGlobalClient(old) }) - instrument.SetGlobalClient(instrument.MockClient{Logger: log}) - } -*/ -package instrument diff --git a/instrument/global.go b/instrument/global.go deleted file mode 100644 index 9f073737..00000000 --- a/instrument/global.go +++ /dev/null @@ -1,61 +0,0 @@ -package instrument - -import ( - "sync" - - "github.com/sirupsen/logrus" - "gopkg.in/segmentio/analytics-go.v3" -) - -var globalLock sync.Mutex -var globalClient Client = MockClient{} - -func SetGlobalClient(client Client) { - if client == nil { - return - } - globalLock.Lock() - globalClient = client - globalLock.Unlock() -} - -func GetGlobalClient() Client { - globalLock.Lock() - defer globalLock.Unlock() - return globalClient -} - -// Init will initialize global client with a segment client -func Init(conf Config, log logrus.FieldLogger) error { - segmentClient, err := NewClient(&conf, log) - if err != nil { - return err - } - SetGlobalClient(segmentClient) - return nil -} - -// Identify sends an identify type message to a queue to be sent to Segment. -func Identify(userID string, traits analytics.Traits) error { - return GetGlobalClient().Identify(userID, traits) -} - -// Track sends a track type message to a queue to be sent to Segment. -func Track(userID string, event string, properties analytics.Properties) error { - return GetGlobalClient().Track(userID, event, properties) -} - -// Page sends a page type message to a queue to be sent to Segment. -func Page(userID string, name string, properties analytics.Properties) error { - return GetGlobalClient().Page(userID, name, properties) -} - -// Group sends a group type message to a queue to be sent to Segment. -func Group(userID string, groupID string, traits analytics.Traits) error { - return GetGlobalClient().Group(userID, groupID, traits) -} - -// Alias sends an alias type message to a queue to be sent to Segment. -func Alias(previousID string, userID string) error { - return GetGlobalClient().Alias(previousID, userID) -} diff --git a/instrument/instrument.go b/instrument/instrument.go deleted file mode 100644 index cced6401..00000000 --- a/instrument/instrument.go +++ /dev/null @@ -1,103 +0,0 @@ -package instrument - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" - "gopkg.in/segmentio/analytics-go.v3" -) - -type Client interface { - Identify(userID string, traits analytics.Traits) error - Track(userID string, event string, properties analytics.Properties) error - Page(userID string, name string, properties analytics.Properties) error - Group(userID string, groupID string, traits analytics.Traits) error - Alias(previousID string, userID string) error -} - -type segmentClient struct { - analytics.Client - log logrus.FieldLogger -} - -var _ Client = &segmentClient{} - -func NewClient(cfg *Config, logger logrus.FieldLogger) (Client, error) { - config := analytics.Config{} - - if !cfg.Enabled { - // use mockClient instead - return &MockClient{logger}, nil - } - - configureLogger(&config, logger) - - inner, err := analytics.NewWithConfig(cfg.Key, config) - if err != nil { - logger.WithError(err).Error("Unable to construct Segment client") - } - return &segmentClient{inner, logger}, err -} - -func (c segmentClient) Identify(userID string, traits analytics.Traits) error { - return c.Client.Enqueue(analytics.Identify{ - UserId: userID, - Traits: traits, - }) -} - -func (c segmentClient) Track(userID string, event string, properties analytics.Properties) error { - return c.Client.Enqueue(analytics.Track{ - UserId: userID, - Event: event, - Properties: properties, - }) -} - -func (c segmentClient) Page(userID string, name string, properties analytics.Properties) error { - return c.Client.Enqueue(analytics.Page{ - UserId: userID, - Name: name, - Properties: properties, - }) -} - -func (c segmentClient) Group(userID string, groupID string, traits analytics.Traits) error { - return c.Client.Enqueue(analytics.Group{ - UserId: userID, - GroupId: groupID, - Traits: traits, - }) -} - -func (c segmentClient) Alias(previousID string, userID string) error { - return c.Client.Enqueue(analytics.Alias{ - PreviousId: previousID, - UserId: userID, - }) -} - -func configureLogger(conf *analytics.Config, log logrus.FieldLogger) { - if log == nil { - l := logrus.New() - l.SetOutput(ioutil.Discard) - log = l - } - log = log.WithField("component", "segment") - conf.Logger = &wrapLog{log.Printf, log.Errorf} -} - -type wrapLog struct { - printf func(format string, args ...interface{}) - errorf func(format string, args ...interface{}) -} - -// Logf implements analytics.Logger interface -func (l *wrapLog) Logf(format string, args ...interface{}) { - l.printf(format, args...) -} - -// Errorf implements analytics.Logger interface -func (l *wrapLog) Errorf(format string, args ...interface{}) { - l.errorf(format, args...) -} diff --git a/instrument/instrument_test.go b/instrument/instrument_test.go deleted file mode 100644 index 21eb7bce..00000000 --- a/instrument/instrument_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package instrument - -import ( - "reflect" - "testing" - - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gopkg.in/segmentio/analytics-go.v3" -) - -func TestLogOnlyClient(t *testing.T) { - cfg := Config{ - Key: "ABCD", - Enabled: false, - } - client, err := NewClient(&cfg, nil) - require.NoError(t, err) - - require.Equal(t, reflect.TypeOf(&MockClient{}).Kind(), reflect.TypeOf(client).Kind()) -} - -func TestMockClient(t *testing.T) { - log := logrus.New() - mock := MockClient{log} - - require.NoError(t, mock.Identify("myuser", analytics.NewTraits().SetName("My User"))) -} - -func TestLogging(t *testing.T) { - cfg := Config{ - Key: "ABCD", - } - - log, hook := test.NewNullLogger() - - client, err := NewClient(&cfg, log.WithField("component", "segment")) - require.NoError(t, err) - require.NoError(t, client.Identify("myuser", analytics.NewTraits().SetName("My User"))) - assert.NotEmpty(t, hook.LastEntry()) -} diff --git a/instrument/mock.go b/instrument/mock.go deleted file mode 100644 index 9b0d2603..00000000 --- a/instrument/mock.go +++ /dev/null @@ -1,55 +0,0 @@ -package instrument - -import ( - "github.com/sirupsen/logrus" - "gopkg.in/segmentio/analytics-go.v3" -) - -type MockClient struct { - Logger logrus.FieldLogger -} - -var _ Client = MockClient{} - -func (c MockClient) Identify(userID string, traits analytics.Traits) error { - c.Logger.WithFields(logrus.Fields{ - "user_id": userID, - "traits": traits, - }).Infof("Received Identity event") - return nil -} - -func (c MockClient) Track(userID string, event string, properties analytics.Properties) error { - c.Logger.WithFields(logrus.Fields{ - "user_id": userID, - "event": event, - "properties": properties, - }).Infof("Received Track event") - return nil -} - -func (c MockClient) Page(userID string, name string, properties analytics.Properties) error { - c.Logger.WithFields(logrus.Fields{ - "user_id": userID, - "name": name, - "properties": properties, - }).Infof("Received Page event") - return nil -} - -func (c MockClient) Group(userID string, groupID string, traits analytics.Traits) error { - c.Logger.WithFields(logrus.Fields{ - "user_id": userID, - "group_id": groupID, - "traits": traits, - }).Infof("Received Group event") - return nil -} - -func (c MockClient) Alias(previousID string, userID string) error { - c.Logger.WithFields(logrus.Fields{ - "previous_id": previousID, - "user_id": userID, - }).Infof("Received Alias event") - return nil -} From a43026a51166b553e3bbd396bbc403e37ef9d28f Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:23:15 -0500 Subject: [PATCH 4/6] chore: clean up code owners file --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 13189038..866f53e4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* * @netlify/backend From fb69ed09c1918e88d604023d41cfa63d8da88b76 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:23:29 -0500 Subject: [PATCH 5/6] chore: rename repository for release please action --- .github/workflows/release-please.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 122b022e..131b1a68 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,7 +14,7 @@ jobs: private-key: ${{ secrets.TOKENS_PRIVATE_KEY }} app-id: ${{ secrets.TOKENS_APP_ID }} - - uses: GoogleCloudPlatform/release-please-action@v4 + - uses: googleapis/release-please-action@v4 with: release-type: go package-name: netlify-commons From a9a6934e925ebe34262dd3ff3793e2d398c04077 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Sat, 27 Dec 2025 13:23:44 -0500 Subject: [PATCH 6/6] chore: go mod tidy --- go.mod | 6 ------ go.sum | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/go.mod b/go.mod index f562dd5d..6bf87310 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,21 @@ module github.com/netlify/netlify-commons require ( github.com/BurntSushi/toml v0.4.1 github.com/armon/go-metrics v0.3.10 // indirect - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect - github.com/kr/pretty v0.2.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/nats-io/nats-streaming-server v0.15.1 // indirect github.com/nats-io/nats.go v1.8.1 github.com/nats-io/stan.go v0.5.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/errors v0.9.1 - github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.7.0 github.com/tidwall/pretty v1.0.1 // indirect - github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect go.mongodb.org/mongo-driver v1.9.0 google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bcac6e1f..ba9d3a82 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= @@ -21,8 +19,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -74,8 +70,6 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -129,8 +123,6 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 h1:ZuhckGJ10ulaKkdvJtiAqsLTiPrLaXSdnVgXJKJkTxE= -github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -153,8 +145,6 @@ github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= -github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= @@ -206,8 +196,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= -gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=