diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 0c700ba1..bfb62f0d 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: "1.26.2" + GO_VERSION: "1.27.0" jobs: static-checks: @@ -40,7 +40,7 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@v9 with: - version: v2.11.3 + version: v2.13.1 args: --timeout=3m tests: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42c1c44e..1428599c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ permissions: contents: read env: - GO_VERSION: "1.26.2" + GO_VERSION: "1.27.0" NODE_VERSION: "24" PNPM_VERSION: "11.0.1" ARTIFACT_RETENTION_DAYS: 60 diff --git a/.github/workflows/upgrade-smoke.yml b/.github/workflows/upgrade-smoke.yml index 28301cbe..347ccf2f 100644 --- a/.github/workflows/upgrade-smoke.yml +++ b/.github/workflows/upgrade-smoke.yml @@ -35,7 +35,7 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: "1.26.2" + GO_VERSION: "1.27.0" NODE_VERSION: "24" PNPM_VERSION: "11.0.1" diff --git a/AGENTS.md b/AGENTS.md index ffb9c2a5..efc3ff26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ repo. If a fact here conflicts with source files or CI config, trust the source Memos is a self-hosted note-taking app. -- Backend: Go 1.26.2, Echo v5, Connect RPC, gRPC-Gateway, Protocol Buffers. +- Backend: Go 1.27.0, Echo v5, Connect RPC, gRPC-Gateway, Protocol Buffers. - Frontend: React 19, TypeScript 6, Vite 8, Tailwind CSS v4, React Query v5. - Storage: SQLite, MySQL, PostgreSQL. - Generated API outputs: `proto/gen/` for Go/OpenAPI, `web/src/types/proto/` for TypeScript. @@ -33,7 +33,7 @@ go test -v ./store/... # Store tests, including DB drivers via TestC go test -v -race ./server/... # Server tests with race detector go test -v -race ./internal/... # Internal package tests with race detector go test -v -run TestFoo ./pkg/... # Run matching Go tests -go mod tidy -go=1.26.2 # Match CI tidy check +go mod tidy -go=1.27.0 # Match CI tidy check golangci-lint run # Go lint, config: .golangci.yaml golangci-lint run --fix # Auto-fix lint, including goimports @@ -119,7 +119,7 @@ cd proto && buf format -w # Format proto files ## CI Reference -- Backend CI: Go 1.26.2, `go mod tidy -go=1.26.2`, golangci-lint v2.11.3, test groups `store`, `server`, `internal`, `other`. +- Backend CI: Go 1.27.0, `go mod tidy -go=1.27.0`, golangci-lint v2.13.1, test groups `store`, `server`, `internal`, `other`. - Frontend CI: Node 24, pnpm 11.0.1, `pnpm lint`, `pnpm test`, `pnpm build`. - Proto CI: `buf lint` and `buf format` check. - Docker: `scripts/Dockerfile`, Alpine 3.21 runtime, non-root user, port 5230, multi-arch amd64/arm64/arm/v7. diff --git a/README.md b/README.md index 0841b942..189a83ca 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Open `http://localhost:5230` and start writing. ### Native Binary +Native macOS binaries require macOS 13 Ventura or later. + ```bash curl -fsSL https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh ``` diff --git a/go.mod b/go.mod index a857b4a1..263e9cf2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/usememos/memos -go 1.26.2 +go 1.27.0 require ( connectrpc.com/connect v1.20.0 @@ -9,10 +9,12 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.32.37 github.com/aws/aws-sdk-go-v2/credentials v1.19.36 github.com/aws/aws-sdk-go-v2/service/s3 v1.107.2 + github.com/aws/smithy-go v1.27.8 + github.com/disintegration/imaging v1.6.2 github.com/go-sql-driver/mysql v1.10.0 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/cel-go v0.31.0 github.com/google/jsonschema-go v0.4.3 - github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 github.com/johannesboyne/gofakes3 v1.2.0 @@ -38,10 +40,13 @@ require ( golang.org/x/net v0.58.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.22.0 + golang.org/x/text v0.41.0 google.golang.org/genai v1.68.0 google.golang.org/genproto v0.0.0-20260810153831-ec0a7760b754 google.golang.org/genproto/googleapis/api v0.0.0-20260810153831-ec0a7760b754 google.golang.org/grpc v1.83.0 + google.golang.org/protobuf v1.36.12 + gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.56.0 ) @@ -55,7 +60,19 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.18 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.37 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.30 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.37 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.38 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.5.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.33.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.45.6 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -63,6 +80,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.8.1 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -76,6 +94,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect github.com/googleapis/gax-go/v2 v2.23.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect @@ -83,6 +102,7 @@ require ( github.com/klauspost/compress v1.19.2 // indirect github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 // indirect github.com/magiconair/properties v1.18.11 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.3.3 // indirect github.com/moby/moby/client v0.5.1 // indirect @@ -95,6 +115,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20260805114148-88456608a4f6 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect @@ -124,6 +145,8 @@ require ( go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297 // indirect golang.org/x/image v0.45.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.49.0 // indirect google.golang.org/api v0.293.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260810153831-ec0a7760b754 // indirect @@ -131,29 +154,3 @@ require ( modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.12.0 // indirect ) - -require ( - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.18 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.37 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.30 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.37 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.38 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.33.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.45.6 // indirect - github.com/aws/smithy-go v1.27.8 - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/disintegration/imaging v1.6.2 - github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/mattn/go-isatty v0.0.24 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.41.0 - golang.org/x/time v0.15.0 // indirect - google.golang.org/protobuf v1.36.12 - gopkg.in/yaml.v3 v3.0.1 -) diff --git a/internal/email/README.md b/internal/email/README.md index 7e10f3a0..0486b4c7 100644 --- a/internal/email/README.md +++ b/internal/email/README.md @@ -410,7 +410,7 @@ email.SendAsync(config, message) ### Required -- **Go 1.25+** +- **Go 1.27+** - Standard library: `net/smtp`, `crypto/tls` - `github.com/pkg/errors` - Error wrapping with context diff --git a/internal/util/util.go b/internal/util/util.go index cdb211fb..43e7dccf 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -6,8 +6,7 @@ import ( "net/mail" "strconv" "strings" - - "github.com/google/uuid" + "uuid" ) // ConvertStringToInt32 converts a string to int32. @@ -38,7 +37,7 @@ func ValidateEmail(email string) bool { } func GenUUID() string { - return uuid.New().String() + return uuid.NewV4().String() } var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") diff --git a/internal/webhook/webhook.go b/internal/webhook/webhook.go index b3df1bd4..f640d468 100644 --- a/internal/webhook/webhook.go +++ b/internal/webhook/webhook.go @@ -15,8 +15,8 @@ import ( "strconv" "strings" "time" + "uuid" - "github.com/google/uuid" "github.com/pkg/errors" v1pb "github.com/usememos/memos/proto/gen/api/v1" @@ -136,7 +136,7 @@ func Post(requestPayload *WebhookRequestPayload) error { return errors.Wrapf(err, "failed to derive signing key for webhook to %s", requestPayload.URL) } - msgID := "msg_" + uuid.New().String() + msgID := "msg_" + uuid.NewV4().String() timestamp := strconv.FormatInt(time.Now().Unix(), 10) mac := hmac.New(sha256.New, key) diff --git a/scripts/Dockerfile b/scripts/Dockerfile index bedefa57..0c1dbd32 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.26.2-alpine AS backend +FROM --platform=$BUILDPLATFORM golang:1.27.0-alpine AS backend WORKDIR /backend-build # Install build dependencies diff --git a/server/auth/token.go b/server/auth/token.go index 917064f7..0cfc7113 100644 --- a/server/auth/token.go +++ b/server/auth/token.go @@ -135,17 +135,15 @@ func GenerateAccessTokenV2(userID int32, username, role, status string, secret [ expiresAt := time.Now().Add(AccessTokenDuration) claims := &AccessTokenClaims{ - Type: "access", - Role: role, - Status: status, - Username: username, - RegisteredClaims: jwt.RegisteredClaims{ - Issuer: Issuer, - Audience: jwt.ClaimStrings{AccessTokenAudienceName}, - Subject: fmt.Sprint(userID), - IssuedAt: jwt.NewNumericDate(time.Now()), - ExpiresAt: jwt.NewNumericDate(expiresAt), - }, + Type: "access", + Role: role, + Status: status, + Username: username, + Issuer: Issuer, + Audience: jwt.ClaimStrings{AccessTokenAudienceName}, + Subject: fmt.Sprint(userID), + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(expiresAt), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) @@ -164,15 +162,13 @@ func GenerateRefreshToken(userID int32, tokenID string, secret []byte) (string, expiresAt := time.Now().Add(RefreshTokenDuration) claims := &RefreshTokenClaims{ - Type: "refresh", - TokenID: tokenID, - RegisteredClaims: jwt.RegisteredClaims{ - Issuer: Issuer, - Audience: jwt.ClaimStrings{RefreshTokenAudienceName}, - Subject: fmt.Sprint(userID), - IssuedAt: jwt.NewNumericDate(time.Now()), - ExpiresAt: jwt.NewNumericDate(expiresAt), - }, + Type: "refresh", + TokenID: tokenID, + Issuer: Issuer, + Audience: jwt.ClaimStrings{RefreshTokenAudienceName}, + Subject: fmt.Sprint(userID), + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(expiresAt), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) diff --git a/server/router/api/v1/memo_attachment_service.go b/server/router/api/v1/memo_attachment_service.go index 9379bfab..110acdbd 100644 --- a/server/router/api/v1/memo_attachment_service.go +++ b/server/router/api/v1/memo_attachment_service.go @@ -386,9 +386,9 @@ func (s *APIV1Service) extractManagedAttachmentReferences(content string) ([]mar } func sameURLAuthority(candidate, instance *url.URL) bool { - withScheme := *candidate + withScheme := candidate.Clone() withScheme.Scheme = instance.Scheme - return sameURLOrigin(&withScheme, instance) + return sameURLOrigin(withScheme, instance) } func sameURLOrigin(a, b *url.URL) bool { diff --git a/server/router/api/v1/test/auth_service_sso_username_test.go b/server/router/api/v1/test/auth_service_sso_username_test.go index a6c36724..f8c83f1f 100644 --- a/server/router/api/v1/test/auth_service_sso_username_test.go +++ b/server/router/api/v1/test/auth_service_sso_username_test.go @@ -5,8 +5,8 @@ import ( "strings" "sync" "testing" + "uuid" - "github.com/google/uuid" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/server/router/api/v1/test/sse_handler_test.go b/server/router/api/v1/test/sse_handler_test.go index ff01e1aa..0f5cd896 100644 --- a/server/router/api/v1/test/sse_handler_test.go +++ b/server/router/api/v1/test/sse_handler_test.go @@ -80,8 +80,8 @@ func TestSSEHandler_Authentication(t *testing.T) { }) t.Run("valid token streams initial comment and event", func(t *testing.T) { - server := httptest.NewServer(e) - defer server.Close() + server := httptest.NewTestServer(t, e) + client := server.Client() reqCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() @@ -89,7 +89,7 @@ func TestSSEHandler_Authentication(t *testing.T) { require.NoError(t, err) req.Header.Set("Authorization", "Bearer "+token) - resp, err := server.Client().Do(req) + resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() require.Equal(t, http.StatusOK, resp.StatusCode) @@ -117,14 +117,14 @@ func TestSSEHandler_Authentication(t *testing.T) { }) t.Run("hub close disconnects stream", func(t *testing.T) { - server := httptest.NewServer(e) - defer server.Close() + server := httptest.NewTestServer(t, e) + client := server.Client() req, err := http.NewRequestWithContext(ctx, http.MethodGet, server.URL+"/api/v1/sse", nil) require.NoError(t, err) req.Header.Set("Authorization", "Bearer "+token) - resp, err := server.Client().Do(req) //nolint:bodyclose // Body is closed after verifying the SSE stream disconnects. + resp, err := client.Do(req) //nolint:bodyclose // Body is closed after verifying the SSE stream disconnects. if err != nil { t.Fatal(err) } diff --git a/server/router/api/v1/v1.go b/server/router/api/v1/v1.go index 3e878f89..8207b2d0 100644 --- a/server/router/api/v1/v1.go +++ b/server/router/api/v1/v1.go @@ -9,7 +9,6 @@ import ( "github.com/labstack/echo/v5" "github.com/pkg/errors" "golang.org/x/sync/semaphore" - "google.golang.org/protobuf/encoding/protojson" "github.com/usememos/memos/internal/httpgetter" "github.com/usememos/memos/internal/markdown" @@ -85,12 +84,8 @@ func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store func newGatewayMarshaler() *runtime.HTTPBodyMarshaler { return &runtime.HTTPBodyMarshaler{ Marshaler: &runtime.JSONPb{ - MarshalOptions: protojson.MarshalOptions{ - EmitDefaultValues: true, - }, - UnmarshalOptions: protojson.UnmarshalOptions{ - DiscardUnknown: true, - }, + EmitDefaultValues: true, + DiscardUnknown: true, }, } } diff --git a/server/server.go b/server/server.go index 9ebd4463..cad3cf31 100644 --- a/server/server.go +++ b/server/server.go @@ -8,8 +8,8 @@ import ( "net/http" "os" "time" + "uuid" - "github.com/google/uuid" "github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware" "github.com/pkg/errors" @@ -168,7 +168,7 @@ func (s *Server) getOrUpsertInstanceBasicSetting(ctx context.Context) (*storepb. } modified := false if instanceBasicSetting.SecretKey == "" { - instanceBasicSetting.SecretKey = uuid.NewString() + instanceBasicSetting.SecretKey = uuid.NewV4().String() modified = true } if modified {