Merge remote-tracking branch 'origin/3.6.1-code-conventions' into 3.6.1-code-conventions

# Conflicts:
#	tools/component/component.go
#	tools/formitychecker/checker/checker.go
This commit is contained in:
Gordon 2024-03-19 12:29:34 +08:00
commit 34df324789
9 changed files with 259 additions and 119 deletions

View File

@ -579,6 +579,7 @@ linters-settings:
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
revive:
# see https://github.com/mgechev/revive#available-rules for details.
ignore-generated-header: true
@ -586,15 +587,16 @@ linters-settings:
rules:
- name: indent-error-flow
severity: warning
staticcheck:
# Select the Go version to target. The default is '1.13'.
go: "1.16"
go: "1.20"
# https://staticcheck.io/docs/options#checks
checks: [ "all" ]
stylecheck:
# Select the Go version to target. The default is '1.13'.
go: "1.16"
go: "1.20"
# https://staticcheck.io/docs/options#checks
checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]

View File

@ -89,10 +89,7 @@ func FriendsDB2Pb(
}
func FriendRequestDB2Pb(ctx context.Context,
friendRequests []*relation.FriendRequestModel,
getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
) ([]*sdkws.FriendRequest, error) {
func FriendRequestDB2Pb(ctx context.Context, friendRequests []*relation.FriendRequestModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) ([]*sdkws.FriendRequest, error) {
if len(friendRequests) == 0 {
return nil, nil
}

View File

@ -56,12 +56,7 @@ func Pb2DbGroupRequest(req *pbgroup.GroupApplicationResponseReq, handleUserID st
}
}
func Db2PbCMSGroup(
m *relation.GroupModel,
ownerUserID string,
ownerUserName string,
memberCount uint32,
) *pbgroup.CMSGroup {
func Db2PbCMSGroup(m *relation.GroupModel, ownerUserID string, ownerUserName string, memberCount uint32) *pbgroup.CMSGroup {
return &pbgroup.CMSGroup{
GroupInfo: Db2PbGroupInfo(m, ownerUserID, memberCount),
GroupOwnerUserID: ownerUserID,
@ -86,11 +81,7 @@ func Db2PbGroupMember(m *relation.GroupMemberModel) *sdkws.GroupMemberFullInfo {
}
}
func Db2PbGroupRequest(
m *relation.GroupRequestModel,
user *sdkws.PublicUserInfo,
group *sdkws.GroupInfo,
) *sdkws.GroupRequest {
func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo, group *sdkws.GroupInfo) *sdkws.GroupRequest {
return &sdkws.GroupRequest{
UserInfo: user,
GroupInfo: group,

View File

@ -19,8 +19,5 @@
# Usage: `scripts/list-feature-tests.sh`.
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
grep "\[Feature:\w+\]" "${OPENIM_ROOT}"/test/e2e/**/*.go -Eoh | LC_ALL=C sort -u

View File

@ -243,7 +243,7 @@ func checkKafka(config *config.GlobalConfig) error {
for _, requiredTopic := range requiredTopics {
if !isTopicPresent(requiredTopic, topics) {
return errs.WrapMsg(err, fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic))
return errs.WrapMsg(nil, "Kafka missing required topic", "topic", requiredTopic, "availableTopics", strings.Join(topics, ", "))
}
}

View File

@ -15,99 +15,136 @@
package checker
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/OpenIMSDK/tools/errs"
"github.com/openimsdk/open-im-server/tools/formitychecker/config"
)
var (
underscoreRegex = regexp.MustCompile(`^[a-zA-Z0-9_]+\.[a-zA-Z0-9]+$`)
hyphenRegex = regexp.MustCompile(`^[a-zA-Z0-9\-]+\.[a-zA-Z0-9]+$`)
)
type Issue struct {
Type string
Path string
Message string
}
// CheckDirectory initiates the checking process for the specified directories using configuration from config.Config.
func CheckDirectory(cfg *config.Config) error {
ignoreMap := make(map[string]struct{})
for _, dir := range cfg.IgnoreDirs {
ignoreMap[dir] = struct{}{}
type Checker struct {
Config *config.Config
Summary struct {
CheckedDirectories int
CheckedFiles int
Issues []Issue
}
}
func (c *Checker) Check() error {
return filepath.Walk(c.Config.BaseConfig.SearchDirectory, c.checkPath)
}
func (c *Checker) checkPath(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
for _, targetDir := range cfg.TargetDirs {
err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return errs.WrapMsg(err, fmt.Sprintf("error walking directory '%s'", targetDir))
}
relativePath, err := filepath.Rel(c.Config.BaseConfig.SearchDirectory, path)
if err != nil {
return err
}
// Skip if the directory is in the ignore list
dirName := filepath.Base(filepath.Dir(path))
if _, ok := ignoreMap[dirName]; ok && info.IsDir() {
return filepath.SkipDir
}
// Check the naming convention
if err := checkNamingConvention(path, info); err != nil {
fmt.Println(err)
}
if relativePath == "." {
return nil
}
if info.IsDir() {
c.Summary.CheckedDirectories++
if c.isIgnoredDirectory(relativePath) {
c.Summary.Issues = append(c.Summary.Issues, Issue{
Type: "ignoredDirectory",
Path: path,
Message: "This directory has been ignored",
})
return filepath.SkipDir
}
if !c.checkDirectoryName(relativePath) {
c.Summary.Issues = append(c.Summary.Issues, Issue{
Type: "directoryNaming",
Path: path,
Message: "The directory name is invalid",
})
}
} else {
if c.isIgnoredFile(path) {
return nil
})
if err != nil {
return fmt.Errorf("error checking directory '%s': %w", targetDir, err)
}
c.Summary.CheckedFiles++
if !c.checkFileName(relativePath) {
c.Summary.Issues = append(c.Summary.Issues, Issue{
Type: "fileNaming",
Path: path,
Message: "The file name does not comply with the specification",
})
}
}
return nil
}
// checkNamingConvention checks if the file or directory name conforms to the standard naming conventions.
func checkNamingConvention(path string, info os.FileInfo) error {
fileName := info.Name()
// Handle special cases for directories like .git
if info.IsDir() && strings.HasPrefix(fileName, ".") {
return nil // Skip special directories
}
// Extract the main part of the name (without extension for files)
mainName := fileName
if !info.IsDir() {
mainName = strings.TrimSuffix(fileName, filepath.Ext(fileName))
}
// Determine the type of file and apply corresponding naming rule
switch {
case info.IsDir():
if !isValidName(mainName, "_") { // Directory names must only contain underscores
return fmt.Errorf("!!! invalid directory name: %s", path)
}
case strings.HasSuffix(fileName, ".go"):
if !isValidName(mainName, "_") { // Go files must only contain underscores
return fmt.Errorf("!!! invalid Go file name: %s", path)
}
case strings.HasSuffix(fileName, ".yml"), strings.HasSuffix(fileName, ".yaml"), strings.HasSuffix(fileName, ".md"):
if !isValidName(mainName, "-") { // YML, YAML, and Markdown files must only contain hyphens
return fmt.Errorf("!!! invalid file name: %s", path)
func (c *Checker) isIgnoredDirectory(path string) bool {
for _, ignoredDir := range c.Config.IgnoreDirectories {
if strings.Contains(path, ignoredDir) {
return true
}
}
return nil
return false
}
// isValidName checks if the file name conforms to the specified rule (underscore or hyphen).
func isValidName(name, charType string) bool {
switch charType {
case "_":
return underscoreRegex.MatchString(name)
case "-":
return hyphenRegex.MatchString(name)
default:
func (c *Checker) isIgnoredFile(path string) bool {
ext := filepath.Ext(path)
for _, format := range c.Config.IgnoreFormats {
if ext == format {
return true
}
}
return false
}
func (c *Checker) checkDirectoryName(path string) bool {
dirName := filepath.Base(path)
if c.Config.DirectoryNaming.MustBeLowercase && (dirName != strings.ToLower(dirName)) {
return false
}
if !c.Config.DirectoryNaming.AllowHyphens && strings.Contains(dirName, "-") {
return false
}
if !c.Config.DirectoryNaming.AllowUnderscores && strings.Contains(dirName, "_") {
return false
}
return true
}
func (c *Checker) checkFileName(path string) bool {
fileName := filepath.Base(path)
ext := filepath.Ext(fileName)
allowHyphens := c.Config.FileNaming.AllowHyphens
allowUnderscores := c.Config.FileNaming.AllowUnderscores
mustBeLowercase := c.Config.FileNaming.MustBeLowercase
if specificNaming, ok := c.Config.FileTypeSpecificNaming[ext]; ok {
allowHyphens = specificNaming.AllowHyphens
allowUnderscores = specificNaming.AllowUnderscores
mustBeLowercase = specificNaming.MustBeLowercase
}
if mustBeLowercase && (fileName != strings.ToLower(fileName)) {
return false
}
if !allowHyphens && strings.Contains(fileName, "-") {
return false
}
if !allowUnderscores && strings.Contains(fileName, "_") {
return false
}
return true
}

View File

@ -0,0 +1,47 @@
# config.yaml 示例配置
# 基础配置
baseConfig:
searchDirectory: "./" # 检索的根目录,"./" 代表当前目录
ignoreCase: false # 是否忽略大小写true 为忽略false 为区分大小写
# 目录命名风格配置
directoryNaming:
allowHyphens: true # 是否允许目录名中含有中划线
allowUnderscores: false # 是否允许目录名中含有下划线
mustBeLowercase: true # 目录名是否必须为小写
# 文件命名风格配置
fileNaming:
allowHyphens: true # 是否允许文件名中含有中划线
allowUnderscores: true # 是否允许文件名中含有下划线
mustBeLowercase: true # 文件名是否必须为小写
# 忽略的文件格式列表
ignoreFormats:
- ".log"
- ".env"
- "_test.go"
# 忽略的目录列表
ignoreDirectories:
- "vendor"
- ".git"
- "node_modules"
- "logs"
- "components"
- "_output"
- "README.md"
- "tools/openim-web"
- "CHANGELOG"
- "docs/readme"
fileTypeSpecificNaming:
".yaml":
allowHyphens: true
allowUnderscores: false
mustBeLowercase: true
".go":
allowHyphens: false
allowUnderscores: true
mustBeLowercase: true

View File

@ -15,27 +15,66 @@
package config
import (
"strings"
"os"
"github.com/openimsdk/open-im-server/tools/codescan/config"
"gopkg.in/yaml.v2"
)
// Config holds all the configuration parameters for the checker.
type Config struct {
TargetDirs []string // Directories to check
IgnoreDirs []string // Directories to ignore
BaseConfig struct {
SearchDirectory string `yaml:"searchDirectory"`
IgnoreCase bool `yaml:"ignoreCase"`
} `yaml:"baseConfig"`
DirectoryNaming struct {
AllowHyphens bool `yaml:"allowHyphens"`
AllowUnderscores bool `yaml:"allowUnderscores"`
MustBeLowercase bool `yaml:"mustBeLowercase"`
} `yaml:"directoryNaming"`
FileNaming struct {
AllowHyphens bool `yaml:"allowHyphens"`
AllowUnderscores bool `yaml:"allowUnderscores"`
MustBeLowercase bool `yaml:"mustBeLowercase"`
} `yaml:"fileNaming"`
IgnoreFormats []string `yaml:"ignoreFormats"`
IgnoreDirectories []string `yaml:"ignoreDirectories"`
FileTypeSpecificNaming map[string]FileTypeSpecificNaming `yaml:"fileTypeSpecificNaming"`
}
// NewConfig creates and returns a new Config instance.
func NewConfig(targetDirs, ignoreDirs string) *Config {
return &Config{
TargetDirs: parseDirs(targetDirs),
IgnoreDirs: parseDirs(ignoreDirs),
}
type FileTypeSpecificNaming struct {
AllowHyphens bool `yaml:"allowHyphens"`
AllowUnderscores bool `yaml:"allowUnderscores"`
MustBeLowercase bool `yaml:"mustBeLowercase"`
}
// parseDirs splits a comma-separated string into a slice of directory names.
func parseDirs(dirs string) []string {
if dirs == "" {
return nil
}
return strings.Split(dirs, ",")
type Issue struct {
Type string
Path string
Message string
}
type Checker struct {
Config *config.Config
Summary struct {
CheckedDirectories int
CheckedFiles int
Issues []Issue
}
Errors []string
}
func LoadConfig(configPath string) (*Config, error) {
var config Config
file, err := os.ReadFile(configPath)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(file, &config)
if err != nil {
return nil, err
}
return &config, nil
}

View File

@ -15,27 +15,57 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"github.com/openimsdk/open-im-server/tools/formitychecker/checker"
"github.com/openimsdk/open-im-server/tools/formitychecker/config"
)
func main() {
defaultTargetDirs := "."
defaultIgnoreDirs := "components,.git"
var targetDirs string
var ignoreDirs string
flag.StringVar(&targetDirs, "target", defaultTargetDirs, "Directories to check (default: current directory)")
flag.StringVar(&ignoreDirs, "ignore", defaultIgnoreDirs, "Directories to ignore (default: A/, B/)")
var configPath string
flag.StringVar(&configPath, "config", "", "Path to the configuration file")
flag.Parse()
conf := config.NewConfig(targetDirs, ignoreDirs)
err := checker.CheckDirectory(conf)
if err != nil {
fmt.Println("Error:", err)
if configPath == "" {
configPath = os.Getenv("CONFIG_PATH")
}
if configPath == "" {
configPath = "config.yaml"
if _, err := os.Stat(".github/formitychecker.yaml"); err == nil {
configPath = ".github/formitychecker.yaml"
}
}
cfg, err := config.LoadConfig(configPath)
if err != nil {
fmt.Println("Error loading config:", err)
return
}
c := &checker.Checker{Config: cfg}
err = c.Check()
if err != nil {
fmt.Println("Error during check:", err)
os.Exit(1)
}
// if len(c.Errors) > 0 {
// fmt.Println("Found errors:")
// for _, errMsg := range c.Errors {
// fmt.Println("-", errMsg)
// }
// os.Exit(1)
// }
summaryJSON, err := json.MarshalIndent(c.Summary, "", " ")
if err != nil {
fmt.Println("Error marshalling summary:", err)
return
}
fmt.Println(string(summaryJSON))
}