Repository: google/go-github
File: github/repos_releases.go (Go)

Code before:
//meta:operation POST /repos/{owner}/{repo}/releases/{release_id}/assets
func (s *RepositoriesService) UploadReleaseAssetFromRelease(ctx context.Context, release *RepositoryRelease, opts *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) {
	if release == nil || release.UploadURL == nil {
		return nil, nil, errors.New("release UploadURL must be provided")
	}
	if file == nil {
		return nil, nil, errors.New("file must be provided")
	}

	// Extract upload URL.
	uploadURL := *release.UploadURL

	// If uploadURL contains a template, strip it (e.g. "{?name,label}").
	if idx := strings.Index(uploadURL, "{"); idx != -1 {
		uploadURL = uploadURL[:idx]
	}

	// If uploadURL is absolute (starts with http/https), parse and use only the path.
	if strings.HasPrefix(uploadURL, "http://") || strings.HasPrefix(uploadURL, "https://") {
		if uParsed, err := url.Parse(uploadURL); err == nil {
			uploadURL = uParsed.Path
		}
	}

	// Defensive: always remove any leading '/' so client gets a relative path.
	uploadURL = strings.TrimPrefix(uploadURL, "/")

	// addOptions will append query params for name/label (same as UploadReleaseAsset)
	u, err := addOptions(uploadURL, opts)
	if err != nil {
		return nil, nil, err
	}

	stat, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}
	if stat.IsDir() {
		return nil, nil, errors.New("the asset to upload can't be a directory")
	}

	mediaType := mime.TypeByExtension(filepath.Ext(file.Name()))
	if opts != nil && opts.MediaType != "" {
		mediaType = opts.MediaType
	}

	req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType)
	if err != nil {
		return nil, nil, err
	}


Review comment (suggestion):
I was not expecting the conversion of the upload URL to a relative URL - that would make the entire point of using the upload URL from the release needlessly depend on the client upload API URL which might have been set with `Client.WithEnterpriseURLs()`. 

My intention for the feature request was also to remove the need to set the upload API endpoint manually - since the GitHub core API is easily discoverable in GitHub Actions context, but the upload API is not directly discoverable (which it doesn't need to be, since it is defined by the Release the upload is for).

Code after:
func (s *RepositoriesService) UploadReleaseAssetFromRelease(
	ctx context.Context,
	release *RepositoryRelease,
	opts *UploadOptions,
	reader io.Reader,
	size int64,
) (*ReleaseAsset, *Response, error) {
	if release == nil || release.UploadURL == nil {
		return nil, nil, errors.New("release UploadURL must be provided")
	}
	if reader == nil {
		return nil, nil, errors.New("reader must be provided")
	}
	if size < 0 {
		return nil, nil, errors.New("size must be >= 0")
	}

	// Strip URI-template portion (e.g. "{?name,label}") if present.
	uploadURL := *release.UploadURL
	if idx := strings.Index(uploadURL, "{"); idx != -1 {
		uploadURL = uploadURL[:idx]
	}

	// If this is a *relative* URL (no scheme), normalize it by trimming a leading "/"
	// so it works with Client.BaseURL path prefixes (e.g. "/api-v3/").
	if !strings.HasPrefix(uploadURL, "http://") && !strings.HasPrefix(uploadURL, "https://") {
		uploadURL = strings.TrimPrefix(uploadURL, "/")
	}

	// addOptions will append name/label query params (same behavior as UploadReleaseAsset).
	u, err := addOptions(uploadURL, opts)
	if err != nil {
		return nil, nil, err
	}

	// determine media type
	mediaType := defaultMediaType
	if opts != nil {
		switch {
		case opts.MediaType != "":
			mediaType = opts.MediaType
		case opts.Name != "":
			if ext := filepath.Ext(opts.Name); ext != "" {
				if mt := mime.TypeByExtension(ext); mt != "" {
					mediaType = mt
				}
			}
		}
	}

	req, err := s.client.NewUploadRequest(u, reader, size, mediaType)