Skip to content

Commit

Permalink
Remove legacy git code (ver < 2.0), fine tune markup tests (#19930)
Browse files Browse the repository at this point in the history
* clean git support for ver < 2.0

* fine tune tests for markup (which requires git module)

* remove unnecessary comments

* try to fix tests

* try test again

* use const for GitVersionRequired instead of var

* try to fix integration test

* Refactor CheckAttributeReader to make a *git.Repository version

* update document for commit signing with Gitea's internal gitconfig

* update document for commit signing with Gitea's internal gitconfig

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
  • Loading branch information
3 people authored Jun 16, 2022
1 parent 70ce051 commit 157b405
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 227 deletions.
4 changes: 2 additions & 2 deletions cmd/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ func runHookPostReceive(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()

setup("hooks/post-receive.log", c.Bool("debug"))

// First of all run update-server-info no matter what
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
Expand All @@ -318,8 +320,6 @@ func runHookPostReceive(c *cli.Context) error {
return nil
}

setup("hooks/post-receive.log", c.Bool("debug"))

if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
return fail(`Rejecting changes as Gitea environment not set.
Expand Down
15 changes: 10 additions & 5 deletions docs/content/doc/advanced/signing.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ The first option to discuss is the `SIGNING_KEY`. There are three main
options:

- `none` - this prevents Gitea from signing any commits
- `default` - Gitea will default to the key configured within
`git config`
- `default` - Gitea will default to the key configured within `git config`
- `KEYID` - Gitea will sign commits with the gpg key with the ID
`KEYID`. In this case you should provide a `SIGNING_NAME` and
`SIGNING_EMAIL` to be displayed for this key.
Expand All @@ -98,6 +97,12 @@ repositories, `SIGNING_KEY=default` could be used to provide different
signing keys on a per-repository basis. However, this is clearly not an
ideal UI and therefore subject to change.

**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`.
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
or the Gitea internal git config `{[repository].ROOT}/.gitconfig`.
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`.


### `INITIAL_COMMIT`

This option determines whether Gitea should sign the initial commit
Expand All @@ -118,7 +123,7 @@ The possible values are:

- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `parentsigned`: Only sign if the parent commit is signed.
- `always`: Always sign

Expand All @@ -132,7 +137,7 @@ editor or API CRUD actions. The possible values are:

- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `parentsigned`: Only sign if the parent commit is signed.
- `always`: Always sign

Expand All @@ -146,7 +151,7 @@ The possible options are:

- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `basesigned`: Only sign if the parent commit in the base repo is signed.
- `headsigned`: Only sign if the head commit in the head branch is signed.
- `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
Expand Down
9 changes: 7 additions & 2 deletions integrations/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,12 @@ func initIntegrationTest() {
setting.LoadForTest()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
_ = util.RemoveAll(repo_module.LocalCopyPath())

if err := git.InitOnceWithSync(context.Background()); err != nil {
log.Fatal("git.InitOnceWithSync: %v", err)
}
git.CheckLFSVersion()

setting.InitDBConfig()
if err := storage.Init(); err != nil {
fmt.Printf("Init storage failed: %v", err)
Expand Down Expand Up @@ -275,7 +280,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down Expand Up @@ -576,7 +581,7 @@ func resetFixtures(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down
2 changes: 1 addition & 1 deletion integrations/migration-test/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ func initMigrationTest(t *testing.T) func() {
assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand All @@ -83,6 +82,7 @@ func initMigrationTest(t *testing.T) func() {
}
}

assert.NoError(t, git.InitOnceWithSync(context.Background()))
git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)
Expand Down
6 changes: 5 additions & 1 deletion models/migrations/migrations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ func TestMain(m *testing.M) {

setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest()
if err = git.InitOnceWithSync(context.Background()); err != nil {
fmt.Printf("Unable to InitOnceWithSync: %v\n", err)
os.Exit(1)
}
git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)
Expand Down Expand Up @@ -203,7 +207,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
deferFn := PrintCurrentTest(t, ourSkip)
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down
4 changes: 3 additions & 1 deletion models/unittest/testdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,11 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
fatalTestError("util.CopyDir: %v\n", err)
}

if err = git.InitOnceWithSync(context.Background()); err != nil {
fatalTestError("git.Init: %v\n", err)
}
git.CheckLFSVersion()

ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
Expand Down Expand Up @@ -202,7 +204,7 @@ func PrepareTestEnv(t testing.TB) {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again

ownerDirs, err := os.ReadDir(setting.RepoRootPath)
assert.NoError(t, err)
Expand Down
27 changes: 9 additions & 18 deletions modules/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,26 +206,17 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
return false, nil
}

if err := CheckGitVersionAtLeast("1.8"); err == nil {
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
var exitError *exec.ExitError
if errors.As(err, &exitError) {
if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 {
return false, nil
}
var exitError *exec.ExitError
if errors.As(err, &exitError) {
if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 {
return false, nil
}
}
return false, err
}

result, _, err := NewCommand(c.repo.Ctx, "rev-list", "--ancestry-path", "-n1", that+".."+this, "--").RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil {
return false, err
}

return len(strings.TrimSpace(result)) > 0, nil
return false, err
}

// CommitsBeforeLimit returns num commits before current revision
Expand Down
55 changes: 31 additions & 24 deletions modules/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ package git

import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
Expand All @@ -22,20 +22,16 @@ import (
"github.com/hashicorp/go-version"
)

var (
// GitVersionRequired is the minimum Git version required
// At the moment, all code for git 1.x are not changed, if some users want to test with old git client
// or bypass the check, they still have a chance to edit this variable manually.
// If everything works fine, the code for git 1.x could be removed in a separate PR before 1.17 frozen.
GitVersionRequired = "2.0.0"
// GitVersionRequired is the minimum Git version required
const GitVersionRequired = "2.0.0"

var (
// GitExecutable is the command name of git
// Could be updated to an absolute path while initialization
GitExecutable = "git"

// DefaultContext is the default context to run git commands in
// will be overwritten by InitXxx with HammerContext
DefaultContext = context.Background()
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
DefaultContext context.Context

// SupportProcReceive version >= 2.29.0
SupportProcReceive bool
Expand Down Expand Up @@ -128,36 +124,43 @@ func VersionInfo() string {
return fmt.Sprintf(format, args...)
}

func checkInit() error {
if setting.RepoRootPath == "" {
return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
}
if DefaultContext != nil {
log.Warn("git module has been initialized already, duplicate init should be fixed")
}
return nil
}

// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
if setting.RepoRootPath == "" {
// TODO: now, some unit test code call the git module directly without initialization, which is incorrect.
// at the moment, we just use a temp HomeDir to prevent from conflicting with user's git config
// in the future, the git module should be initialized first before use.
tmpHomeDir := filepath.Join(os.TempDir(), "gitea-temp-home")
log.Error("Git's HomeDir is empty (RepoRootPath is empty), the git module is not initialized correctly, using a temp HomeDir (%s) temporarily", tmpHomeDir)
return tmpHomeDir
// strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
return ""
}
return setting.RepoRootPath
}

// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
// However, in integration test, the sub-command function may be called in the current process, so the InitSimple would be called multiple times, too
func InitSimple(ctx context.Context) error {
if err := checkInit(); err != nil {
return err
}

DefaultContext = ctx

if setting.Git.Timeout.Default > 0 {
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
}

if err := SetExecutablePath(setting.Git.Path); err != nil {
return err
}

// force cleanup args
globalCommandArgs = []string{}

return nil
return SetExecutablePath(setting.Git.Path)
}

var initOnce sync.Once
Expand All @@ -166,6 +169,10 @@ var initOnce sync.Once
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
// otherwise there will be data-race problem at the moment.
func InitOnceWithSync(ctx context.Context) (err error) {
if err = checkInit(); err != nil {
return err
}

initOnce.Do(func() {
err = InitSimple(ctx)
if err != nil {
Expand Down
60 changes: 43 additions & 17 deletions modules/git/repo_attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ type CheckAttributeOpts struct {
func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
env := []string{}

if len(opts.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(opts.IndexFile) > 0 {
env = append(env, "GIT_INDEX_FILE="+opts.IndexFile)
}
if len(opts.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(opts.WorkTree) > 0 {
env = append(env, "GIT_WORK_TREE="+opts.WorkTree)
}

Expand All @@ -56,8 +56,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
}
}

// git check-attr --cached first appears in git 1.7.8
if opts.CachedOnly && CheckGitVersionAtLeast("1.7.8") == nil {
if opts.CachedOnly {
cmdArgs = append(cmdArgs, "--cached")
}

Expand Down Expand Up @@ -125,12 +124,12 @@ type CheckAttributeReader struct {
func (c *CheckAttributeReader) Init(ctx context.Context) error {
cmdArgs := []string{"check-attr", "--stdin", "-z"}

if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(c.IndexFile) > 0 {
cmdArgs = append(cmdArgs, "--cached")
c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile)
}

if len(c.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(c.WorkTree) > 0 {
c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree)
}

Expand Down Expand Up @@ -160,17 +159,10 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error {
return err
}

if CheckGitVersionAtLeast("1.8.5") == nil {
lw := new(nulSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
} else {
lw := new(lineSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
}
lw := new(nulSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
return nil
}

Expand Down Expand Up @@ -400,3 +392,37 @@ func (wr *lineSeparatedAttributeWriter) Close() error {
close(wr.closed)
return nil
}

// Create a check attribute reader for the current repository and provided commit ID
func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) {
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
if err != nil {
return nil, func() {}
}

checker := &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo,
IndexFile: indexFilename,
WorkTree: worktree,
}
ctx, cancel := context.WithCancel(repo.Ctx)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else {
go func() {
err := checker.Run()
if err != nil && err != ctx.Err() {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
}
cancel()
}()
}
deferable := func() {
_ = checker.Close()
cancel()
deleteTemporaryFile()
}

return checker, deferable
}
Loading

0 comments on commit 157b405

Please sign in to comment.