A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go

Implements linter for pkg/hold

authored by Eduardo Cuducos and committed by tangled.org 2d5039d3 e0a2dda1

Changed files
+137 -26
.tangled
workflows
pkg
hold
pds
+11
.golangci.yml
··· 25 25 linters: 26 26 - errcheck 27 27 28 + # TODO: fix issues and remove these paths one by one 29 + - path: pkg/auth 30 + linters: 31 + - errcheck 32 + - path: pkg/appview 33 + linters: 34 + - errcheck 35 + - path: cmd/credential-helper 36 + linters: 37 + - errcheck 38 + 28 39 formatters: 29 40 enable: 30 41 - gofmt
+24
.tangled/workflows/lint.yaml
··· 1 + when: 2 + - event: ["push"] 3 + branch: ["*"] 4 + - event: ["pull_request"] 5 + branch: ["main"] 6 + 7 + engine: kubernetes 8 + image: golang:1.25-trixie 9 + architecture: amd64 10 + 11 + steps: 12 + - name: Download and Generate 13 + environment: 14 + CGO_ENABLED: 1 15 + command: | 16 + go mod download 17 + go generate ./... 18 + 19 + - name: Run Linter 20 + environment: 21 + CGO_ENABLED: 1 22 + command: | 23 + curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.7.2 24 + golangci-lint run ./...
+102 -26
pkg/hold/pds/xrpc.go
··· 207 207 } 208 208 209 209 w.Header().Set("Content-Type", "application/json") 210 - json.NewEncoder(w).Encode(response) 210 + if err := json.NewEncoder(w).Encode(response); err != nil { 211 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 212 + w.WriteHeader(http.StatusInternalServerError) 213 + } 211 214 } 212 215 213 216 // HandleDescribeServer returns server metadata ··· 227 230 } 228 231 229 232 w.Header().Set("Content-Type", "application/json") 230 - json.NewEncoder(w).Encode(response) 233 + if err := json.NewEncoder(w).Encode(response); err != nil { 234 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 235 + w.WriteHeader(http.StatusInternalServerError) 236 + } 231 237 } 232 238 233 239 // HandleResolveHandle resolves a handle to a DID ··· 255 261 } 256 262 257 263 w.Header().Set("Content-Type", "application/json") 258 - json.NewEncoder(w).Encode(response) 264 + if err := json.NewEncoder(w).Encode(response); err != nil { 265 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 266 + w.WriteHeader(http.StatusInternalServerError) 267 + } 259 268 } 260 269 261 270 // HandleGetProfile returns aggregated profile information ··· 290 299 response := h.buildProfileResponse(r.Context()) 291 300 292 301 w.Header().Set("Content-Type", "application/json") 293 - json.NewEncoder(w).Encode(response) 302 + if err := json.NewEncoder(w).Encode(response); err != nil { 303 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 304 + w.WriteHeader(http.StatusInternalServerError) 305 + } 294 306 } 295 307 296 308 // HandleGetProfiles returns aggregated profile information for multiple actors ··· 341 353 } 342 354 343 355 w.Header().Set("Content-Type", "application/json") 344 - json.NewEncoder(w).Encode(response) 356 + if err := json.NewEncoder(w).Encode(response); err != nil { 357 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 358 + w.WriteHeader(http.StatusInternalServerError) 359 + } 345 360 } 346 361 347 362 // buildProfileResponse builds a profile response map (shared by GetProfile and GetProfiles) ··· 435 450 } 436 451 437 452 w.Header().Set("Content-Type", "application/json") 438 - json.NewEncoder(w).Encode(response) 453 + if err := json.NewEncoder(w).Encode(response); err != nil { 454 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 455 + w.WriteHeader(http.StatusInternalServerError) 456 + } 439 457 } 440 458 441 459 // HandleGetRecord retrieves a record from the repository ··· 479 497 } 480 498 481 499 w.Header().Set("Content-Type", "application/json") 482 - json.NewEncoder(w).Encode(response) 500 + if err := json.NewEncoder(w).Encode(response); err != nil { 501 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 502 + w.WriteHeader(http.StatusInternalServerError) 503 + } 483 504 } 484 505 485 506 // HandleListRecords lists records in a collection ··· 551 572 // Empty repo, return empty list 552 573 response := map[string]any{"records": []any{}} 553 574 w.Header().Set("Content-Type", "application/json") 554 - json.NewEncoder(w).Encode(response) 575 + if err := json.NewEncoder(w).Encode(response); err != nil { 576 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 577 + w.WriteHeader(http.StatusInternalServerError) 578 + } 555 579 return 556 580 } 557 581 ··· 598 622 } 599 623 600 624 w.Header().Set("Content-Type", "application/json") 601 - json.NewEncoder(w).Encode(response) 625 + if err := json.NewEncoder(w).Encode(response); err != nil { 626 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 627 + w.WriteHeader(http.StatusInternalServerError) 628 + } 602 629 } 603 630 604 631 // handleListRecordsMST uses the legacy MST-based listing (fallback for tests) ··· 620 647 // Empty repo, return empty list 621 648 response := map[string]any{"records": []any{}} 622 649 w.Header().Set("Content-Type", "application/json") 623 - json.NewEncoder(w).Encode(response) 650 + if err := json.NewEncoder(w).Encode(response); err != nil { 651 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 652 + w.WriteHeader(http.StatusInternalServerError) 653 + } 624 654 return 625 655 } 626 656 ··· 729 759 } 730 760 731 761 w.Header().Set("Content-Type", "application/json") 732 - json.NewEncoder(w).Encode(response) 762 + if err := json.NewEncoder(w).Encode(response); err != nil { 763 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 764 + w.WriteHeader(http.StatusInternalServerError) 765 + } 733 766 } 734 767 735 768 // HandleDeleteRecord deletes a record from the repository ··· 799 832 if !currentCID.Equals(swapRecordCID) { 800 833 // Swap failed - record CID doesn't match 801 834 w.WriteHeader(http.StatusBadRequest) 802 - json.NewEncoder(w).Encode(map[string]any{ 835 + response := map[string]any{ 803 836 "error": "InvalidSwap", 804 837 "message": "record CID does not match swapRecord", 805 - }) 838 + } 839 + if err := json.NewEncoder(w).Encode(response); err != nil { 840 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 841 + w.WriteHeader(http.StatusInternalServerError) 842 + } 806 843 return 807 844 } 808 845 } ··· 846 883 } 847 884 848 885 w.Header().Set("Content-Type", "application/json") 849 - json.NewEncoder(w).Encode(response) 886 + if err := json.NewEncoder(w).Encode(response); err != nil { 887 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 888 + w.WriteHeader(http.StatusInternalServerError) 889 + } 850 890 } 851 891 852 892 // HandleSyncGetRecord returns a single record as a CAR file for sync ··· 900 940 } 901 941 902 942 // Write the CAR data to the response 903 - w.Write(buf.Bytes()) 943 + if _, err := w.Write(buf.Bytes()); err != nil { 944 + slog.Error("failed to write car to http response", "error", err, "path", r.URL.Path) 945 + w.WriteHeader(http.StatusInternalServerError) 946 + } 904 947 } 905 948 906 949 // HandleGetRepo returns the full repository as a CAR file ··· 1063 1106 } 1064 1107 1065 1108 w.Header().Set("Content-Type", "application/json") 1066 - json.NewEncoder(w).Encode(response) 1109 + if err := json.NewEncoder(w).Encode(response); err != nil { 1110 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1111 + w.WriteHeader(http.StatusInternalServerError) 1112 + } 1067 1113 } 1068 1114 1069 1115 // HandleGetBlob routes blob requests to appropriate handlers based on blob type ··· 1139 1185 "url": presignedURL, 1140 1186 } 1141 1187 w.Header().Set("Content-Type", "application/json") 1142 - json.NewEncoder(w).Encode(response) 1188 + if err := json.NewEncoder(w).Encode(response); err != nil { 1189 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1190 + w.WriteHeader(http.StatusInternalServerError) 1191 + } 1143 1192 } 1144 1193 1145 1194 // handleGetATProtoBlob handles standard ATProto blob requests ··· 1193 1242 "repos": []any{}, 1194 1243 } 1195 1244 w.Header().Set("Content-Type", "application/json") 1196 - json.NewEncoder(w).Encode(response) 1245 + if err := json.NewEncoder(w).Encode(response); err != nil { 1246 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1247 + w.WriteHeader(http.StatusInternalServerError) 1248 + } 1197 1249 return 1198 1250 } 1199 1251 ··· 1205 1257 "repos": []any{}, 1206 1258 } 1207 1259 w.Header().Set("Content-Type", "application/json") 1208 - json.NewEncoder(w).Encode(response) 1260 + if err := json.NewEncoder(w).Encode(response); err != nil { 1261 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1262 + w.WriteHeader(http.StatusInternalServerError) 1263 + } 1209 1264 return 1210 1265 } 1211 1266 ··· 1223 1278 } 1224 1279 1225 1280 w.Header().Set("Content-Type", "application/json") 1226 - json.NewEncoder(w).Encode(response) 1281 + if err := json.NewEncoder(w).Encode(response); err != nil { 1282 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1283 + w.WriteHeader(http.StatusInternalServerError) 1284 + } 1227 1285 } 1228 1286 1229 1287 // HandleGetRepoStatus returns the hosting status for a repository ··· 1252 1310 "active": true, 1253 1311 } 1254 1312 w.Header().Set("Content-Type", "application/json") 1255 - json.NewEncoder(w).Encode(response) 1313 + if err := json.NewEncoder(w).Encode(response); err != nil { 1314 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1315 + w.WriteHeader(http.StatusInternalServerError) 1316 + } 1256 1317 return 1257 1318 } 1258 1319 ··· 1264 1325 } 1265 1326 1266 1327 w.Header().Set("Content-Type", "application/json") 1267 - json.NewEncoder(w).Encode(response) 1328 + if err := json.NewEncoder(w).Encode(response); err != nil { 1329 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1330 + w.WriteHeader(http.StatusInternalServerError) 1331 + } 1268 1332 } 1269 1333 1270 1334 // HandleDIDDocument returns the DID document ··· 1276 1340 } 1277 1341 1278 1342 w.Header().Set("Content-Type", "application/json") 1279 - json.NewEncoder(w).Encode(doc) 1343 + if err := json.NewEncoder(w).Encode(doc); err != nil { 1344 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1345 + w.WriteHeader(http.StatusInternalServerError) 1346 + } 1280 1347 } 1281 1348 1282 1349 // HandleAtprotoDID returns the DID for handle resolution ··· 1375 1442 } 1376 1443 w.Header().Set("Content-Type", "application/json") 1377 1444 w.WriteHeader(http.StatusOK) 1378 - json.NewEncoder(w).Encode(response) 1445 + if err := json.NewEncoder(w).Encode(response); err != nil { 1446 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1447 + w.WriteHeader(http.StatusInternalServerError) 1448 + } 1379 1449 return 1380 1450 } 1381 1451 } ··· 1408 1478 1409 1479 w.Header().Set("Content-Type", "application/json") 1410 1480 w.WriteHeader(http.StatusCreated) 1411 - json.NewEncoder(w).Encode(response) 1481 + if err := json.NewEncoder(w).Encode(response); err != nil { 1482 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1483 + w.WriteHeader(http.StatusInternalServerError) 1484 + } 1412 1485 } 1413 1486 1414 1487 // GetPresignedURL generates a presigned URL for GET, HEAD, or PUT operations ··· 1546 1619 } 1547 1620 1548 1621 w.Header().Set("Content-Type", "application/json") 1549 - json.NewEncoder(w).Encode(stats) 1622 + if err := json.NewEncoder(w).Encode(stats); err != nil { 1623 + slog.Error("failed to encode json to http response", "error", err, "path", r.URL.Path) 1624 + w.WriteHeader(http.StatusInternalServerError) 1625 + } 1550 1626 }