func (gh *Storage) readIndex() (map[string]int64, string, error) {
	index := map[string]int64{}
	contents, sha, err := gh.readFile(fs.IndexName)
	if errors.Is(err, errFileNotFound) {
		return index, "", nil
	}
	if err != nil {
		return nil, "", err
	}
	err = json.Unmarshal(contents, &index)
	return index, sha, err
}
readIndex reads the index JSON from the Git repo into a map.
It returns the populated map & the Git SHA associated with the contents.
If the index file is not found in the Git repo, an empty index is returned with no error.
If any error occurs, a nil index and empty SHA are returned along with the error.
package github
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"path/filepath"
	"strings"
	"time"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"github.com/sourcegraph/checkup/storage/fs"
	"github.com/sourcegraph/checkup/types"
)
const Type = "github"
var errFileNotFound = fmt.Errorf("file not found on github")
type Storage struct {
	AccessToken string `json:"access_token"`
	RepositoryOwner string `json:"repository_owner"`
	RepositoryName string `json:"repository_name"`
	CommitterName string `json:"committer_name"`
	CommitterEmail string `json:"committer_email"`
	Branch string `json:"branch"`
	Dir string `json:"dir"`
	CheckExpiry time.Duration `json:"check_expiry,omitempty"`
	client *github.Client
}
func New(config json.RawMessage) (*Storage, error) {
	storage := new(Storage)
	err := json.Unmarshal(config, &storage)
	return storage, err
}
func (Storage) Type() string {
	return Type
}
func (gh *Storage) ensureClient() error {
	if gh.client != nil {
		return nil
	}
	if gh.AccessToken == "" {
		return fmt.Errorf("Please specify access_token in storage configuration")
	}
	gh.client = github.NewClient(oauth2.NewClient(
		context.Background(),
		oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: gh.AccessToken},
		),
	))
	return nil
}
func (gh *Storage) fullPathName(filename string) string {
	if strings.HasPrefix(filename, gh.Dir) {
		return filename
	}
	return filepath.Join(gh.Dir, filename)
}
func (gh *Storage) readFile(filename string) ([]byte, string, error) {
	if err := gh.ensureClient(); err != nil {
		return nil, "", err
	}
	contents, _, resp, err := gh.client.Repositories.GetContents(
		context.Background(),
		gh.RepositoryOwner,
		gh.RepositoryName,
		gh.fullPathName(filename),
		&github.RepositoryContentGetOptions{Ref: "heads/" + gh.Branch},
	)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusNotFound {
			return nil, "", errFileNotFound
		}
		return nil, "", err
	}
	decoded, err := contents.GetContent()
	return []byte(decoded), *contents.SHA, err
}
func (gh *Storage) writeFile(filename string, sha string, contents []byte) error {
	if err := gh.ensureClient(); err != nil {
		return err
	}
	var err error
	var writeFunc func(context.Context, string, string, string, *github.RepositoryContentFileOptions) (*github.RepositoryContentResponse, *github.Response, error)
	opts := &github.RepositoryContentFileOptions{
		Message: github.String(fmt.Sprintf("[checkup] store %s [ci skip]", gh.fullPathName(filename))),
		Content: contents,
		Committer: &github.CommitAuthor{
			Name:  &gh.CommitterName,
			Email: &gh.CommitterEmail,
		},
	}
	if gh.Branch != "" {
		opts.Branch = &gh.Branch
	}
	if sha == "" {
		writeFunc = gh.client.Repositories.CreateFile
		log.Printf("github: creating %s on branch '%s'", gh.fullPathName(filename), gh.Branch)
	} else {
		opts.SHA = github.String(sha)
		writeFunc = gh.client.Repositories.UpdateFile
		log.Printf("github: updating %s on branch '%s'", gh.fullPathName(filename), gh.Branch)
	}
	_, _, err = writeFunc(
		context.Background(),
		gh.RepositoryOwner,
		gh.RepositoryName,
		gh.fullPathName(filename),
		opts,
	)
	return err
}
func (gh *Storage) deleteFile(filename string, sha string) error {
	if err := gh.ensureClient(); err != nil {
		return err
	}
	if sha == "" {
		return errFileNotFound
	}
	log.Printf("github: deleting %s on branch '%s'", gh.fullPathName(filename), gh.Branch)
	_, _, err := gh.client.Repositories.DeleteFile(
		context.Background(),
		gh.RepositoryOwner,
		gh.RepositoryName,
		gh.fullPathName(filename),
		&github.RepositoryContentFileOptions{
			Message: github.String(fmt.Sprintf("[checkup] delete %s [ci skip]", gh.fullPathName(filename))),
			SHA:     github.String(sha),
			Branch:  &gh.Branch,
			Committer: &github.CommitAuthor{
				Name:  &gh.CommitterName,
				Email: &gh.CommitterEmail,
			},
		},
	)
	if err != nil {
		return err
	}
	return nil
}