revert: tea 1.0 rewrite (#1122–#1126) (#1146)

Reverts #1126, #1125, #1124, #1123 and #1122.

`go.mod` keeps the Go 1.26 line with toolchain `go1.26.9` (instead of the original `go1.26.6`) so the security fix from #1131 isn't lost.

reopens #69
reopens #141
reopens #162
reopens #172
reopens #197
reopens #232
reopens #240
reopens #246
reopens #323
reopens #325
reopens #326
reopens #328
reopens #344
reopens #351
reopens #373
reopens #403
reopens #406
reopens #417
reopens #430
reopens #457
reopens #480
reopens #482
reopens #489
reopens #490
reopens #498
reopens #500
reopens #504
reopens #510
reopens #516
reopens #523
reopens #556
reopens #560
reopens #569
reopens #571
reopens #602
reopens #632
reopens #633
reopens #643
reopens #666
reopens #670
reopens #672
reopens #677
reopens #683
reopens #697
reopens #726
reopens #737
reopens #770
reopens #779
reopens #804
reopens #817
reopens #831
reopens #833
reopens #834
reopens #908
reopens #966
reopens #1023
reopens #1029
reopens #1032
reopens #1050
reopens #1052
reopens #1069
reopens #1073
reopens #1087
reopens #1091
reopens #1094
reopens #1110
reopens #1115
reopens #1116
reopens #1118

Reviewed-on: https://gitea.com/gitea/tea/pulls/1146
Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
bircni committed 2026-10-18 20:56:33 +00:00
1 parent 33bb12729e
commit c5f674111a
604 files changed
+33711 -35545

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
stdctx "context"
"fmt"
"gitea.dev/tea/cmd/flags"
"gitea.dev/tea/modules/context"
"github.com/urfave/cli/v3"
)
// CmdVariablesDelete represents a sub command to delete action variables
var CmdVariablesDelete = cli.Command{
Name: "delete",
Aliases: []string{"remove", "rm"},
Usage: "Delete an action variable",
Description: "Delete a variable used by repository actions",
ArgsUsage: "<variable-name>",
Action: runVariablesDelete,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "confirm",
Aliases: []string{"y"},
Usage: "confirm deletion without prompting",
},
}, flags.AllDefaultFlags...),
}
func runVariablesDelete(ctx stdctx.Context, cmd *cli.Command) error {
if cmd.Args().Len() == 0 {
return fmt.Errorf("variable name is required")
}
c, err := context.InitCommand(cmd)
if err != nil {
return err
}
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
return err
}
client := c.Login.Client()
variableName := cmd.Args().First()
if !cmd.Bool("confirm") {
fmt.Printf("Are you sure you want to delete variable '%s'? [y/N] ", variableName)
var response string
fmt.Scanln(&response)
if response != "y" && response != "Y" && response != "yes" {
fmt.Println("Deletion canceled.")
return nil
}
}
_, err = client.Actions.DeleteRepoVariable(ctx, c.Owner, c.Repo, variableName)
if err != nil {
return err
}
fmt.Printf("Variable '%s' deleted successfully\n", variableName)
return nil
}
+98
View File
@@ -0,0 +1,98 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
"fmt"
"testing"
)
func TestVariablesDeleteValidation(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
}{
{
name: "valid variable name",
args: []string{"VALID_VARIABLE"},
wantErr: false,
},
{
name: "valid lowercase name",
args: []string{"valid_variable"},
wantErr: false,
},
{
name: "no args",
args: []string{},
wantErr: true,
},
{
name: "too many args",
args: []string{"VARIABLE1", "VARIABLE2"},
wantErr: true,
},
{
name: "invalid variable name",
args: []string{"invalid-variable"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateVariableDeleteArgs(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("validateVariableDeleteArgs() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestVariablesDeleteFlags(t *testing.T) {
cmd := CmdVariablesDelete
// Test command properties
if cmd.Name != "delete" {
t.Errorf("Expected command name 'delete', got %s", cmd.Name)
}
// Check that rm is one of the aliases
hasRmAlias := false
for _, alias := range cmd.Aliases {
if alias == "rm" {
hasRmAlias = true
break
}
}
if !hasRmAlias {
t.Error("Expected 'rm' to be one of the aliases for delete command")
}
if cmd.ArgsUsage != "<variable-name>" {
t.Errorf("Expected ArgsUsage '<variable-name>', got %s", cmd.ArgsUsage)
}
if cmd.Usage == "" {
t.Error("Delete command should have usage text")
}
if cmd.Description == "" {
t.Error("Delete command should have description")
}
}
// validateVariableDeleteArgs validates arguments for the delete command
func validateVariableDeleteArgs(args []string) error {
if len(args) == 0 {
return fmt.Errorf("variable name is required")
}
if len(args) > 1 {
return fmt.Errorf("only one variable name allowed")
}
return validateVariableName(args[0])
}
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
stdctx "context"
"fmt"
"gitea.dev/tea/cmd/flags"
"gitea.dev/tea/modules/context"
"gitea.dev/tea/modules/print"
"github.com/urfave/cli/v3"
)
// CmdVariablesList represents a sub command to list action variables
var CmdVariablesList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List action variables",
Description: "List variables configured for repository actions",
Action: RunVariablesList,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "show specific variable by name",
},
}, flags.AllDefaultFlags...),
}
// RunVariablesList list action variables
func RunVariablesList(ctx stdctx.Context, cmd *cli.Command) error {
c, err := context.InitCommand(cmd)
if err != nil {
return err
}
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
return err
}
client := c.Login.Client()
if name := cmd.String("name"); name != "" {
// Get specific variable
variable, _, err := client.Actions.GetRepoVariable(ctx, c.Owner, c.Repo, name)
if err != nil {
return err
}
print.ActionVariableDetails(variable)
return nil
}
// List all variables - Note: SDK doesn't have ListRepoActionVariables yet
// This is a limitation of the current SDK
fmt.Println("Note: Listing all variables is not yet supported by the Gitea SDK.")
fmt.Println("Use 'tea actions variables list --name <variable-name>' to get a specific variable.")
fmt.Println("You can also check your repository's Actions settings in the web interface.")
return nil
}
+98
View File
@@ -0,0 +1,98 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v3"
"gitea.dev/tea/modules/config"
)
func TestVariablesListFlags(t *testing.T) {
cmd := CmdVariablesList
// Test that required flags exist
expectedFlags := []string{"output", "remote", "login", "repo"}
for _, flagName := range expectedFlags {
found := false
for _, flag := range cmd.Flags {
if flag.Names()[0] == flagName {
found = true
break
}
}
if !found {
t.Errorf("Expected flag %s not found in CmdVariablesList", flagName)
}
}
// Test command properties
if cmd.Name != "list" {
t.Errorf("Expected command name 'list', got %s", cmd.Name)
}
if len(cmd.Aliases) == 0 || cmd.Aliases[0] != "ls" {
t.Errorf("Expected alias 'ls' for list command")
}
if cmd.Usage == "" {
t.Error("List command should have usage text")
}
if cmd.Description == "" {
t.Error("List command should have description")
}
}
func TestVariablesListValidation(t *testing.T) {
// Basic validation that the command accepts the expected arguments
// More detailed testing would require mocking the Gitea client
// Test that list command doesn't require arguments
args := []string{}
if len(args) > 0 {
t.Error("List command should not require arguments")
}
// Test that extra arguments are ignored
extraArgs := []string{"extra", "args"}
if len(extraArgs) > 0 {
// This is fine - list commands typically ignore extra args
}
}
func TestRunVariablesListRequiresRepoContext(t *testing.T) {
oldWd, err := os.Getwd()
require.NoError(t, err)
require.NoError(t, os.Chdir(t.TempDir()))
t.Cleanup(func() {
require.NoError(t, os.Chdir(oldWd))
})
config.SetConfigForTesting(config.LocalConfig{
Logins: []config.Login{{
Name: "test",
URL: "https://gitea.example.com",
Token: "token",
User: "tester",
Default: true,
}},
})
cmd := &cli.Command{
Name: CmdVariablesList.Name,
Flags: CmdVariablesList.Flags,
}
require.NoError(t, cmd.Set("login", "test"))
err = RunVariablesList(t.Context(), cmd)
require.ErrorContains(t, err, "remote repository required")
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
stdctx "context"
"fmt"
"regexp"
"gitea.dev/tea/cmd/flags"
"gitea.dev/tea/modules/context"
"gitea.dev/tea/modules/utils"
"github.com/urfave/cli/v3"
)
// CmdVariablesSet represents a sub command to set action variables
var CmdVariablesSet = cli.Command{
Name: "set",
Aliases: []string{"create", "update"},
Usage: "Set an action variable",
Description: "Set a variable for use in repository actions and workflows",
ArgsUsage: "<variable-name> [variable-value]",
Action: runVariablesSet,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "file",
Usage: "read variable value from file",
},
&cli.BoolFlag{
Name: "stdin",
Usage: "read variable value from stdin",
},
}, flags.AllDefaultFlags...),
}
func runVariablesSet(ctx stdctx.Context, cmd *cli.Command) error {
if cmd.Args().Len() == 0 {
return fmt.Errorf("variable name is required")
}
c, err := context.InitCommand(cmd)
if err != nil {
return err
}
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
return err
}
client := c.Login.Client()
variableName := cmd.Args().First()
if err := validateVariableName(variableName); err != nil {
return err
}
// Read variable value using the utility
variableValue, err := utils.ReadValue(cmd, utils.ReadValueOptions{
ResourceName: "variable",
PromptMsg: fmt.Sprintf("Enter variable value for '%s'", variableName),
Hidden: false,
AllowEmpty: false,
})
if err != nil {
return err
}
if err := validateVariableValue(variableValue); err != nil {
return err
}
_, err = client.Actions.CreateRepoVariable(ctx, c.Owner, c.Repo, variableName, variableValue)
if err != nil {
return err
}
fmt.Printf("Variable '%s' set successfully\n", variableName)
return nil
}
// validateVariableName validates that a variable name follows the required format
func validateVariableName(name string) error {
if name == "" {
return fmt.Errorf("variable name cannot be empty")
}
// Variable names can contain letters (upper/lower), numbers, and underscores
// Cannot start with a number
// Cannot contain spaces or special characters (except underscore)
validPattern := regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
if !validPattern.MatchString(name) {
return fmt.Errorf("variable name must contain only letters, numbers, and underscores, and cannot start with a number")
}
return nil
}
// validateVariableValue validates that a variable value is acceptable
func validateVariableValue(value string) error {
// Variables can be empty or contain whitespace, unlike secrets
// Check for maximum size (64KB limit)
if len(value) > 65536 {
return fmt.Errorf("variable value cannot exceed 64KB")
}
return nil
}
+213
View File
@@ -0,0 +1,213 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package variables
import (
"strings"
"testing"
)
func TestValidateVariableName(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{
name: "valid name",
input: "VALID_VARIABLE_NAME",
wantErr: false,
},
{
name: "valid name with numbers",
input: "VARIABLE_123",
wantErr: false,
},
{
name: "valid lowercase",
input: "valid_variable",
wantErr: false,
},
{
name: "valid mixed case",
input: "Mixed_Case_Variable",
wantErr: false,
},
{
name: "invalid - spaces",
input: "INVALID VARIABLE",
wantErr: true,
},
{
name: "invalid - special chars",
input: "INVALID-VARIABLE!",
wantErr: true,
},
{
name: "invalid - starts with number",
input: "1INVALID",
wantErr: true,
},
{
name: "invalid - empty",
input: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateVariableName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("validateVariableName(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
}
})
}
}
func TestGetVariableSourceArgs(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
}{
{
name: "valid args",
args: []string{"VALID_VARIABLE", "variable_value"},
wantErr: false,
},
{
name: "valid lowercase",
args: []string{"valid_variable", "value"},
wantErr: false,
},
{
name: "missing name",
args: []string{},
wantErr: true,
},
{
name: "too many args",
args: []string{"VARIABLE_NAME", "value", "extra"},
wantErr: true,
},
{
name: "invalid variable name",
args: []string{"invalid-variable", "value"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test argument validation only
if len(tt.args) == 0 {
if !tt.wantErr {
t.Error("Expected error for empty args")
}
return
}
if len(tt.args) > 2 {
if !tt.wantErr {
t.Error("Expected error for too many args")
}
return
}
// Test variable name validation
err := validateVariableName(tt.args[0])
if (err != nil) != tt.wantErr {
t.Errorf("validateVariableName() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestVariableNameValidation(t *testing.T) {
// Test that variable names follow GitHub Actions/Gitea Actions conventions
validNames := []string{
"VALID_VARIABLE",
"API_URL",
"DATABASE_HOST",
"VARIABLE_123",
"mixed_Case_Variable",
"lowercase_variable",
"UPPERCASE_VARIABLE",
}
invalidNames := []string{
"Invalid-Dashes",
"INVALID SPACES",
"123_STARTS_WITH_NUMBER",
"", // Empty
"INVALID!@#", // Special chars
}
for _, name := range validNames {
t.Run("valid_"+name, func(t *testing.T) {
err := validateVariableName(name)
if err != nil {
t.Errorf("validateVariableName(%q) should be valid, got error: %v", name, err)
}
})
}
for _, name := range invalidNames {
t.Run("invalid_"+name, func(t *testing.T) {
err := validateVariableName(name)
if err == nil {
t.Errorf("validateVariableName(%q) should be invalid, got no error", name)
}
})
}
}
func TestVariableValueValidation(t *testing.T) {
tests := []struct {
name string
value string
wantErr bool
}{
{
name: "valid value",
value: "variable123",
wantErr: false,
},
{
name: "valid complex value",
value: "https://api.example.com/v1",
wantErr: false,
},
{
name: "valid multiline value",
value: "line1\nline2\nline3",
wantErr: false,
},
{
name: "empty value allowed",
value: "",
wantErr: false, // Variables can be empty unlike secrets
},
{
name: "whitespace only allowed",
value: " \t\n ",
wantErr: false, // Variables can contain whitespace
},
{
name: "very long value",
value: strings.Repeat("a", 65537), // Over 64KB
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateVariableValue(tt.value)
if (err != nil) != tt.wantErr {
t.Errorf("validateVariableValue() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}