@@ -14,7 +14,6 @@ import (
1414
1515 "github.com/pkg/browser"
1616
17- "github.com/sourcegraph/sourcegraph/lib/accesstoken"
1817 "github.com/sourcegraph/sourcegraph/lib/codeintel/upload"
1918 "github.com/sourcegraph/sourcegraph/lib/errors"
2019 "github.com/sourcegraph/sourcegraph/lib/output"
@@ -71,7 +70,7 @@ func handleCodeIntelUpload(args []string) error {
7170 }
7271 }
7372 if err != nil {
74- return handleUploadError (cfg . AccessToken , err )
73+ return handleUploadError (err )
7574 }
7675
7776 client := cfg .apiClient (codeintelUploadFlags .apiFlags , io .Discard )
@@ -84,7 +83,7 @@ func handleCodeIntelUpload(args []string) error {
8483 uploadID , err = UploadUncompressedIndex (ctx , codeintelUploadFlags .file , client , uploadOptions )
8584 }
8685 if err != nil {
87- return handleUploadError (uploadOptions . SourcegraphInstanceOptions . AccessToken , err )
86+ return handleUploadError (err )
8887 }
8988
9089 uploadURL , err := makeCodeIntelUploadURL (uploadID )
@@ -216,9 +215,30 @@ func (e errorWithHint) Error() string {
216215// given output object is nil then the error will be written to standard out.
217216//
218217// This method returns the error that should be passed back up to the runner.
219- func handleUploadError (accessToken string , err error ) error {
220- if errors .Is (err , upload .ErrUnauthorized ) {
221- err = attachHintsForAuthorizationError (accessToken , err )
218+ func handleUploadError (err error ) error {
219+ if errors .Is (err , ErrUnauthorized ) {
220+ serverMessage := serverMessageFromError (err )
221+ if serverMessage != "" {
222+ err = errorWithHint {
223+ err : errors .Newf ("upload rejected by server: %s" , serverMessage ),
224+ hint : codeHostTokenHint (),
225+ }
226+ } else {
227+ err = errorWithHint {
228+ err : errors .New ("upload failed: unauthorized (check your Sourcegraph access token)" ),
229+ hint : "To create a Sourcegraph access token, see https://sourcegraph.com/docs/cli/how-tos/creating_an_access_token." ,
230+ }
231+ }
232+ } else if strings .Contains (err .Error (), "unexpected status code: 403" ) {
233+ serverMessage := serverMessageFrom403Error (err )
234+ displayMsg := "upload failed: forbidden"
235+ if serverMessage != "" {
236+ displayMsg = fmt .Sprintf ("upload rejected by server: %s" , serverMessage )
237+ }
238+ err = errorWithHint {
239+ err : errors .New (displayMsg ),
240+ hint : codeHostTokenHint (),
241+ }
222242 }
223243
224244 if codeintelUploadFlags .ignoreUploadFailures {
@@ -230,76 +250,49 @@ func handleUploadError(accessToken string, err error) error {
230250 return err
231251}
232252
233- func attachHintsForAuthorizationError (accessToken string , originalError error ) error {
234- var actionableHints []string
235-
236- likelyTokenError := accessToken == ""
237- if _ , parseErr := accesstoken .ParsePersonalAccessToken (accessToken ); accessToken != "" && parseErr != nil {
238- likelyTokenError = true
239- actionableHints = append (actionableHints ,
240- "However, the provided access token does not match expected format; was it truncated?" ,
241- "Typically the access token looks like sgp_<40 hex chars> or sgp_<instance-id>_<40 hex chars>." )
253+ // serverMessageFromError extracts the detail string from a wrapped ErrUnauthorized.
254+ // Returns "" if the error is the bare sentinel (no server message).
255+ func serverMessageFromError (err error ) string {
256+ msg := err .Error ()
257+ sentinel := ErrUnauthorized .Error ()
258+ if msg == sentinel {
259+ return ""
242260 }
243-
244- if likelyTokenError {
245- return errorWithHint {err : originalError , hint : strings .Join (mergeStringSlices (
246- []string {"A Sourcegraph access token must be provided via SRC_ACCESS_TOKEN for uploading SCIP data." },
247- actionableHints ,
248- []string {"For more details, see https://sourcegraph.com/docs/cli/how-tos/creating_an_access_token." },
249- ), "\n " )}
261+ // errors.Wrap produces "detail: sentinel", so strip the suffix.
262+ if strings .HasSuffix (msg , ": " + sentinel ) {
263+ return strings .TrimSuffix (msg , ": " + sentinel )
250264 }
265+ return ""
266+ }
251267
252- needsGitHubToken := strings .HasPrefix (codeintelUploadFlags .repo , "github.com" )
253- needsGitLabToken := strings .HasPrefix (codeintelUploadFlags .repo , "gitlab.com" )
254-
255- if needsGitHubToken {
256- if codeintelUploadFlags .gitHubToken != "" {
257- actionableHints = append (actionableHints ,
258- fmt .Sprintf ("The supplied -github-token does not indicate that you have collaborator access to %s." , codeintelUploadFlags .repo ),
259- "Please check the value of the supplied token and its permissions on the code host and try again." ,
260- )
261- } else {
262- actionableHints = append (actionableHints ,
263- fmt .Sprintf ("Please retry your request with a -github-token=XXX with collaborator access to %s." , codeintelUploadFlags .repo ),
264- "This token will be used to check with the code host that the uploading user has write access to the target repository." ,
265- )
266- }
267- } else if needsGitLabToken {
268- if codeintelUploadFlags .gitLabToken != "" {
269- actionableHints = append (actionableHints ,
270- fmt .Sprintf ("The supplied -gitlab-token does not indicate that you have write access to %s." , codeintelUploadFlags .repo ),
271- "Please check the value of the supplied token and its permissions on the code host and try again." ,
272- )
273- } else {
274- actionableHints = append (actionableHints ,
275- fmt .Sprintf ("Please retry your request with a -gitlab-token=XXX with write access to %s." , codeintelUploadFlags .repo ),
276- "This token will be used to check with the code host that the uploading user has write access to the target repository." ,
277- )
278- }
279- } else {
280- actionableHints = append (actionableHints ,
281- "Verification is supported for the following code hosts: github.com, gitlab.com." ,
282- "Please request support for additional code host verification at https://github.com/sourcegraph/sourcegraph/issues/4967." ,
283- )
268+ // serverMessageFrom403Error extracts the server body from a 403 error.
269+ // The vendored code formats these as "unexpected status code: 403 (body)".
270+ func serverMessageFrom403Error (err error ) string {
271+ msg := err .Error ()
272+ prefix := "unexpected status code: 403 ("
273+ if strings .HasPrefix (msg , prefix ) && strings .HasSuffix (msg , ")" ) {
274+ return msg [len (prefix ) : len (msg )- 1 ]
284275 }
276+ return ""
277+ }
285278
286- return errorWithHint {err : originalError , hint : strings .Join (mergeStringSlices (
287- []string {"This Sourcegraph instance has enforced auth for SCIP uploads." },
288- actionableHints ,
289- []string {"For more details, see https://docs.sourcegraph.com/cli/references/code-intel/upload." },
290- ), "\n " )}
279+ // codeHostTokenHint returns documentation links relevant to the upload failure.
280+ // Always includes the general upload docs, and adds code-host-specific token
281+ // documentation if the user supplied a code host token flag.
282+ func codeHostTokenHint () string {
283+ hint := "For more details on uploading SCIP indexes, see https://sourcegraph.com/docs/cli/references/code-intel/upload."
284+ if codeintelUploadFlags .gitHubToken != "" {
285+ hint += "\n If the issue is related to your GitHub token, see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens."
286+ }
287+ if codeintelUploadFlags .gitLabToken != "" {
288+ hint += "\n If the issue is related to your GitLab token, see https://docs.gitlab.com/user/profile/personal_access_tokens/."
289+ }
290+ return hint
291291}
292292
293293// emergencyOutput creates a default Output object writing to standard out.
294294func emergencyOutput () * output.Output {
295295 return output .NewOutput (os .Stdout , output.OutputOpts {})
296296}
297297
298- func mergeStringSlices (ss ... []string ) []string {
299- var combined []string
300- for _ , s := range ss {
301- combined = append (combined , s ... )
302- }
303298
304- return combined
305- }
0 commit comments