From cc20b52ab3eddb01c06b491ece157316fde8de3e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 12 Sep 2025 16:51:43 +0000 Subject: [PATCH] feat: add validation for object-format flag in repo create command (#741) This PR adds validation for the `--object-format` flag in the `repo create` command. The flag now accepts only `sha1` or `sha256` as valid values, and returns an error for any other value. Changes: - Added validation in `runRepoCreate` to check for valid object format values - Added unit tests to verify the validation logic - Fixed the field name from `ObjectFormat` to `ObjectFormatName` to match the SDK The changes ensure that users get clear error messages when using invalid object format values, improving the user experience. Fix #727 Fix #660 Fix #767 Co-authored-by: techknowlogick Reviewed-on: https://gitea.com/gitea/tea/pulls/741 Reviewed-by: TheFox0x7 --- .gitea/workflows/test-pr.yml | 38 ++++++++++++++++ cmd/repos/create.go | 34 +++++++++----- cmd/repos/create_test.go | 88 ++++++++++++++++++++++++++++++++++++ docs/CLI.md | 2 + modules/task/login_create.go | 8 +++- 5 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 cmd/repos/create_test.go diff --git a/.gitea/workflows/test-pr.yml b/.gitea/workflows/test-pr.yml index 888475a..30a9527 100644 --- a/.gitea/workflows/test-pr.yml +++ b/.gitea/workflows/test-pr.yml @@ -4,8 +4,21 @@ on: - pull_request jobs: + govulncheck_job: + runs-on: ubuntu-latest + name: Run govulncheck + steps: + - id: govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-file: 'go.mod' check-and-test: runs-on: ubuntu-latest + env: + HTTP_PROXY: "" + GITEA_TEA_TEST_URL: "http://gitea:3000" + GITEA_TEA_TEST_USERNAME: "test01" + GITEA_TEA_TEST_PASSWORD: "test01" steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -20,7 +33,32 @@ jobs: make misspell-check make docs-check make build + - run: curl --noproxy "*" http://gitea:3000/api/v1/version # verify connection to instance - name: test and coverage run: | make test make unit-test-coverage + services: + gitea: + image: docker.gitea.com/gitea:1.24.5 + cmd: + - bash + - -c + - >- + mkdir -p /tmp/conf/ + && mkdir -p /tmp/data/ + && echo "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT = true" > /tmp/conf/app.ini + && echo "[security]" >> /tmp/conf/app.ini + && echo "INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NTg4MzY4ODB9.LoKQyK5TN_0kMJFVHWUW0uDAyoGjDP6Mkup4ps2VJN4" >> /tmp/conf/app.ini + && echo "INSTALL_LOCK = true" >> /tmp/conf/app.ini + && echo "SECRET_KEY = 2crAW4UANgvLipDS6U5obRcFosjSJHQANll6MNfX7P0G3se3fKcCwwK3szPyGcbo" >> /tmp/conf/app.ini + && echo "PASSWORD_COMPLEXITY = off" >> /tmp/conf/app.ini + && echo "[database]" >> /tmp/conf/app.ini + && echo "DB_TYPE = sqlite3" >> /tmp/conf/app.ini + && echo "[repository]" >> /tmp/conf/app.ini + && echo "ROOT = /tmp/data/" >> /tmp/conf/app.ini + && echo "[server]" >> /tmp/conf/app.ini + && echo "ROOT_URL = http://gitea:3000" >> /tmp/conf/app.ini + && gitea migrate -c /tmp/conf/app.ini + && gitea admin user create --username=test01 --password=test01 --email=test01@gitea.io --admin=true --must-change-password=false --access-token -c /tmp/conf/app.ini + && gitea web -c /tmp/conf/app.ini diff --git a/cmd/repos/create.go b/cmd/repos/create.go index 8adc236..3486352 100644 --- a/cmd/repos/create.go +++ b/cmd/repos/create.go @@ -88,6 +88,17 @@ var CmdRepoCreate = cli.Command{ Name: "trustmodel", Usage: "select trust model (committer,collaborator,collaborator+committer)", }, + &cli.StringFlag{ + Name: "object-format", + Required: false, + Usage: "select git object format (sha1,sha256)", + Validator: func(v string) error { + if v != "sha1" && v != "sha256" { + return fmt.Errorf("invalid object format '%s', must be either 'sha1' or 'sha256'", v) + } + return nil + }, + }, }, flags.LoginOutputFlags...), } @@ -114,17 +125,18 @@ func runRepoCreate(_ stdctx.Context, cmd *cli.Command) error { } opts := gitea.CreateRepoOption{ - Name: ctx.String("name"), - Description: ctx.String("description"), - Private: ctx.Bool("private"), - AutoInit: ctx.Bool("init"), - IssueLabels: ctx.String("labels"), - Gitignores: ctx.String("gitignores"), - License: ctx.String("license"), - Readme: ctx.String("readme"), - DefaultBranch: ctx.String("branch"), - Template: ctx.Bool("template"), - TrustModel: trustmodel, + Name: ctx.String("name"), + Description: ctx.String("description"), + Private: ctx.Bool("private"), + AutoInit: ctx.Bool("init"), + IssueLabels: ctx.String("labels"), + Gitignores: ctx.String("gitignores"), + License: ctx.String("license"), + Readme: ctx.String("readme"), + DefaultBranch: ctx.String("branch"), + Template: ctx.Bool("template"), + TrustModel: trustmodel, + ObjectFormatName: ctx.String("object-format"), } if len(ctx.String("owner")) != 0 { repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts) diff --git a/cmd/repos/create_test.go b/cmd/repos/create_test.go new file mode 100644 index 0000000..acb42aa --- /dev/null +++ b/cmd/repos/create_test.go @@ -0,0 +1,88 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repos + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/task" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestCreateRepoObjectFormat(t *testing.T) { + giteaURL := os.Getenv("GITEA_TEA_TEST_URL") + if giteaURL == "" { + t.Skip("GITEA_TEA_TEST_URL is not set, skipping test") + } + + timestamp := time.Now().Unix() + tests := []struct { + name string + args []string + wantOpts gitea.CreateRepoOption + wantErr bool + errContains string + }{ + { + name: "create repo with sha1 object format", + args: []string{"--name", fmt.Sprintf("test-sha1-%d", timestamp), "--object-format", "sha1"}, + wantOpts: gitea.CreateRepoOption{ + Name: fmt.Sprintf("test-sha1-%d", timestamp), + ObjectFormatName: "sha1", + }, + wantErr: false, + }, + { + name: "create repo with sha256 object format", + args: []string{"--name", fmt.Sprintf("test-sha256-%d", timestamp), "--object-format", "sha256"}, + wantOpts: gitea.CreateRepoOption{ + Name: fmt.Sprintf("test-sha256-%d", timestamp), + ObjectFormatName: "sha256", + }, + wantErr: false, + }, + { + name: "create repo with invalid object format", + args: []string{"--name", fmt.Sprintf("test-invalid-%d", timestamp), "--object-format", "invalid"}, + wantErr: true, + errContains: "invalid object format", + }, + } + + giteaUserName := os.Getenv("GITEA_TEA_TEST_USERNAME") + giteaUserPasword := os.Getenv("GITEA_TEA_TEST_PASSWORD") + + err := task.CreateLogin("test", "", giteaUserName, giteaUserPasword, "", "", "", giteaURL, "", "", true, false, false, false) + if err != nil && err.Error() != "login name 'test' has already been used" { + t.Fatal(err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reposCmd := &cli.Command{ + Name: "repos", + Commands: []*cli.Command{&CmdRepoCreate}, + } + tt.args = append(tt.args, "--login", "test") + args := append([]string{"repos", "create"}, tt.args...) + + err := reposCmd.Run(context.Background(), args) + if tt.wantErr { + assert.Error(t, err) + if tt.errContains != "" { + assert.Contains(t, err.Error(), tt.errContains) + } + return + } + + assert.NoError(t, err) + }) + } +} diff --git a/docs/CLI.md b/docs/CLI.md index 7a844c5..2b329c5 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -1071,6 +1071,8 @@ Create a repository **--name, -**="": name of new repo +**--object-format**="": select git object format (sha1,sha256) + **--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json) **--owner, -O**="": name of repo owner diff --git a/modules/task/login_create.go b/modules/task/login_create.go index 4da591d..94e5e74 100644 --- a/modules/task/login_create.go +++ b/modules/task/login_create.go @@ -167,8 +167,12 @@ func generateToken(login config.Login, user, pass, otp, scopes string) (string, } var tokenScopes []gitea.AccessTokenScope - for _, scope := range strings.Split(scopes, ",") { - tokenScopes = append(tokenScopes, gitea.AccessTokenScope(strings.TrimSpace(scope))) + if len(scopes) == 0 { + tokenScopes = []gitea.AccessTokenScope{gitea.AccessTokenScopeAll} + } else { + for _, scope := range strings.Split(scopes, ",") { + tokenScopes = append(tokenScopes, gitea.AccessTokenScope(strings.TrimSpace(scope))) + } } t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{