123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
// Copyright (C) 2022 The pages authors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/go-git/go-billy/v5"
|
|
"github.com/go-git/go-billy/v5/osfs"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/mjpitz/myago/zaputil"
|
|
)
|
|
|
|
// Config encapsulates the elements that can be configured about the git service.
|
|
type Config struct {
|
|
URL string `json:"url" usage:"the git url used to clone the repository"`
|
|
Branch string `json:"branch" usage:"the name of the git branch to clone"`
|
|
Tag string `json:"tag" usage:"the name of the git tag to clone"`
|
|
Username string `json:"username" usage:"the username used to authenticate with the git service"`
|
|
Password string `json:"password" usage:"the password used to authenticate with the git service"`
|
|
SyncInterval time.Duration `json:"sync_interval" usage:"how frequently the git repository is pulled for changes" default:"1h"`
|
|
}
|
|
|
|
// NewService constructs a Service that manages the underlying git repository.
|
|
func NewService(config Config) (*Service, error) {
|
|
temp, err := os.MkdirTemp(os.TempDir(), "pages-*")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
options := &git.CloneOptions{
|
|
URL: config.URL,
|
|
}
|
|
|
|
if config.Username != "" && config.Password != "" {
|
|
options.Auth = &http.BasicAuth{
|
|
Username: config.Username,
|
|
Password: config.Password,
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case config.Tag != "":
|
|
options.ReferenceName = plumbing.NewTagReferenceName(config.Tag)
|
|
case config.Branch != "":
|
|
options.ReferenceName = plumbing.NewBranchReferenceName(config.Branch)
|
|
}
|
|
|
|
return &Service{
|
|
options: options,
|
|
Store: memory.NewStorage(),
|
|
FS: osfs.New(temp),
|
|
}, nil
|
|
}
|
|
|
|
// Service encapsulates operations that can be performed against the target git repository.
|
|
type Service struct {
|
|
options *git.CloneOptions
|
|
Store *memory.Storage
|
|
FS billy.Filesystem
|
|
Repository *git.Repository
|
|
}
|
|
|
|
// Load initializes the git repository given the provided options. This _should_ only be called once.
|
|
func (s *Service) Load(ctx context.Context) (err error) {
|
|
zaputil.Extract(ctx).Info("cloning", zap.String("url", s.options.URL))
|
|
|
|
s.Repository, err = git.CloneContext(ctx, s.Store, s.FS, s.options)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to clone repository")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Sync pulls the underlying repository to ensure it's up-to-date.
|
|
func (s *Service) Sync(ctx context.Context) error {
|
|
zaputil.Extract(ctx).Info("synchronizing", zap.String("url", s.options.URL))
|
|
|
|
wt, err := s.Repository.Worktree()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to obtain worktree")
|
|
}
|
|
|
|
err = wt.PullContext(ctx, &git.PullOptions{
|
|
ReferenceName: s.options.ReferenceName,
|
|
SingleBranch: s.options.SingleBranch,
|
|
Depth: s.options.Depth,
|
|
Auth: s.options.Auth,
|
|
Force: true,
|
|
})
|
|
|
|
switch {
|
|
case errors.Is(err, git.NoErrAlreadyUpToDate):
|
|
case err != nil:
|
|
zaputil.Extract(ctx).Error("failed to pull", zap.Error(err))
|
|
}
|
|
|
|
return nil
|
|
}
|