diff --git a/cmd/openim-test/p.go b/cmd/openim-test/p.go new file mode 100644 index 000000000..e7fe67a2c --- /dev/null +++ b/cmd/openim-test/p.go @@ -0,0 +1,18 @@ +package main + +import ( + "flag" + "fmt" +) + +func main() { + // Define flags + index := flag.Int("i", 0, "Index number") + config := flag.String("c", "", "Configuration path") + + // Parse the flags + flag.Parse() + + // Print the values of the flags + fmt.Printf("-i %d -c %s\n", *index, *config) +} diff --git a/docs/contrib/go-code1.md b/docs/contrib/go-code1.md new file mode 100644 index 000000000..8dcb9ce6d --- /dev/null +++ b/docs/contrib/go-code1.md @@ -0,0 +1,1554 @@ +## OpenIM development specification +We have very high standards for code style and specification, and we want our products to be polished and perfect + +## 1. Code style + +### 1.1 Code format + +- Code must be formatted with `gofmt`. +- Leave spaces between operators and operands. +- It is recommended that a line of code does not exceed 120 characters. If the part exceeds, please use an appropriate line break method. But there are also some exception scenarios, such as import lines, code automatically generated by tools, and struct fields with tags. +- The file length cannot exceed 800 lines. +- Function length cannot exceed 80 lines. +- import specification +- All code must be formatted with `goimports` (it is recommended to set the code Go code editor to: run `goimports` on save). +- Do not use relative paths to import packages, such as `import ../util/net`. +- Import aliases must be used when the package name does not match the last directory name of the import path, or when multiple identical package names conflict. + +```go +// bad +"github.com/dgrijalva/jwt-go/v4" + +//good +jwt "github.com/dgrijalva/jwt-go/v4" +``` +- Imported packages are suggested to be grouped, and anonymous package references use a new group, and anonymous package references are explained. + +```go +import ( + // go standard package + "fmt" + + // third party package + "github.com/jinzhu/gorm" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + // Anonymous packages are grouped separately, and anonymous package references are explained + // import mysql driver + _ "github.com/jinzhu/gorm/dialects/mysql" + + // inner package +) +``` + +### 1.2 Declaration, initialization and definition + +When multiple variables need to be used in a function, the `var` declaration can be used at the beginning of the function. Declaration outside the function must use `var`, do not use `:=`, it is easy to step on the scope of the variable. + +```go +var ( + Width int + Height int +) +``` + +- When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization. + +```go + // bad + sptr := new(T) + sptr.Name = "bar" + + // good + sptr := &T{Name: "bar"} +``` + +- The struct declaration and initialization format takes multiple lines and is defined as follows. + +```go + type User struct{ + Username string + Email string + } + + user := User{ + Username: "belm", + Email: "nosbelm@qq.com", +} +``` + +- Similar declarations are grouped together, and the same applies to constant, variable, and type declarations. + +```go +// bad +import "a" +import "b" + +//good +import ( + "a" + "b" +) +``` + +- Specify container capacity where possible to pre-allocate memory for the container, for example: + +```go +v := make(map[int]string, 4) +v := make([]string, 0, 4) +``` + +- At the top level, use the standard var keyword. Do not specify a type unless it is different from the type of the expression. + +```go +// bad +var s string = F() + +func F() string { return "A" } + +// good +var s = F() +// Since F already explicitly returns a string type, we don't need to explicitly specify the type of _s +// still of that type + +func F() string { return "A" } +``` + +- This example emphasizes using PascalCase for exported constants and camelCase for unexported ones, avoiding all caps and underscores. + +```go +// bad +const ( + MAX_COUNT = 100 + timeout = 30 +) + +// good +const ( + MaxCount = 100 // Exported constants should use PascalCase. + defaultTimeout = 30 // Unexported constants should use camelCase. +) +``` + +- Grouping related constants enhances organization and readability, especially when there are multiple constants related to a particular feature or configuration. + +```go +// bad +const apiVersion = "v1" +const retryInterval = 5 + +// good +const ( + ApiVersion = "v1" // Group related constants together for better organization. + RetryInterval = 5 +) +``` + +- The "good" practice utilizes iota for a clear, concise, and auto-incrementing way to define enumerations, reducing the potential for errors and improving maintainability. + +```go +// bad +const ( + StatusActive = 0 + StatusInactive = 1 + StatusUnknown = 2 +) + +// good +const ( + StatusActive = iota // Use iota for simple and efficient constant enumerations. + StatusInactive + StatusUnknown +) +``` + +- Specifying types explicitly improves clarity, especially when the purpose or type of a constant might not be immediately obvious. Additionally, adding comments to exported constants or those whose purpose isn't clear from the name alone can greatly aid in understanding the code. + +```go +// bad +const serverAddress = "localhost:8080" +const debugMode = 1 // Is this supposed to be a boolean or an int? + +// good +const ServerAddress string = "localhost:8080" // Specify type for clarity. +// DebugMode indicates if the application should run in debug mode (true for debug mode). +const DebugMode bool = true +``` + +- By defining a contextKey type and making userIDKey of this type, you avoid potential collisions with other context keys. This approach leverages Go's type system to provide compile-time checks against misuse. + +```go +// bad +const userIDKey = "userID" + +// In this example, userIDKey is a string type, which can lead to conflicts or accidental misuse because string keys are prone to typos and collisions in a global namespace. + + +// good +type contextKey string + +const userIDKey contextKey = "userID" +``` + + +- Embedded types (such as mutexes) should be at the top of the field list within the struct, and there must be a blank line separating embedded fields from regular fields. + +```go +// bad +type Client struct { + version int + http.Client +} + +//good +type Client struct { + http.Client + + version int +} +``` + + +### 1.5 Unit Tests + +- The unit test filename naming convention is `example_test.go`. +- Write a test case for every important exportable function. +- Because the functions in the unit test file are not external, the exportable structures, functions, etc. can be uncommented. +- If `func (b *Bar) Foo` exists, the single test function can be `func TestBar_Foo`. + +## 2. Naming convention + +The naming convention is a very important part of the code specification. A uniform, short, and precise naming convention can greatly improve the readability of the code and avoid unnecessary bugs. + +### 2.1 Package Naming + +- The package name must be consistent with the directory name, try to use a meaningful and short package name, and do not conflict with the standard library. +- Package names are all lowercase, without uppercase or underscores, and use multi-level directories to divide the hierarchy. +- Item names can connect multiple words with dashes. +- Do not use plurals for the package name and the directory name where the package is located, for example, `net/url` instead of `net/urls`. +- Don't use broad, meaningless package names like common, util, shared or lib. +- The package name should be simple and clear, such as net, time, log. + + +### 2.2 Function Naming Conventions + +Function names should adhere to the following guidelines, inspired by OpenIM’s standards and Google’s Go Style Guide: + +- Use camel case for function names. Start with an uppercase letter for public functions (`MixedCaps`) and a lowercase letter for private functions (`mixedCaps`). +- Exceptions to this rule include code automatically generated by tools (e.g., `xxxx.pb.go`) and test functions that use underscores for clarity (e.g., `TestMyFunction_WhatIsBeingTested`). + +### 2.3 File and Directory Naming Practices + +To maintain consistency and readability across the OpenIM project, observe the following naming practices: + +**File Names:** +- Use underscores (`_`) as the default separator in filenames, keeping them short and descriptive. +- Both hyphens (`-`) and underscores (`_`) are allowed, but underscores are preferred for general use. + +**Script and Markdown Files:** +- Prefer hyphens (`-`) for shell scripts and Markdown (`.md`) files to enhance searchability and web compatibility. + +**Directories:** +- Name directories with hyphens (`-`) exclusively to separate words, ensuring consistency and readability. + +Remember to keep filenames lowercase and use meaningful, concise identifiers to facilitate better organization and navigation within the project. + +### 2.4 Structure Naming + +- The camel case is adopted, and the first letter is uppercase or lowercase according to the access control, such as `MixedCaps` or `mixedCaps`. +- Struct names should not be verbs, but should be nouns, such as `Node`, `NodeSpec`. +- Avoid using meaningless structure names such as Data and Info. +- The declaration and initialization of the structure should take multiple lines, for example: + +```go +// User multi-line declaration +type User struct { + name string + Email string +} + +// multi-line initialization +u := User{ + UserName: "belm", + Email: "nosbelm@qq.com", +} +``` + +### 2.5 Interface Naming + +- The interface naming rules are basically consistent with the structure naming rules: +- Interface names of individual functions suffixed with "er"" (e.g. Reader, Writer) can sometimes lead to broken English, but that's okay. +- The interface name of the two functions is named after the two function names, eg ReadWriter. +- An interface name for more than three functions, similar to a structure name. + +For example: + +```go +// Seeking to an offset before the start of the file is an error. +// Seeking to any positive offset is legal, but the behavior of subsequent +// I/O operations on the underlying object are implementation-dependent. +type Seeker interface { + Seek(offset int64, whence int) (int64, error) +} + +// ReadWriter is the interface that groups the basic Read and Write methods. +type ReadWriter interface { + reader + Writer +} +``` + +### 2.6 Variable Naming + +- Variable names must follow camel case, and the initial letter is uppercase or lowercase according to the access control decision. +- In relatively simple (few objects, highly targeted) environments, some names can be abbreviated from full words to single letters, for example: +- user can be abbreviated as u; +- userID can be abbreviated as uid. +- When using proper nouns, the following rules need to be followed: +- If the variable is private and the proper noun is the first word, use lowercase, such as apiClient. +- In other cases, the original wording of the noun should be used, such as APIClient, repoID, UserID. + +Some common nouns are listed below. + +```go +// A GonicMapper that contains a list of common initialisms taken from golang/lint +var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} +``` + +- If the variable type is bool, the name should start with Has, Is, Can or Allow, for example: + +```go +var hasConflict bool +var isExist bool +var canManage bool +var allowGitHook bool +``` + +- Local variables should be as short as possible, for example, use buf to refer to buffer, and use i to refer to index. +- The code automatically generated by the code generation tool can exclude this rule (such as the Id in `xxx.pb.go`) + +### 2.7 Constant Naming + +In Go, constants play a critical role in defining values that do not change throughout the execution of a program. Adhering to best practices in naming constants can significantly improve the readability and maintainability of your code. Here are some guidelines for constant naming: + +- **Camel Case Naming:** The name of a constant must follow the camel case notation. The initial letter should be uppercase or lowercase based on the access control requirements. Uppercase indicates that the constant is exported (visible outside the package), while lowercase indicates package-private visibility (visible only within its own package). + +- **Enumeration Type Constants:** For constants that represent a set of enumerated values, it's recommended to define a corresponding type first. This approach not only enhances type safety but also improves code readability by clearly indicating the purpose of the enumeration. + +**Example:** + +```go +// Code defines an error code type. +type Code int + +// Internal errors. +const ( + // ErrUnknown - 0: An unknown error occurred. + ErrUnknown Code = iota + // ErrFatal - 1: A fatal error occurred. + ErrFatal +) +``` + +In the example above, `Code` is defined as a new type based on `int`. The enumerated constants `ErrUnknown` and `ErrFatal` are then defined with explicit comments to indicate their purpose and values. This pattern is particularly useful for grouping related constants and providing additional context. + +### Global Variables and Constants Across Packages + +- **Use Constants for Global Variables:** When defining variables that are intended to be accessed across packages, prefer using constants to ensure immutability. This practice avoids unintended modifications to the value, which can lead to unpredictable behavior or hard-to-track bugs. + +- **Lowercase for Package-Private Usage:** If a global variable or constant is intended for use only within its own package, it should start with a lowercase letter. This clearly signals its limited scope of visibility, adhering to Go's access control mechanism based on naming conventions. + +**Guideline:** + +- For global constants that need to be accessed across packages, declare them with an uppercase initial letter. This makes them exported, adhering to Go's visibility rules. +- For constants used within the same package, start their names with a lowercase letter to limit their scope to the package. + +**Example:** + +```go +package config + +// MaxConnections - the maximum number of allowed connections. Visible across packages. +const MaxConnections int = 100 + +// minIdleTime - the minimum idle time before a connection is considered stale. Only visible within the config package. +const minIdleTime int = 30 +``` + +In this example, `MaxConnections` is a global constant meant to be accessed across packages, hence it starts with an uppercase letter. On the other hand, `minIdleTime` is intended for use only within the `config` package, so it starts with a lowercase letter. + +Following these guidelines ensures that your Go code is more readable, maintainable, and consistent with Go's design philosophy and access control mechanisms. + + + + +### 2.10 Using Context with IO or Inter-Process Communication (IPC) + +In Go, `context.Context` is a powerful construct for managing deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. It is particularly important in I/O operations or inter-process communication (IPC), where operations might need to be cancelled or timed out. + +#### Guideline: Use Context for IO and IPC + +- **Mandatory Use of Context:** When performing I/O operations or inter-process communication, it's crucial to use `context.Context` to manage the lifecycle of these operations. This includes setting deadlines, handling cancellation signals, and passing request-scoped values. + +#### Incorrect Example: Ignoring Context in an HTTP Call + +```go +package main + +import ( + "io/ioutil" + "net/http" + "log" +) + +// FetchData makes an HTTP GET request to the specified URL and returns the response body. +// This function does not use context, making it impossible to cancel the request or set a deadline. +func FetchData(url string) (string, error) { + resp, err := http.Get(url) // Incorrect: Ignoring context + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), nil +} + +func main() { + data, err := FetchData("http://example.com") + if err != nil { + log.Fatalf("Failed to fetch data: %v", err) + } + log.Println(data) +} +``` + +In this incorrect example, the `FetchData` function makes an HTTP GET request without using a `context`. This approach does not allow the request to be cancelled or a timeout to be set, potentially leading to resources being wasted if the server takes too long to respond or if the operation needs to be aborted for any reason. + +#### Correct Example: Using Context in an HTTP Call + +```go +package main + +import ( + "context" + "io/ioutil" + "net/http" + "log" + "time" +) + +// FetchDataWithContext makes an HTTP GET request to the specified URL using the provided context. +// This allows the request to be cancelled or timed out according to the context's deadline. +func FetchDataWithContext(ctx context.Context, url string) (string, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return "", err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), nil +} + +func main() { + // Create a context with a 5-second timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + data, err := FetchDataWithContext(ctx, "http://example.com") + if err != nil { + log.Fatalf("Failed to fetch data: %v", err) + } + log.Println(data) +} +``` + +In the correct example, `FetchDataWithContext` uses a context to make the HTTP GET request. This allows the operation to be cancelled or subjected to a timeout, as dictated by the context passed to it. The `context.WithTimeout` function is used in `main` to create a context that cancels the request if it takes longer than 5 seconds, demonstrating a practical use of context to manage operation lifecycle. + +### Best Practices for Using Context + +1. **Pass context as the first parameter of a function**, following the convention `func(ctx context.Context, ...)`. +2. **Never ignore the context** provided to you in functions that support it. Always use it in your I/O or IPC operations. +3. **Avoid storing context in a struct**. Contexts are meant to be passed around within the call stack, not stored. +4. **Use context's cancellation and deadline features** to control the lifecycle of blocking operations, especially in network I/O and IPC scenarios. +5. **Propagate context down the call stack** to any function that supports it, ensuring that your application can respond to cancellation signals and deadlines effectively. + +By adhering to these guidelines and examples, you can ensure that your Go applications handle I/O and IPC operations more reliably and efficiently, with proper support for cancellation, timeouts, and request-scoped values. + + + +## 3. 日志规范 + +启动时正常日志,打印流程日志,如链接mongo成功,注意不要打印密码等敏感信息。 + +启动时以及运行中异常终止日志,如果需要终止程序,调用ExitWithError + +运行时日志打印,对于错误日志,使用日志库打印,仅在最上层调用打印;对于debug日志,可以随意打印;对于关键日志打印 info; + +## 5.异常及错误处理 + +任何情况禁止使用panic + +错误需要wrap,并带上message和key value,用户排查问题;错误wrap仅一次,及函数本身出现的错误,或者调用项目之外的函数产生的错误。 + +用errs.New()替代errors.New() + + + + + +### 1.4 Panic Processing + +The use of `panic` should be carefully controlled in Go applications to ensure program stability and predictable error handling. Following are revised guidelines emphasizing the restriction on using `panic` and promoting alternative strategies for error handling and program termination. + +- **Prohibited in Business Logic:** Using `panic` within business logic processing is strictly prohibited. Business logic should handle errors gracefully and use error returns to propagate issues up the call stack. + +- **Restricted Use in Main Package:** In the main package, the use of `panic` should be reserved for situations where the program is entirely inoperable, such as failure to open essential files, inability to connect to the database, or other critical startup issues. Even in these scenarios, prefer using structured error handling to terminate the program. + +- **Prohibition on Exportable Interfaces:** Exportable interfaces must not invoke `panic`. They should handle errors gracefully and return errors as part of their contract. + +- **Prefer Errors Over Panic:** It is recommended to use error returns instead of panic to convey errors within a package. This approach promotes error handling that integrates smoothly with Go's error handling idioms. + +#### Alternative to Panic: Structured Program Termination + +To enforce these guidelines, consider implementing structured functions to terminate the program gracefully in the face of unrecoverable errors, while providing clear error messages. Here are two recommended functions: + +```go +// ExitWithError logs an error message and exits the program with a non-zero status. +func ExitWithError(err error) { + progName := filepath.Base(os.Args[0]) + fmt.Fprintf(os.Stderr, "%s exit -1: %+v\n", progName, err) + os.Exit(-1) +} + +// SIGTERMExit logs a warning message when the program receives a SIGTERM signal and exits with status 0. +func SIGTERMExit() { + progName := filepath.Base(os.Args[0]) + fmt.Fprintf(os.Stderr, "Warning %s receive process terminal SIGTERM exit 0\n", progName) +} +``` + +#### Example Usage: + +```go +import ( + _ "net/http/pprof" + + "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" +) + +func main() { + apiCmd := cmd.NewApiCmd() + apiCmd.AddPortFlag() + apiCmd.AddPrometheusPortFlag() + if err := apiCmd.Execute(); err != nil { + util.ExitWithError(err) + } +} +``` + +In this example, `ExitWithError` is used to terminate the program when an unrecoverable error occurs, providing a clear error message to stderr and exiting with a non-zero status. This approach ensures that critical errors are logged and the program exits in a controlled manner, facilitating troubleshooting and maintaining the stability of the application. + + + +### 1.3 Error Handling + +- `error` is returned as the value of the function, `error` must be handled, or the return value assigned to explicitly ignore. For `defer xx.Close()`, there is no need to explicitly handle it. + +```go +func load() error { +// normal code +} + +// bad +load() + +//good + _ = load() +``` + +- When `error` is returned as the value of a function and there are multiple return values, `error` must be the last parameter. + +```go +// bad +func load() (error, int) { +// normal code +} + +//good +func load() (int, error) { +// normal code +} +``` + +- Perform error handling as early as possible and return as early as possible to reduce nesting. + +```go +// bad +if err != nil { +// error code +} else { +// normal code +} + +//good +if err != nil { +// error handling +return err +} +// normal code +``` + +- If you need to use the result of the function call outside if, you should use the following method. + +```go +// bad +if v, err := foo(); err != nil { +// error handling +} + +// good +v, err := foo() +if err != nil { +// error handling +} +``` + +- Errors should be judged independently, not combined with other logic. + +```go +// bad +v, err := foo() +if err != nil || v == nil { + // error handling + return err +} + +//good +v, err := foo() +if err != nil { + // error handling + return err +} + +if v == nil { + // error handling + return errors. New("invalid value v") +} +``` + +- If the return value needs to be initialized, use the following method. + +```go +v, err := f() +if err != nil { + // error handling + return // or continue. +} +``` + +- Bug description suggestions +- Error descriptions start with a lowercase letter and do not end with punctuation, for example: + +```go +// bad +errors.New("Redis connection failed") +errors.New("redis connection failed.") + +// good +errors.New("redis connection failed") +``` + +- Tell users what they can do, not what they can't. +- When declaring a requirement, use must instead of should. For example, `must be greater than 0, must match regex '[a-z]+'`. +- When declaring that a format is incorrect, use must not. For example, `must not contain`. +- Use may not when declaring an action. For example, `may not be specified when otherField is empty, only name may be specified`. +- When quoting a literal string value, indicate the literal in single quotes. For example, `ust not contain '..'`. +- When referencing another field name, specify that name in backticks. For example, must be greater than `request`. +- When specifying unequal, use words instead of symbols. For example, `must be less than 256, must be greater than or equal to 0 (do not use larger than, bigger than, more than, higher than)`. +- When specifying ranges of numbers, use inclusive ranges whenever possible. +- Go 1.13 or above is recommended, and the error generation method is `fmt.Errorf("module xxx: %w", err)`. + +### 1.6 Type assertion failure handling + +- A single return value from a type assertion will panic for an incorrect type. Always use the "comma ok" idiom. + +```go +// bad +t := n.(int) + +//good +t, ok := n.(int) +if !ok { +// error handling +} +``` + + + +### 2.8 Error naming + +- The Error type should be written in the form of FooError. + +```go +type ExitError struct { +// .... +} +``` + +- The Error variable is written in the form of ErrFoo. + +```go +var ErrFormat = errors. New("unknown format") +``` + +For non-standard Err naming, CICD will report an error + + +### 2.9 Handling Errors Properly + +In Go, proper error handling is crucial for creating reliable and maintainable applications. It's important to ensure that errors are not ignored or discarded, as this can lead to unpredictable behavior and difficult-to-debug issues. Here are the guidelines and examples regarding the proper handling of errors. + +#### Guideline: Do Not Discard Errors + +- **Mandatory Error Propagation:** When calling a function that returns an error, the calling function must handle or propagate the error, instead of ignoring it. This approach ensures that errors are not silently ignored, allowing higher-level logic to make informed decisions about error handling. + +#### Incorrect Example: Discarding an Error + +```go +package main + +import ( + "io/ioutil" + "log" +) + +func ReadFileContent(filename string) string { + content, _ := ioutil.ReadFile(filename) // Incorrect: Error is ignored + return string(content) +} + +func main() { + content := ReadFileContent("example.txt") + log.Println(content) +} +``` + +In this incorrect example, the error returned by `ioutil.ReadFile` is ignored. This can lead to situations where the program continues execution even if the file doesn't exist or cannot be accessed, potentially causing more cryptic errors downstream. + +#### Correct Example: Propagating an Error + +```go +package main + +import ( + "io/ioutil" + "log" +) + +// ReadFileContent attempts to read and return the content of the specified file. +// It returns an error if reading fails. +func ReadFileContent(filename string) (string, error) { + content, err := ioutil.ReadFile(filename) + if err != nil { + // Correct: Propagate the error + return "", err + } + return string(content), nil +} + +func main() { + content, err := ReadFileContent("example.txt") + if err != nil { + log.Fatalf("Failed to read file: %v", err) + } + log.Println(content) +} +``` + +In the correct example, the error returned by `ioutil.ReadFile` is propagated back to the caller. The `main` function then checks the error and terminates the program with an appropriate error message if an error occurred. This approach ensures that errors are handled appropriately, and the program does not proceed with invalid state. + +### Best Practices for Error Handling + +1. **Always check the error returned by a function.** Do not ignore it. +2. **Propagate errors up the call stack unless they can be handled gracefully at the current level.** +3. **Provide context for errors when propagating them, making it easier to trace the source of the error.** This can be achieved using `fmt.Errorf` with the `%w` verb or dedicated wrapping functions provided by some error handling packages. +4. **Log the error at the point where it is handled or makes the program to terminate, to provide insight into the failure.** + +By following these guidelines, you ensure that your Go applications handle errors in a consistent and effective manner, improving their reliability and maintainability. + + + + + + + + + + + + + +## Suggestions + + + +## 3. Comment specification + +- Each exportable name must have a comment, which briefly introduces the exported variables, functions, structures, interfaces, etc. +- All single-line comments are used, and multi-line comments are prohibited. +- Same as the code specification, single-line comments should not be too long, and no more than 120 characters are allowed. If it exceeds, please use a new line to display, and try to keep the format elegant. +- A comment must be a complete sentence, starting with the content to be commented and ending with a period, `the format is // name description.`. For example: + +```go +// bad +// logs the flags in the flagset. +func PrintFlags(flags *pflag. FlagSet) { +// normal code +} + +//good +// PrintFlags logs the flags in the flagset. +func PrintFlags(flags *pflag. FlagSet) { +// normal code +} +``` + +- All commented out code should be deleted before submitting code review, otherwise, it should explain why it is not deleted, and give follow-up processing suggestions. + +- Multiple comments can be separated by blank lines, as follows: + +```go +// Package superman implements methods for saving the world. +// +// Experience has shown that a small number of procedures can prove +// helpful when attempting to save the world. +package superman +``` + +### 3.1 Package Notes + +- Each package has one and only one package-level annotation. +- Package comments are uniformly commented with // in the format of `// Package package description`, for example: + +```go +// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce +// useful helper functions. +package genericclioptions +``` + +### 3.2 Variable/Constant Comments + +- Each variable/constant that can be exported must have a comment description, `the format is // variable name variable description`, for example: + +```go +// ErrSigningMethod defines invalid signing method error. +var ErrSigningMethod = errors. New("Invalid signing method") +``` + +- When there is a large block of constant or variable definition, you can comment a general description in front, and then comment the definition of the constant in detail before or at the end of each line of constant, for example: + +```go +// Code must start with 1xxxxx. +const ( + // ErrSuccess - 200: OK. + ErrSuccess int = iota + 100001 + + // ErrUnknown - 500: Internal server error. + ErrUnknown + + // ErrBind - 400: Error occurred while binding the request body to the struct. + ErrBind + + // ErrValidation - 400: Validation failed. + ErrValidation +) +``` + +### 3.3 Structure Annotation + +- Each structure or interface that needs to be exported must have a comment description, the format is `// structure name structure description.`. +- The name of the exportable member variable in the structure, if the meaning is not clear, a comment must be given and placed before the member variable or at the end of the same line. For example: + +```go +// User represents a user restful resource. It is also used as gorm model. +type User struct { + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty"` + + Nickname string `json:"nickname" gorm:"column:nickname"` + Password string `json:"password" gorm:"column:password"` + Email string `json:"email" gorm:"column:email"` + Phone string `json:"phone" gorm:"column:phone"` + IsAdmin int `json:"isAdmin,omitempty" gorm:"column:isAdmin"` +} +``` + +### 3.4 Method Notes + +Each function or method that needs to be exported must have a comment, the format is // function name function description., for examplelike: + +```go +// BeforeUpdate run before update database record. +func (p *Policy) BeforeUpdate() (err error) { +// normal code + return nil +} +``` + +### 3.5 Type annotations + +- Each type definition and type alias that needs to be exported must have a comment description, the format is `// type name type description.`, for example: + +```go +// Code defines an error code type. +type Code int +``` + +## 4. Type + +### 4.1 Strings + +- Empty string judgment. + +```go +// bad +if s == "" { + // normal code +} + +//good +if len(s) == 0 { + // normal code +} +``` + +- `[]byte`/`string` equality comparison. + +```go +// bad +var s1 []byte +var s2 []byte +... +bytes.Equal(s1, s2) == 0 +bytes.Equal(s1, s2) != 0 + +//good +var s1 []byte +var s2 []byte +... +bytes. Compare(s1, s2) == 0 +bytes. Compare(s1, s2) != 0 +``` + +- Complex strings use raw strings to avoid character escaping. + +```go +// bad +regexp.MustCompile("\\.") + +//good +regexp.MustCompile(`\.`) +``` + +### 4.2 Slicing + +- Empty slice judgment. + +```go +// bad +if len(slice) = 0 { + // normal code +} + +//good +if slice != nil && len(slice) == 0 { + // normal code +} +``` + +The above judgment also applies to map and channel. + +- Declare a slice. + +```go +// bad +s := []string{} +s := make([]string, 0) + +//good +var s[]string +``` + +- slice copy. + +```go +// bad +var b1, b2 []byte +for i, v := range b1 { + b2[i] = v +} +for i := range b1 { + b2[i] = b1[i] +} + +//good +copy(b2, b1) +``` + +- slice added. + +```go +// bad +var a, b []int +for _, v := range a { + b = append(b, v) +} + +//good +var a, b []int +b = append(b, a...) +``` + +### 4.3 Structure + +- struct initialization. + +The struct is initialized in multi-line format. + +```go +type user struct { +Id int64 +name string +} + +u1 := user{100, "Colin"} + +u2 := user{ + Id: 200, + Name: "Lex", +} +``` + +- + + + +## 5. Control Structure + +### 5.1 if + +- if accepts the initialization statement, the convention is to create local variables in the following way. + +```go +if err := loadConfig(); err != nil { +// error handling +return err +} +``` + +- if For variables of bool type, true and false judgments should be made directly. + +```go +var isAllow bool +if isAllow { +// normal code +} +``` + +### 5.2 for + +- Create local variables using short declarations. + +```go +sum := 0 +for i := 0; i < 10; i++ { + sum += 1 +} +``` + +- Don't use defer in for loop, defer will only be executed when the function exits. + +```go +// bad +for file := range files { + fd, err := os. Open(file) + if err != nil { + return err +} +defer fd. Close() +// normal code +} + +//good +for file := range files { + func() { + fd, err := os. Open(file) + if err != nil { + return err + } + defer fd. Close() + // normal code + }() +} +``` + +### 5.3 range + +- If only the first item (key) is needed, discard the second. + +```go +for keyIndex := range keys { +// normal code +} +``` + +- If only the second item is required, underline the first item. + +```go +sum := 0 +for _, value := range array { + sum += value +} +``` + +### 5.4 switch + +- must have default. + +```go +switch os := runtime.GOOS; os { + case "linux": + fmt.Println("Linux.") + case "darwin": + fmt.Println("OS X.") + default: + fmt.Printf("%s.\n", os) +} +``` + +### 5.5 goto + +- Business code prohibits the use of goto. +- Try not to use frameworks or other low-level source code. + +## 6. Functions + +- Incoming variables and return variables start with a lowercase letter. +- The number of function parameters cannot exceed 5. +- Function grouping and ordering +- Functions should be sorted in rough calling order. +- Functions in the same file should be grouped by receiver. +- Try to use value transfer instead of pointer transfer. +- The incoming parameters are map, slice, chan, interface, do not pass pointers. + +### 6.1 Function parameters + +- If the function returns two or three arguments of the same type, or if the meaning of the result is not clear from the context, use named returns, otherwise it is not recommended to use named returns, for example: + +```go +func coordinate() (x, y float64, err error) { +// normal code +} +``` + +- Both incoming and returned variables start with a lowercase letter. +- Try to pass by value instead of pointer. +- The number of parameters cannot exceed 5. +- Multiple return values can return up to three, and if there are more than three, please use struct. + +### 6.2 defer + +- When resources are created, resources should be released immediately after defer (defer can be used boldly, the performance of defer is greatly improved in Go1.14 version, and the performance loss of defer can be ignored even in performance-sensitive businesses). +- First judge whether there is an error, and then defer to release resources, for example: + +```go +rep, err := http. Get(url) +if err != nil { + return err +} + +defer resp.Body.Close() +``` + +### 6.3 Method Receiver + +- It is recommended to use the lowercase of the first English letter of the class name as the name of the receiver. +- Don't use a single character in the name of the receiver when the function exceeds 20 lines. +- The name of the receiver cannot use confusing names such as me, this, and self. + +### 6.4 Nesting + +- The nesting depth cannot exceed 4 levels. + +### 6.5 Variable Naming + +- The variable declaration should be placed before the first use of the variable as far as possible, following the principle of proximity. +- If the magic number appears more than twice, it is forbidden to use it and use a constant instead, for example: + +```go +// PI... +const Price = 3.14 + +func getAppleCost(n float64) float64 { +return Price * n +} + +func getOrangeCost(n float64) float64 { +return Price * n +} +``` + +## 7. GOPATH setting specification + +- After Go 1.11, the GOPATH rule has been weakened. Existing code (many libraries must have been created before 1.11) must conform to this rule. It is recommended to keep the GOPATH rule to facilitate code maintenance. +- Only one GOPATH is recommended, multiple GOPATHs are not recommended. If multiple GOPATHs are used, the bin directory where compilation takes effect is under the first GOPATH. + + + +## 8. Dependency Management + +- Go 1.11 and above must use Go Modules. +- When using Go Modules as a dependency management project, it is not recommended to submit the vendor directory. +- When using Go Modules as a dependency management project, the go.sum file must be submitted. + +### 9. Best Practices + +- Minimize the use of global variables, but pass parameters, so that each function is "stateless". This reduces coupling and facilitates division of labor and unit testing. +- Verify interface compliance at compile time, for example: + +```go +type LogHandler struct { + h http.Handler + log *zap. Logger +} +var_http.Handler = LogHandler{} +``` + +- When the server processes a request, it should create a context, save the relevant information of the request (such as requestID), and pass it in the function call chain. + +### 9.1 Performance + +- string represents an immutable string variable, modifying string is a relatively heavy operation, and basically needs to re-apply for memory. Therefore, if there is no special need, use []byte more when you need to modify. +- Prefer strconv over fmt. + +### 9.2 Precautions + +- append Be careful about automatically allocating memory, append may return a newly allocated address. +- If you want to directly modify the value of the map, the value can only be a pointer, otherwise the original value must be overwritten. +- map needs to be locked during concurrency. +- The conversion of interface{} cannot be checked during compilation, it can only be checked at runtime, be careful to cause panic. + + + + + +## 10 Golang CI Lint + +- Golang CI Lint is a fast Go linters runner. It runs linters in parallel, uses caching, and works well with all environments, including CI. + +**In local development, you can use the following command to install Golang CI Lint: ** + +```bash +make lint +``` + +**In CI/CD, Check the Github Actions status code below after you submit the code directly** + +[![OpenIM golangci-lint](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml) + +golangci lint can select the types of tools, refer to the official documentation: [https://golangci-lint.run/usage/linters/](https://golangci-lint.run/usage/linters/) + +The types of comments we currently use include: [https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml](https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml) the `linters.enable` field in the file. + +e.g: + +```yaml +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + # enable-all: true + disable-all: true + enable: + - typecheck # Basic type checking + - gofmt # Format check + - govet # Go's standard linting tool + - gosimple # Suggestions for simplifying code + - misspell # Spelling mistakes + - staticcheck # Static analysis + - unused # Checks for unused code + - goimports # Checks if imports are correctly sorted and formatted + - godot # Checks for comment punctuation + - bodyclose # Ensures HTTP response body is closed + - errcheck # Checks for missed error returns + fast: true +``` + +Add that Chinese comments are not allowed in go code, please write a complete golangci lint specification on the basis of the above. + + +### 10.1 Configuration Document + +This configuration document is designed to configure the operational parameters of OpenIM (a hypothetical or specific code analysis tool), customize output formats, and provide detailed settings for specific code checkers (linters). Below is a summary of the document drafted based on the provided configuration information. + +#### 10.1 Runtime Options + +- **Concurrency** (`concurrency`): Default to use the available CPU count, can be manually set to 4 for parallel analysis. +- **Timeout** (`timeout`): Timeout duration for analysis operations, default is 1 minute, set here to 5 minutes. +- **Issue Exit Code** (`issues-exit-code`): Exit code defaults to 1 if at least one issue is found. +- **Test Files** (`tests`): Whether to include test files, defaults to true. +- **Build Tags** (`build-tags`): Specify build tags used by all linters, defaults to an empty list. Example adds `mytag`. +- **Skip Directories** (`skip-dirs`): Configure which directories' issues are not reported, defaults to empty, but some default directories are independently skipped. +- **Skip Files** (`skip-files`): Specify files where issues should not be reported, supports regular expressions. + +#### 10.2 Output Configuration + +- **Format** (`format`): Set output format, default is "colored-line-number". +- **Print Issued Lines** (`print-issued-lines`): Whether to print the lines where issues occur, defaults to true. +- **Print Linter Name** (`print-linter-name`): Whether to print the linter name at the end of issue text, defaults to true. +- **Uniqueness Filter** (`uniq-by-line`): Whether to make issue outputs unique per line, defaults to true. +- **Path Prefix** (`path-prefix`): Prefix to add to output file references, defaults to no prefix. +- **Sort Results** (`sort-results`): Sort results by file path, line number, and column number. + +#### 10.3 Linters Settings + +In the configuration file, the `linters-settings` section allows detailed configuration of individual linters. Below are examples of specific linters settings and their purposes: + +- **bidichk**: Used to check bidirectional text characters, ensuring correct display direction of text, especially when dealing with mixed left-to-right (LTR) and right-to-left (RTL) text. + +- **dogsled**: Monitors excessive use of blank identifiers (`_`) in assignment operations, which may obscure data processing errors or unclear logic. + +- **dupl**: Identifies duplicate code blocks, helping developers avoid code redundancy. The `threshold` parameter in settings allows adjustment of code similarity threshold triggering warnings. + +- **errcheck**: Checks for unhandled errors. In Go, error handling is achieved by checking function return values. This linter helps ensure all errors are properly handled. + +- **exhaustive**: Checks if `switch` statements include all possible values of an enum type, ensuring exhaustiveness of code. This helps avoid forgetting to handle certain cases. + +#### 10.4 Example: `errcheck` + +**Incorrect Code Example**: + +```go +package main + +import ( + "fmt" + "os" +) + +func main() { + f, _ := os.Open("filename.ext") + defer f.Close() +} +``` + +**Issue**: In the above code, the error return value of `os.Open` function is explicitly ignored. This is a common mistake as it may lead to unhandled errors and hard-to-trace bugs. + +**Correct Form**: + +```go +package main + +import ( + "fmt" + "os" +) + +func main() { + f, err := os.Open("filename.ext") + if err != nil { + fmt.Printf("error opening file: %v\n", err) + return + } + defer f.Close() +} +``` + +In the correct form, by checking the error (`err`) returned by `os.Open`, we gracefully handle error cases rather than simply ignoring them. + +#### 10.5 Example: `gofmt` + +**Incorrect Code Example**: + +```go +package main +import "fmt" +func main() { +fmt.Println("Hello, world!") +} +``` + +**Issue**: This code snippet doesn't follow Go's standard formatting rules, for example, incorrect indentation of `fmt.Println`. + +**Correct Form**: + +```go +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world!") +} +``` + +Using `gofmt` tool can automatically fix such formatting issues, ensuring the code adheres to the coding standards of the Go community. + +#### 10.6 Example: `unused` + +**Incorrect Code Example**: + +```go +package main + +func helper() {} + +func main() {} +``` + +**Issue**: The `helper` function is defined but not called anywhere, indicating potential redundant code or missing functionality implementation. + +**Correct Form**: + +```go +package main + +// If the helper function is indeed needed, ensure it's used properly. +func helper() { + // Implement the function's functionality or ensure it's called elsewhere +} + +func main() { + helper() +} +``` + +To improve the section on Linters settings in the document, we'll expand with more detailed explanations and reinforce understanding through examples. + +#### 10.7 Example: `dogsled` + +**Incorrect Code Example**: + +```go +func getValues() (int, int, int) { + return 1, 2, 3 +} + +func main() { + _, _, val := getValues() + fmt.Println(val) // Only interested in the third return value +} +``` + +**Explanation**: In the above code, we use two blank identifiers to ignore the first two return values. Excessive use of blank identifiers can make code reading difficult. + +**Improved Code**: +Consider refactoring the function or the usage of return values to reduce the need for blank identifiers or explicitly comment why ignoring certain values is safe. + +#### 10.8: `exhaustive` + +**Incorrect Code Example**: + +```go +type Fruit int + +const ( + Apple Fruit = iota + Banana + Orange +) + +func getFruitName(f Fruit) string { + switch f { + case Apple: + return "Apple" + case Banana: + return "Banana" + // Missing handling for Orange + } + return "Unknown" +} +``` + +**Explanation**: In this code, the `switch` statement doesn't cover all possible values of the `Fruit` type; the case for `Orange` is missing. + +**Improved Code**: + +```go +func getFruitName(f Fruit) string { + switch f { + case Apple: + return "Apple" + case Banana: + return "Banana" + case Orange: + return "Orange" + } + return "Unknown" +} +``` + +By adding the missing `case`, we ensure the `switch` statement is exhaustive, handling every possible enum value. + +#### 10.9 Optimization of Configuration Files and Application of Code Analysis Tools + +Through these examples, we demonstrate how to improve code quality by identifying and fixing common coding issues. OpenIM's configuration files allow developers to customize linters' behavior according to project requirements, ensuring code compliance with predefined quality standards and style guidelines. + +By employing these tools and configuration strategies, teams can reduce the number of bugs, enhance code maintainability, and facilitate efficient collaboration during code review processes. + + + + + + + diff --git a/scripts-new/.spelling_failures b/scripts-new/.spelling_failures new file mode 100644 index 000000000..347246f2e --- /dev/null +++ b/scripts-new/.spelling_failures @@ -0,0 +1,31 @@ +CHANGELOG +go.mod +go.sum +third_party/ +translations/ +logs +.git +.golangci.yml +docs/readme/README_uk.md +docs/readme/README_cs.md +docs/readme/README_hu.md +docs/readme/README_es.md +docs/readme/README_fa.md +docs/readme/README_fr.md +docs/readme/README_de.md +docs/readme/README_pl.md +docs/readme/README_id.md +docs/readme/README_fi.md +docs/readme/README_ml.md +docs/readme/README_ja.md +docs/readme/README_nl.md +docs/readme/README_it.md +docs/readme/README_ru.md +docs/readme/README_pt_BR +docs/readme/README_eo.md +docs/readme/README_ko.md +docs/readme/README_ar.md +docs/readme/README_vi.md +docs/readme/README_da.md +docs/readme/README_el.md +docs/readme/README_tr.md diff --git a/scripts-new/README.md b/scripts-new/README.md new file mode 100644 index 000000000..b55049a7b --- /dev/null +++ b/scripts-new/README.md @@ -0,0 +1,311 @@ +# OpenIM Scripts Directory Structure + +- [OpenIM Scripts Directory Structure](#openim-scripts-directory-structure) + - [log directory](#log-directory) + - [Supported platforms](#supported-platforms) + - [Get started quickly - demo.sh](#get-started-quickly---demosh) + - [Guide: Using and Understanding OpenIM Utility Functions](#guide-using-and-understanding-openim-utility-functions) + - [Table of Contents](#table-of-contents) + - [1. Checking the Status of Services by Ports](#1-checking-the-status-of-services-by-ports) + - [Function: `openim::util::check_ports`](#function-openimutilcheck_ports) + - [Example:](#example) + - [2. Checking the Status of Services by Process Names](#2-checking-the-status-of-services-by-process-names) + - [Function: `openim::util::check_process_names`](#function-openimutilcheck_process_names) + - [Example:](#example-1) + - [3. Stopping Services by Ports](#3-stopping-services-by-ports) + - [Function: `openim::util::stop_services_on_ports`](#function-openimutilstop_services_on_ports) + - [Example:](#example-2) + - [4. Stopping Services by Process Names](#4-stopping-services-by-process-names) + - [Function: `openim::util::stop_services_with_name`](#function-openimutilstop_services_with_name) + - [Example:](#example-3) + - [system management and installation of openim via Linux system](#system-management-and-installation-of-openim-via-linux-system) + - [examples](#examples) + + +This document outlines the directory structure for scripts in the OpenIM Server project. These scripts play a critical role in various areas like building, deploying, running and managing the services of OpenIM. + +```bash +scripts/ +├── README.md # Documentation for the scripts directory. +├── advertise.sh # Script to advertise or broadcast services. +├── batch_start_all.sh # Batch script to start all services. +├── build-all-service.sh # Script to build all services. +├── build.cmd # Command script for building (usually for Windows). +├── check-all.sh # Check script for all components or services. +├── cherry-pick.sh # Helper script for git cherry-pick operations. +├── common.sh # Common utilities and shared functions. +├── coverage.awk # AWK script for processing code coverage data. +├── coverage.sh # Script to gather and report code coverage. +├── demo.sh # Demonstration or example script. +├── docker-check-service.sh # Docker script to check services' status. +├── docker-start-all.sh # Docker script to start all containers/services. +├── ensure-tag.sh # Ensure correct tags or labeling. +├── env_check.sh # Environment verification and checking. +├── gen-swagger-docs.sh # Script to generate Swagger documentation. +├── genconfig.sh # Generate configuration files. +├── gendoc.sh # General documentation generation script. +├── githooks # Directory containing git hooks. +│ ├── commit-msg # Git hook for commit messages. +│ ├── pre-commit # Pre-commit git hook. +│ └── pre-push # Pre-push git hook. +├── golangci.yml # Configuration for GolangCI linting. +├── init-config.sh # Initialize configurations. +├── init-env.sh # Initialize the environment. +├── init-pwd.sh # Initialize or set password. +├── install # Installation scripts directory. +│ ├── README.md # Installation documentation. +│ ├── common.sh # Common utilities for installation. +│ ├── dependency.sh # Script to install dependencies. +│ ├── environment.sh # Set up the environment during installation. +│ ├── install-protobuf.sh # Install Protocol Buffers. +│ ├── install.sh # Main installation script. +│ ├── openim-api.sh # Install OpenIM API. +│ ├── openim-crontask.sh # Install OpenIM crontask. +│ ├── openim-man.sh # Install OpenIM management tool. +│ ├── openim-msggateway.sh # Install OpenIM message gateway. +│ ├── openim-msgtransfer.sh # Install OpenIM message transfer. +│ ├── openim-push.sh # Install OpenIM push service. +│ ├── openim-rpc.sh # Install OpenIM RPC. +│ ├── openim-tools.sh # Install OpenIM tools. +│ ├── test.sh # Installation testing script. +│ └── vimrc # Vim configuration file. +├── install-im-server.sh # Script to install the OpenIM server. +├── install_im_compose.sh # Install OpenIM using Docker Compose. +├── lib # Library or utility scripts directory. +│ ├── chat.sh # Utilities related to chat. +│ ├── color.sh # Color-related utilities. +│ ├── golang.sh # Golang utilities. +│ ├── init.sh # Initialization utilities. +│ ├── logging.sh # Logging utilities. +│ ├── release.sh # Release related utilities. +│ ├── util.sh # General utility functions. +│ └── version.sh # Version management utilities. +├── list-feature-tests.sh # Script to list feature tests. +├── make-rules # Makefile rule templates. +│ ├── common.mk # Common make rules. +│ ├── copyright.mk # Copyright related make rules. +│ ├── dependencies.mk # Dependency management rules. +│ ├── gen.mk # Generic or general rules. +│ ├── golang.mk # Golang-specific make rules. +│ ├── image.mk # Image or container-related rules. +│ ├── release.mk # Release specific rules. +│ ├── swagger.mk # Swagger documentation rules. +│ └── tools.mk # Tooling-related make rules. +├── mongo-init.sh # MongoDB initialization script. +├── release.sh # Script for releasing or deployment. +├── run-in-gopath.sh # Script to run commands within GOPATH. +├── start-all.sh # Script to start all services. +├── start.bat # Batch file to start services (usually for Windows). +├── stop-all.sh # Script to stop all services. +├── template # Directory containing template files. +│ ├── LICENSE # License template. +│ ├── LICENSE_TEMPLATES # Collection of license templates. +│ ├── boilerplate.txt # Boilerplate template. +│ ├── footer.md.tmpl # Footer template for markdown. +│ ├── head.md.tmpl # Header template for markdown. +│ └── project_README.md # Project README template. +├── update-generated-docs.sh # Update generated documentation. +├── update-yamlfmt.sh # Update YAML formatting. +├── verify-pkg-names.sh # Verify package names. +├── verify-shellcheck.sh # Shell script linting verification. +├── verify-spelling.sh # Spelling verification script. +├── verify-typecheck.sh # Type checking verification. +├── verify-yamlfmt.sh # Verify YAML format. +└── wait-for-it.sh # Script to wait for a condition or service to be ready. +``` + +The purpose of having a structured scripts directory like this is to make the operations of OpenIM Server clear and easy to manage. Each script has its own responsibility, making it easier to maintain and update. It's also helpful for newcomers who can easily understand what each part of the system is doing by just looking at this directory structure. + +Each directory and script in the structure should be understood as a part of a larger whole. All scripts work together to ensure the smooth operation and maintenance of the OpenIM Server. + + +## log directory + +**PATH:** `scripts/lib/logging.sh` + ++ [log details](../docs/contrib/bash-log.md) + +## Supported platforms + +- Linux x86_64 (linux_amd64) : 64-bit Linux for most desktop and server systems. + +- Windows x86_64 (windows_amd64) : 64-bit version for most Windows operating systems. + +- macOS x86_64 (darwin_amd64) : 64-bit version for Apple Macintosh computers. + +- Linux ARM64 (linux_arm64) : For ARM-based 64-bit Linux systems such as Raspberry Pi 4 and Jetson Nano. + +- Linux s390x (linux_s390x) : 64-bit Linux for IBM System z hosts. + +- Linux MIPS64 (linux_mips64) : 64-bit Linux for MIPS architecture. + +- Linux MIPS64LE (linux_mips64le) : Suitable for 64-bit Linux systems with little endian MIPS architecture. + +## Get started quickly - demo.sh + +Is the `demo.sh` script teaching you how to quickly get started with OpenIM development and use + + +Steps to run demo: + +```sh +$ make demo +``` + +More about `make` read: + ++ [makefile](../docs/contrib/go-code.md) + +Instructions for producing the demo movie: + +```bash +# Create temporary directory +mkdir /tmp/kb-demo +cd /tmp/kb-demo + +asciinema rec +/scripts/demo/run.sh + + to terminate the script + to terminate the asciinema recording + to save the recording locally + +# Edit the recorded file by editing the controller-gen path +# Once you are happy with the recording, use svg-term program to generate the svg + +svg-term --cast= --out _output/demo.svg --window +``` + +Here you will learn how to test a script, We take the four functions for starting and checking a service as an example. + +## Guide: Using and Understanding OpenIM Utility Functions + +This document provides an overview of the four utility functions designed for managing processes and services. These functions can check the status of services based on ports and process names, as well as stop services based on the same criteria. + +### Table of Contents +- [1. Checking the Status of Services by Ports](#checking-the-status-of-services-by-ports) +- [2. Checking the Status of Services by Process Names](#checking-the-status-of-services-by-process-names) +- [3. Stopping Services by Ports](#stopping-services-by-ports) +- [4. Stopping Services by Process Names](#stopping-services-by-process-names) + +### 1. Checking the Status of Services by Ports + +#### Function: `openim::util::check_ports` + +This function checks the status of services running on specified ports. + +**Usage**: + +```bash +$ openim::util::check_ports ... +``` + +**Design**: + +- The function iterates through each provided port. +- It uses the `lsof` command to identify if there is a service running on the specified port. +- If a service is running, it logs the command, PID, and start time of the service. +- If a service is not running, it logs that the port is not started. +- If any service is not running, the function returns a status of 1. + +#### Example: + +```bash +$ openim::util::check_ports 8080 8081 8082 +``` + +### 2. Checking the Status of Services by Process Names + +#### Function: `openim::util::check_process_names` + +This function checks the status of services based on their process names. + +**Usage**: + +```bash +$ openim::util::check_process_names ... +``` + +**Design**: + +- The function uses `pgrep` to find process IDs associated with the given process names. +- If processes are found, it logs the command, PID, associated port, and start time. +- If no processes are found for a name, it logs that the process is not started. +- If any process is not running, the function returns a status of 1. + +#### Example: + +```bash +$ openim::util::check_process_names nginx mysql redis +``` + +### 3. Stopping Services by Ports + +#### Function: `openim::util::stop_services_on_ports` + +This function attempts to stop services running on the specified ports. + +**Usage**: + +```bash +$ openim::util::stop_services_on_ports ... +``` + +**Design**: + +- The function uses the `lsof` command to identify services running on the specified ports. +- If a service is running on a port, it tries to terminate the associated process using the `kill` command. +- It logs successful terminations and any failures. +- If any service couldn't be stopped, the function returns a status of 1. + +#### Example: + +```bash +$ openim::util::stop_services_on_ports 8080 8081 8082 +``` + +### 4. Stopping Services by Process Names + +#### Function: `openim::util::stop_services_with_name` + +This function attempts to stop services based on their process names. + +**Usage**: + +```bash +$ openim::util::stop_services_with_name ... +``` + +**Design**: + +- The function uses `pgrep` to identify processes associated with the specified names. +- If processes are found, it tries to terminate them using the `kill` command. +- It logs successful terminations and any failures. +- If any service couldn't be stopped, the function returns a status of 1. + +#### Example: + +```bash +$ openim::util::stop_services_with_name nginx apache +``` + +### system management and installation of openim via Linux system + +```bash +$ ./scripts/install/install.sh +``` + +## examples +Scripts to perform various build, install, analysis, etc operations. + +The script directory design of OpenIM and the writing of scripts and tools refer to many excellent open source projects, such as helm, iam, kubernetes, docker, etc. + +Maybe they'll give you inspiration for later maintenance... + +These scripts keep the root level Makefile small and simple. + +Examples: + +* https://github.com/kubernetes/helm/tree/master/scripts +* https://github.com/cockroachdb/cockroach/tree/master/scripts +* https://github.com/hashicorp/terraform/tree/master/scripts \ No newline at end of file diff --git a/scripts-new/advertise.sh b/scripts-new/advertise.sh new file mode 100644 index 000000000..958b4e3ae --- /dev/null +++ b/scripts-new/advertise.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + + +. $(dirname ${BASH_SOURCE})/lib/init.sh + +trap 'openim::util::onCtrlC' INT + +print_with_delay() { + text="$1" + delay="$2" + + for i in $(seq 0 $((${#text}-1))); do + printf "${text:$i:1}" + sleep $delay + done + printf "\n" +} + +print_progress() { + total="$1" + delay="$2" + + printf "[" + for i in $(seq 1 $total); do + printf "#" + sleep $delay + done + printf "]\n" +} + +function openim_logo() { + # Set text color to cyan for header and URL + echo -e "\033[0;36m" + + # Display fancy ASCII Art logo + # look http://patorjk.com/software/taag/#p=display&h=1&v=1&f=Doh&t=OpenIM + print_with_delay ' + + + OOOOOOOOO IIIIIIIIIIMMMMMMMM MMMMMMMM + OO:::::::::OO I::::::::IM:::::::M M:::::::M + OO:::::::::::::OO I::::::::IM::::::::M M::::::::M +O:::::::OOO:::::::O II::::::IIM:::::::::M M:::::::::M +O::::::O O::::::Oppppp ppppppppp eeeeeeeeeeee nnnn nnnnnnnn I::::I M::::::::::M M::::::::::M +O:::::O O:::::Op::::ppp:::::::::p ee::::::::::::ee n:::nn::::::::nn I::::I M:::::::::::M M:::::::::::M +O:::::O O:::::Op:::::::::::::::::p e::::::eeeee:::::een::::::::::::::nn I::::I M:::::::M::::M M::::M:::::::M +O:::::O O:::::Opp::::::ppppp::::::pe::::::e e:::::enn:::::::::::::::n I::::I M::::::M M::::M M::::M M::::::M +O:::::O O:::::O p:::::p p:::::pe:::::::eeeee::::::e n:::::nnnn:::::n I::::I M::::::M M::::M::::M M::::::M +O:::::O O:::::O p:::::p p:::::pe:::::::::::::::::e n::::n n::::n I::::I M::::::M M:::::::M M::::::M +O:::::O O:::::O p:::::p p:::::pe::::::eeeeeeeeeee n::::n n::::n I::::I M::::::M M:::::M M::::::M +O::::::O O::::::O p:::::p p::::::pe:::::::e n::::n n::::n I::::I M::::::M MMMMM M::::::M +O:::::::OOO:::::::O p:::::ppppp:::::::pe::::::::e n::::n n::::nII::::::IIM::::::M M::::::M + OO:::::::::::::OO p::::::::::::::::p e::::::::eeeeeeee n::::n n::::nI::::::::IM::::::M M::::::M + OO:::::::::OO p::::::::::::::pp ee:::::::::::::e n::::n n::::nI::::::::IM::::::M M::::::M + OOOOOOOOO p::::::pppppppp eeeeeeeeeeeeee nnnnnn nnnnnnIIIIIIIIIIMMMMMMMM MMMMMMMM + p:::::p + p:::::p + p:::::::p + p:::::::p + p:::::::p + ppppppppp + + ' 0.0001 + + # Display product URL + print_with_delay "Discover more and contribute at: https://github.com/openimsdk/open-im-server" 0.01 + + # Reset text color back to normal + echo -e "\033[0m" + + # Set text color to green for product description + echo -e "\033[1;32m" + + print_with_delay "Open-IM-Server: Reinventing Instant Messaging" 0.01 + print_progress 50 0.02 + + print_with_delay "Open-IM-Server is not just a product; it's a revolution. It's about bringing the power of seamless," 0.01 + print_with_delay "real-time messaging to your fingertips. And it's about joining a global community of developers, dedicated to pushing the boundaries of what's possible." 0.01 + + print_progress 50 0.02 + + # Reset text color back to normal + echo -e "\033[0m" + + # Set text color to yellow for the Slack link + echo -e "\033[1;33m" + + print_with_delay "Join our developer community on Slack: https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q" 0.01 + + # Reset text color back to normal + echo -e "\033[0m" +} + +function main() { + openim_logo +} +main "$@" diff --git a/scripts-new/bash_beautify.py b/scripts-new/bash_beautify.py new file mode 100644 index 000000000..54c6fa0ad --- /dev/null +++ b/scripts-new/bash_beautify.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#************************************************************************** +# Copyright (C) 2011, Paul Lutus * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 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 General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +#************************************************************************** + +import re +import sys + +PVERSION = '1.0' + + +class BeautifyBash: + + def __init__(self): + self.tab_str = ' ' + self.tab_size = 2 + + def read_file(self, fp): + with open(fp) as f: + return f.read() + + def write_file(self, fp, data): + with open(fp, 'w') as f: + f.write(data) + + def beautify_string(self, data, path=''): + tab = 0 + case_stack = [] + in_here_doc = False + defer_ext_quote = False + in_ext_quote = False + ext_quote_string = '' + here_string = '' + output = [] + line = 1 + for record in re.split('\n', data): + record = record.rstrip() + stripped_record = record.strip() + + # collapse multiple quotes between ' ... ' + test_record = re.sub(r'\'.*?\'', '', stripped_record) + # collapse multiple quotes between " ... " + test_record = re.sub(r'".*?"', '', test_record) + # collapse multiple quotes between ` ... ` + test_record = re.sub(r'`.*?`', '', test_record) + # collapse multiple quotes between \` ... ' (weird case) + test_record = re.sub(r'\\`.*?\'', '', test_record) + # strip out any escaped single characters + test_record = re.sub(r'\\.', '', test_record) + # remove '#' comments + test_record = re.sub(r'(\A|\s)(#.*)', '', test_record, 1) + if(not in_here_doc): + if(re.search('<<-?', test_record)): + here_string = re.sub( + '.*<<-?\s*[\'|"]?([_|\w]+)[\'|"]?.*', '\\1', stripped_record, 1) + in_here_doc = (len(here_string) > 0) + if(in_here_doc): # pass on with no changes + output.append(record) + # now test for here-doc termination string + if(re.search(here_string, test_record) and not re.search('<<', test_record)): + in_here_doc = False + else: # not in here doc + if(in_ext_quote): + if(re.search(ext_quote_string, test_record)): + # provide line after quotes + test_record = re.sub( + '.*%s(.*)' % ext_quote_string, '\\1', test_record, 1) + in_ext_quote = False + else: # not in ext quote + if(re.search(r'(\A|\s)(\'|")', test_record)): + # apply only after this line has been processed + defer_ext_quote = True + ext_quote_string = re.sub( + '.*([\'"]).*', '\\1', test_record, 1) + # provide line before quote + test_record = re.sub( + '(.*)%s.*' % ext_quote_string, '\\1', test_record, 1) + if(in_ext_quote): + # pass on unchanged + output.append(record) + else: # not in ext quote + inc = len(re.findall( + '(\s|\A|;)(case|then|do)(;|\Z|\s)', test_record)) + inc += len(re.findall('(\{|\(|\[)', test_record)) + outc = len(re.findall( + '(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)', test_record)) + outc += len(re.findall('(\}|\)|\])', test_record)) + if(re.search(r'\besac\b', test_record)): + if(len(case_stack) == 0): + sys.stderr.write( + 'File %s: error: "esac" before "case" in line %d.\n' % ( + path, line) + ) + else: + outc += case_stack.pop() + # sepcial handling for bad syntax within case ... esac + if(len(case_stack) > 0): + if(re.search('\A[^(]*\)', test_record)): + # avoid overcount + outc -= 2 + case_stack[-1] += 1 + if(re.search(';;', test_record)): + outc += 1 + case_stack[-1] -= 1 + # an ad-hoc solution for the "else" keyword + else_case = ( + 0, -1)[re.search('^(else)', test_record) != None] + net = inc - outc + tab += min(net, 0) + extab = tab + else_case + extab = max(0, extab) + output.append( + (self.tab_str * self.tab_size * extab) + stripped_record) + tab += max(net, 0) + if(defer_ext_quote): + in_ext_quote = True + defer_ext_quote = False + if(re.search(r'\bcase\b', test_record)): + case_stack.append(0) + line += 1 + error = (tab != 0) + if(error): + sys.stderr.write( + 'File %s: error: indent/outdent mismatch: %d.\n' % (path, tab)) + return '\n'.join(output), error + + def beautify_file(self, path): + error = False + if(path == '-'): + data = sys.stdin.read() + result, error = self.beautify_string(data, '(stdin)') + sys.stdout.write(result) + else: # named file + data = self.read_file(path) + result, error = self.beautify_string(data, path) + if(data != result): + # make a backup copy + self.write_file(path + '~', data) + self.write_file(path, result) + return error + + def main(self): + error = False + sys.argv.pop(0) + if(len(sys.argv) < 1): + sys.stderr.write( + 'usage: shell script filenames or \"-\" for stdin.\n') + else: + for path in sys.argv: + error |= self.beautify_file(path) + sys.exit((0, 1)[error]) + +# if not called as a module +if(__name__ == '__main__'): + BeautifyBash().main() + diff --git a/scripts-new/build-all-service.sh b/scripts-new/build-all-service.sh new file mode 100644 index 000000000..eea380b4f --- /dev/null +++ b/scripts-new/build-all-service.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This script runs `make build` command. +# The command compiles all Makefile configs. +# Args: +# WHAT: Directory names to build. If any of these directories has a 'main' +# package, the build will produce executable files under $(OUT_DIR)/bin/platforms OR $(OUT_DIR)/bin—tools/platforms. +# If not specified, "everything" will be built. +# Usage: `scripts/build-all-service.sh`. +# Example: `scripts/build-go.sh WHAT=cmd/kubelet`. + + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# CPU core number +pushd "${OPENIM_ROOT}/tools/ncpu" >/dev/null +cpu_count=$(go run .) +popd >/dev/null + +openim::color::echo ${GREEN_PREFIX} "======> cpu_count=$cpu_count" + +openim::log::info "Building OpenIM, Parallel compilation compile=$cpu_count" +compile_count=$((cpu_count / 2)) + +# For help output +ARGHELP="" +if [[ "$#" -gt 0 ]]; then + ARGHELP="'$*'" +fi + +openim::color::echo $COLOR_CYAN "NOTE: $0 has been replaced by 'make multiarch' or 'make build'" +echo +echo "The equivalent of this invocation is: " +echo " make build ${ARGHELP}" +echo " ./scripts/build-all-service.sh ${ARGHELP}" +echo +echo " Example: " +echo " Print a single binary:" +echo " make build BINS=openim-api" +echo " ./scripts/build-all-service.sh BINS=openim-api" +echo " Print : Enable debugging and logging" +echo " make build BINS=openim-api V=1 DEBUG=1" +echo " ./scripts/build-all-service.sh BINS=openim-api V=1 DEBUG=1" +echo + +if [ -z "$*" ]; then + openim::log::info "no args, build all service" + make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build +else + openim::log::info "build service: $*" + make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build "$*" +fi + +if [ $? -eq 0 ]; then + openim::log::success "all service build success, run 'make start' or './scripts/start-all.sh'" +else + openim::log::error "make build Error, script exits" +fi diff --git a/scripts-new/check-all.sh b/scripts-new/check-all.sh new file mode 100644 index 000000000..c4363a93c --- /dev/null +++ b/scripts-new/check-all.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script is check openim service is running normally +# +# Usage: `scripts/check-all.sh`. +# Encapsulated as: `make check`. +# READ: https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh + + + + + + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/install/common.sh" + +if openim::util::is_running_in_container; then + exec >> ${DOCKER_LOG_FILE} 2>&1 +fi + + +OPENIM_VERBOSE=4 + +openim::log::info "\n# Begin to check all OpenIM service" + +openim::log::status "Check all dependent service ports" +# Elegant printing function +# Elegant printing function +print_services_and_ports() { + local service_names=("$@") + local half_length=$((${#service_names[@]} / 2)) + local service_ports=("${service_names[@]:half_length}") + + echo "+-------------------------+----------+" + echo "| Service Name | Port |" + echo "+-------------------------+----------+" + + for ((index=0; index < half_length; index++)); do + printf "| %-23s | %-8s |\n" "${service_names[$index]}" "${service_ports[$index]}" + done + + echo "+-------------------------+----------+" +} + + +# Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined +# Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS + +# Print out services and their ports +print_services_and_ports "${OPENIM_SERVER_NAME_TARGETS[@]}" "${OPENIM_SERVER_PORT_TARGETS[@]}" + +# Print out dependencies and their ports +print_services_and_ports "${OPENIM_DEPENDENCY_TARGETS[@]}" "${OPENIM_DEPENDENCY_PORT_TARGETS[@]}" + +# OpenIM check +#echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}" +openim::log::info "\n## Check all dependent components service ports" +#echo "++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}" + + +# Later, after discarding Docker, the Docker keyword is unreliable, and Kubepods is used +if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then + openim::color::echo ${COLOR_CYAN} "Environment in the interior of the container" +else + openim::color::echo ${COLOR_CYAN}"The environment is outside the container" + openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]} +fi + +if [[ $? -ne 0 ]]; then + openim::log::error_exit "The service does not start properly, please check the port, query variable definition!" + echo "+++ https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh +++" +else + openim::log::success "All components depended on by OpenIM are running normally! " +fi + + +openim::log::status "Check OpenIM service:" +openim::log::colorless "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer" +result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check) +if [[ $? -ne 0 ]]; then + #echo "+++ cat openim log file >>> ${LOG_FILE}" + + openim::log::error "The service is not running properly, please check the logs $result" +fi + + +openim::log::status "Check OpenIM service:" +for item in "${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}"; do + openim::log::colorless "$item" +done + + +result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}) +if [[ $? -ne 0 ]]; then + #echo "+++ cat OpenIM log file >>> ${LOG_FILE}" + openim::log::error "The service is not running properly, please check the logs " + echo "$result" + exit 1 +else + openim::log::status "List the ports listened to by the OpenIM service:" + openim::util::find_ports_for_all_services ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]} + openim::util::find_ports_for_all_services ${OPENIM_MSGTRANSFER_BINARY[@]} + openim::log::success "All OpenIM services are running normally! " +fi + + + + diff --git a/scripts-new/cherry-pick.sh b/scripts-new/cherry-pick.sh new file mode 100644 index 000000000..ff303269d --- /dev/null +++ b/scripts-new/cherry-pick.sh @@ -0,0 +1,252 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Usage Instructions: https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md + +# Checkout a PR from GitHub. (Yes, this is sitting in a Git tree. How +# meta.) Assumes you care about pulls from remote "upstream" and +# checks them out to a branch named: +# automated-cherry-pick-of--- + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +REPO_ROOT="$(git rev-parse --show-toplevel)" +declare -r REPO_ROOT +cd "${REPO_ROOT}" + +STARTINGBRANCH=$(git symbolic-ref --short HEAD) +declare -r STARTINGBRANCH +declare -r REBASEMAGIC="${REPO_ROOT}/.git/rebase-apply" +DRY_RUN=${DRY_RUN:-""} +REGENERATE_DOCS=${REGENERATE_DOCS:-""} +UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream} +FORK_REMOTE=${FORK_REMOTE:-origin} +MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')} +MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')} + +if [[ -z ${GITHUB_USER:-} ]]; then + openim::log::error_exit "Please export GITHUB_USER= (or GH organization, if that's where your fork lives)" +fi + +if ! command -v gh > /dev/null; then + openim::log::error_exit "Can't find 'gh' tool in PATH, please install from https://github.com/cli/cli" +fi + +if [[ "$#" -lt 2 ]]; then + echo "${0} ...: cherry pick one or more onto and leave instructions for proposing pull request" + echo + echo " Checks out and handles the cherry-pick of (possibly multiple) for you." + echo " Examples:" + echo " $0 upstream/release-v3.1 12345 # Cherry-picks PR 12345 onto upstream/release-v3.1 and proposes that as a PR." + echo " $0 upstream/release-v3.1 12345 56789 # Cherry-picks PR 12345, then 56789 and proposes the combination as a single PR." + echo + echo " Set the DRY_RUN environment var to skip git push and creating PR." + echo " This is useful for creating patches to a release branch without making a PR." + echo " When DRY_RUN is set the script will leave you in a branch containing the commits you cherry-picked." + echo + echo " Set the REGENERATE_DOCS environment var to regenerate documentation for the target branch after picking the specified commits." + echo " This is useful when picking commits containing changes to API documentation." + echo + echo " Set UPSTREAM_REMOTE (default: upstream) and FORK_REMOTE (default: origin)" + echo " to override the default remote names to what you have locally." + echo + echo " For merge process info, see https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md" + exit 2 +fi + +# Checks if you are logged in. Will error/bail if you are not. +gh auth status + +if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then + openim::log::error_exit "!!! Dirty tree. Clean up and try again." +fi + +if [[ -e "${REBASEMAGIC}" ]]; then + openim::log::error_exit "!!! 'git rebase' or 'git am' in progress. Clean up and try again." +fi + +declare -r BRANCH="$1" +shift 1 +declare -r PULLS=( "$@" ) + +function join { local IFS="$1"; shift; echo "$*"; } +PULLDASH=$(join - "${PULLS[@]/#/#}") # Generates something like "#12345-#56789" +declare -r PULLDASH +PULLSUBJ=$(join " " "${PULLS[@]/#/#}") # Generates something like "#12345 #56789" +declare -r PULLSUBJ + +openim::log::status "Updating remotes..." +git remote update "${UPSTREAM_REMOTE}" "${FORK_REMOTE}" + +if ! git log -n1 --format=%H "${BRANCH}" >/dev/null 2>&1; then + openim::log::error " '${BRANCH}' not found. The second argument should be something like ${UPSTREAM_REMOTE}/release-0.21." + openim::log::error " (In particular, it needs to be a valid, existing remote branch that I can 'git checkout'.)" + exit 1 +fi + +NEWBRANCHREQ="automated-cherry-pick-of-${PULLDASH}" # "Required" portion for tools. +declare -r NEWBRANCHREQ +NEWBRANCH="$(echo "${NEWBRANCHREQ}-${BRANCH}" | sed 's/\//-/g')" +declare -r NEWBRANCH +NEWBRANCHUNIQ="${NEWBRANCH}-$(date +%s)" +declare -r NEWBRANCHUNIQ +openim::log::info "+++ Creating local branch ${NEWBRANCHUNIQ}" + +cleanbranch="" +gitamcleanup=false +function return_to_kansas { + if [[ "${gitamcleanup}" == "true" ]]; then + echo + openim::log::status "Aborting in-progress git am." + git am --abort >/dev/null 2>&1 || true + fi + + # return to the starting branch and delete the PR text file + if [[ -z "${DRY_RUN}" ]]; then + echo + openim::log::status "Returning you to the ${STARTINGBRANCH} branch and cleaning up." + git checkout -f "${STARTINGBRANCH}" >/dev/null 2>&1 || true + if [[ -n "${cleanbranch}" ]]; then + git branch -D "${cleanbranch}" >/dev/null 2>&1 || true + fi + fi +} +trap return_to_kansas EXIT + +SUBJECTS=() +function make-a-pr() { + local rel + rel="$(basename "${BRANCH}")" + echo + openim::log::status "Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}" + + local numandtitle + numandtitle=$(printf '%s\n' "${SUBJECTS[@]}") + prtext=$(cat <&2 + exit 1 + fi + done + + if [[ "${conflicts}" != "true" ]]; then + echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'" + exit 1 + fi +} + +# set the subject +subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //') +SUBJECTS+=("#${pull}: ${subject}") + +# remove the patch file from /tmp +rm -f "/tmp/${pull}.patch" +done +gitamcleanup=false + +# Re-generate docs (if needed) +if [[ -n "${REGENERATE_DOCS}" ]]; then +echo +echo "Regenerating docs..." +if ! scripts/generate-docs.sh; then + echo + echo "scripts/gendoc.sh FAILED to complete." + exit 1 +fi +fi + +if [[ -n "${DRY_RUN}" ]]; then +openim::log::error "!!! Skipping git push and PR creation because you set DRY_RUN." +echo "To return to the branch you were in when you invoked this script:" +echo +echo " git checkout ${STARTINGBRANCH}" +echo +echo "To delete this branch:" +echo +echo " git branch -D ${NEWBRANCHUNIQ}" +exit 0 +fi + +if git remote -v | grep ^"${FORK_REMOTE}" | grep "${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"; then +echo "!!! You have ${FORK_REMOTE} configured as your ${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git" +echo "This isn't normal. Leaving you with push instructions:" +echo +openim::log::status "First manually push the branch this script created:" +echo +echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}" +echo +echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)." +echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values." +echo +make-a-pr +cleanbranch="" +exit 0 +fi + +echo +openim::log::status "I'm about to do the following to push to GitHub (and I'm assuming ${FORK_REMOTE} is your personal fork):" +echo +echo " git push ${FORK_REMOTE} ${NEWBRANCHUNIQ}:${NEWBRANCH}" +echo +read -p "+++ Proceed (anything other than 'y' aborts the cherry-pick)? [y/n] " -r +if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then +echo "Aborting." >&2 +exit 1 +fi + +git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}" +make-a-pr \ No newline at end of file diff --git a/scripts-new/common.sh b/scripts-new/common.sh new file mode 100644 index 000000000..f7155fca2 --- /dev/null +++ b/scripts-new/common.sh @@ -0,0 +1,516 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# shellcheck disable=SC2034 # Variables sourced in other scripts. + +# Common utilities, variables and checks for all build scripts. + +# Unset CDPATH, having it set messes up with script import paths +unset CDPATH + +USER_ID=$(id -u) +GROUP_ID=$(id -g) + +DOCKER_OPTS=${DOCKER_OPTS:-""} +IFS=" " read -r -a DOCKER <<< "docker ${DOCKER_OPTS}" +DOCKER_HOST=${DOCKER_HOST:-""} +DOCKER_MACHINE_NAME=${DOCKER_MACHINE_NAME:-"openim-dev"} +readonly DOCKER_MACHINE_DRIVER=${DOCKER_MACHINE_DRIVER:-"virtualbox --virtualbox-cpu-count -1"} + +# This will canonicalize the path +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P) + +# Please do not refer to lib after referring to common +. $(dirname ${BASH_SOURCE})/lib/init.sh + +# Constants +readonly OPENIM_BUILD_IMAGE_REPO=openim-build +#readonly OPENIM_BUILD_IMAGE_CROSS_TAG="$(cat "${OPENIM_ROOT}/build/build-image/cross/VERSION")" + +readonly OPENIM_DOCKER_REGISTRY="${OPENIM_DOCKER_REGISTRY:-k8s.gcr.io}" +readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s-artifacts-prod/build-image}" + +# This version number is used to cause everyone to rebuild their data containers +# and build image. This is especially useful for automated build systems like +# Jenkins. +# +# Increment/change this number if you change the build image (anything under +# build/build-image) or change the set of volumes in the data container. +#readonly OPENIM_BUILD_IMAGE_VERSION_BASE="$(cat "${OPENIM_ROOT}/build/build-image/VERSION")" +#readonly OPENIM_BUILD_IMAGE_VERSION="${OPENIM_BUILD_IMAGE_VERSION_BASE}-${OPENIM_BUILD_IMAGE_CROSS_TAG}" + +# Here we map the output directories across both the local and remote _output +# directories: +# +# *_OUTPUT_ROOT - the base of all output in that environment. +# *_OUTPUT_SUBPATH - location where golang stuff is built/cached. Also +# persisted across docker runs with a volume mount. +# *_OUTPUT_BINPATH - location where final binaries are placed. If the remote +# is really remote, this is the stuff that has to be copied +# back. +# OUT_DIR can come in from the Makefile, so honor it. +readonly LOCAL_OUTPUT_ROOT="${OPENIM_ROOT}/${OUT_DIR:-_output}" +readonly LOCAL_OUTPUT_SUBPATH="${LOCAL_OUTPUT_ROOT}/bin" +readonly LOCAL_OUTPUT_BINPATH="${LOCAL_OUTPUT_SUBPATH}/platforms" +readonly LOCAL_OUTPUT_BINTOOLSPATH="${LOCAL_OUTPUT_SUBPATH}/tools" +readonly LOCAL_OUTPUT_GOPATH="${LOCAL_OUTPUT_SUBPATH}/go" +readonly LOCAL_OUTPUT_IMAGE_STAGING="${LOCAL_OUTPUT_ROOT}/images" + +# This is the port on the workstation host to expose RSYNC on. Set this if you +# are doing something fancy with ssh tunneling. +readonly OPENIM_RSYNC_PORT="${OPENIM_RSYNC_PORT:-}" + +# This is the port that rsync is running on *inside* the container. This may be +# mapped to OPENIM_RSYNC_PORT via docker networking. +readonly OPENIM_CONTAINER_RSYNC_PORT=8730 + +# Get the set of master binaries that run in Docker (on Linux) +# Entry format is ",". +# Binaries are placed in /usr/local/bin inside the image. +# +# $1 - server architecture +openim::build::get_docker_wrapped_binaries() { +local arch=$1 +local debian_base_version=v2.1.0 +local debian_iptables_version=v12.1.0 +### If you change any of these lists, please also update DOCKERIZED_BINARIES +### in build/BUILD. And openim::golang::server_image_targets + +local targets=( + "openim-api,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-cmdutils,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-crontask,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-msggateway,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-msgtransfer,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-push,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-auth,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-conversation,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-friend,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-group,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-msg,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-third,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" + "openim-rpc-user,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" +) +echo "${targets[@]}" +} + +# --------------------------------------------------------------------------- +# Basic setup functions + +# Verify that the right utilities and such are installed for building openim. Set +# up some dynamic constants. +# Args: +# $1 - boolean of whether to require functioning docker (default true) +# +# Vars set: +# OPENIM_ROOT_HASH +# OPENIM_BUILD_IMAGE_TAG_BASE +# OPENIM_BUILD_IMAGE_TAG +# OPENIM_BUILD_IMAGE +# OPENIM_BUILD_CONTAINER_NAME_BASE +# OPENIM_BUILD_CONTAINER_NAME +# OPENIM_DATA_CONTAINER_NAME_BASE +# OPENIM_DATA_CONTAINER_NAME +# OPENIM_RSYNC_CONTAINER_NAME_BASE +# OPENIM_RSYNC_CONTAINER_NAME +# DOCKER_MOUNT_ARGS +# LOCAL_OUTPUT_BUILD_CONTEXT +function openim::build::verify_prereqs() { +local -r require_docker=${1:-true} +openim::log::status "Verifying Prerequisites...." +openim::build::ensure_tar || return 1 +openim::build::ensure_rsync || return 1 +if ${require_docker}; then + openim::build::ensure_docker_in_path || return 1 + openim::util::ensure_docker_daemon_connectivity || return 1 + + if (( OPENIM_VERBOSE > 6 )); then + openim::log::status "Docker Version:" + "${DOCKER[@]}" version | openim::log::info_from_stdin + fi +fi + +OPENIM_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true) +OPENIM_ROOT_HASH=$(openim::build::short_hash "${HOSTNAME:-}:${OPENIM_ROOT}:${OPENIM_GIT_BRANCH}") +OPENIM_BUILD_IMAGE_TAG_BASE="build-${OPENIM_ROOT_HASH}" +#OPENIM_BUILD_IMAGE_TAG="${OPENIM_BUILD_IMAGE_TAG_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" +#OPENIM_BUILD_IMAGE="${OPENIM_BUILD_IMAGE_REPO}:${OPENIM_BUILD_IMAGE_TAG}" +OPENIM_BUILD_CONTAINER_NAME_BASE="openim-build-${OPENIM_ROOT_HASH}" +#OPENIM_BUILD_CONTAINER_NAME="${OPENIM_BUILD_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" +OPENIM_RSYNC_CONTAINER_NAME_BASE="openim-rsync-${OPENIM_ROOT_HASH}" +#OPENIM_RSYNC_CONTAINER_NAME="${OPENIM_RSYNC_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" +OPENIM_DATA_CONTAINER_NAME_BASE="openim-build-data-${OPENIM_ROOT_HASH}" +#OPENIM_DATA_CONTAINER_NAME="${OPENIM_DATA_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" +#DOCKER_MOUNT_ARGS=(--volumes-from "${OPENIM_DATA_CONTAINER_NAME}") +#LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${OPENIM_BUILD_IMAGE}" + +openim::version::get_version_vars +#openim::version::save_version_vars "${OPENIM_ROOT}/.dockerized-openim-version-defs" +} + +# --------------------------------------------------------------------------- +# Utility functions + +function openim::build::docker_available_on_osx() { +if [[ -z "${DOCKER_HOST}" ]]; then + if [[ -S "/var/run/docker.sock" ]]; then + openim::log::status "Using Docker for MacOS" + return 0 + fi + + openim::log::status "No docker host is set. Checking options for setting one..." + if [[ -z "$(which docker-machine)" ]]; then + openim::log::status "It looks like you're running Mac OS X, yet neither Docker for Mac nor docker-machine can be found." + openim::log::status "See: https://docs.docker.com/engine/installation/mac/ for installation instructions." + return 1 + elif [[ -n "$(which docker-machine)" ]]; then + openim::build::prepare_docker_machine + fi +fi +} + +function openim::build::prepare_docker_machine() { +openim::log::status "docker-machine was found." + +local available_memory_bytes +available_memory_bytes=$(sysctl -n hw.memsize 2>/dev/null) + +local bytes_in_mb=1048576 + +# Give virtualbox 1/2 the system memory. Its necessary to divide by 2, instead +# of multiple by .5, because bash can only multiply by ints. +local memory_divisor=2 + +local virtualbox_memory_mb=$(( available_memory_bytes / (bytes_in_mb * memory_divisor) )) + +docker-machine inspect "${DOCKER_MACHINE_NAME}" &> /dev/null || { + openim::log::status "Creating a machine to build OPENIM" + docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \ + --virtualbox-memory "${virtualbox_memory_mb}" \ + --engine-env HTTP_PROXY="${OPENIMRNETES_HTTP_PROXY:-}" \ + --engine-env HTTPS_PROXY="${OPENIMRNETES_HTTPS_PROXY:-}" \ + --engine-env NO_PROXY="${OPENIMRNETES_NO_PROXY:-127.0.0.1}" \ + "${DOCKER_MACHINE_NAME}" > /dev/null || { + openim::log::error "Something went wrong creating a machine." + openim::log::error "Try the following: " + openim::log::error "docker-machine create -d ${DOCKER_MACHINE_DRIVER} --virtualbox-memory ${virtualbox_memory_mb} ${DOCKER_MACHINE_NAME}" + return 1 + } +} +docker-machine start "${DOCKER_MACHINE_NAME}" &> /dev/null +# it takes `docker-machine env` a few seconds to work if the machine was just started +local docker_machine_out +while ! docker_machine_out=$(docker-machine env "${DOCKER_MACHINE_NAME}" 2>&1); do + if [[ ${docker_machine_out} =~ "Error checking TLS connection" ]]; then + echo "${docker_machine_out}" + docker-machine regenerate-certs "${DOCKER_MACHINE_NAME}" + else + sleep 1 + fi +done +eval "$(docker-machine env "${DOCKER_MACHINE_NAME}")" +openim::log::status "A Docker host using docker-machine named '${DOCKER_MACHINE_NAME}' is ready to go!" +return 0 +} + +function openim::build::is_gnu_sed() { +[[ $(sed --version 2>&1) == *GNU* ]] +} + +function openim::build::ensure_rsync() { +if [[ -z "$(which rsync)" ]]; then + openim::log::error "Can't find 'rsync' in PATH, please fix and retry." + return 1 +fi +} + +function openim::build::update_dockerfile() { +if openim::build::is_gnu_sed; then + sed_opts=(-i) +else + sed_opts=(-i '') +fi +sed "${sed_opts[@]}" "s/OPENIM_BUILD_IMAGE_CROSS_TAG/${OPENIM_BUILD_IMAGE_CROSS_TAG}/" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +} + +function openim::build::set_proxy() { +if [[ -n "${OPENIMRNETES_HTTPS_PROXY:-}" ]]; then + echo "ENV https_proxy $OPENIMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +fi +if [[ -n "${OPENIMRNETES_HTTP_PROXY:-}" ]]; then + echo "ENV http_proxy $OPENIMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +fi +if [[ -n "${OPENIMRNETES_NO_PROXY:-}" ]]; then + echo "ENV no_proxy $OPENIMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +fi +} + +function openim::build::ensure_docker_in_path() { +if [[ -z "$(which docker)" ]]; then + openim::log::error "Can't find 'docker' in PATH, please fix and retry." + openim::log::error "See https://docs.docker.com/installation/#installation for installation instructions." + return 1 +fi +} + +function openim::build::ensure_tar() { +if [[ -n "${TAR:-}" ]]; then + return +fi + +# Find gnu tar if it is available, bomb out if not. +TAR=tar +if which gtar &>/dev/null; then + TAR=gtar +else + if which gnutar &>/dev/null; then + TAR=gnutar + fi +fi +if ! "${TAR}" --version | grep -q GNU; then + echo " !!! Cannot find GNU tar. Build on Linux or install GNU tar" + echo " on Mac OS X (brew install gnu-tar)." + return 1 +fi +} + +function openim::build::has_docker() { +which docker &> /dev/null +} + +function openim::build::has_ip() { +which ip &> /dev/null && ip -Version | grep 'iproute2' &> /dev/null +} + +# Detect if a specific image exists +# +# $1 - image repo name +# $2 - image tag +function openim::build::docker_image_exists() { +[[ -n $1 && -n $2 ]] || { + openim::log::error "Internal error. Image not specified in docker_image_exists." + exit 2 +} + +[[ $("${DOCKER[@]}" images -q "${1}:${2}") ]] +} + +# Delete all images that match a tag prefix except for the "current" version +# +# $1: The image repo/name +# $2: The tag base. We consider any image that matches $2* +# $3: The current image not to delete if provided +function openim::build::docker_delete_old_images() { +# In Docker 1.12, we can replace this with +# docker images "$1" --format "{{.Tag}}" +for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do + if [[ "${tag}" != "${2}"* ]] ; then + V=3 openim::log::status "Keeping image ${1}:${tag}" + continue + fi + + if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then + V=2 openim::log::status "Deleting image ${1}:${tag}" + "${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null + else + V=3 openim::log::status "Keeping image ${1}:${tag}" + fi +done +} + +# Stop and delete all containers that match a pattern +# +# $1: The base container prefix +# $2: The current container to keep, if provided +function openim::build::docker_delete_old_containers() { +# In Docker 1.12 we can replace this line with +# docker ps -a --format="{{.Names}}" +for container in $("${DOCKER[@]}" ps -a | tail -n +2 | awk '{print $NF}') ; do + if [[ "${container}" != "${1}"* ]] ; then + V=3 openim::log::status "Keeping container ${container}" + continue + fi + if [[ -z "${2:-}" || "${container}" != "${2}" ]] ; then + V=2 openim::log::status "Deleting container ${container}" + openim::build::destroy_container "${container}" + else + V=3 openim::log::status "Keeping container ${container}" + fi +done +} + +# Takes $1 and computes a short has for it. Useful for unique tag generation +function openim::build::short_hash() { +[[ $# -eq 1 ]] || { + openim::log::error "Internal error. No data based to short_hash." + exit 2 +} + +local short_hash +if which md5 >/dev/null 2>&1; then + short_hash=$(md5 -q -s "$1") +else + short_hash=$(echo -n "$1" | md5sum) +fi +echo "${short_hash:0:10}" +} + +# Pedantically kill, wait-on and remove a container. The -f -v options +# to rm don't actually seem to get the job done, so force kill the +# container, wait to ensure it's stopped, then try the remove. This is +# a workaround for bug https://github.com/docker/docker/issues/3968. +function openim::build::destroy_container() { +"${DOCKER[@]}" kill "$1" >/dev/null 2>&1 || true +if [[ $("${DOCKER[@]}" version --format '{{.Server.Version}}') = 17.06.0* ]]; then + # Workaround https://github.com/moby/moby/issues/33948. + # TODO: remove when 17.06.0 is not relevant anymore + DOCKER_API_VERSION=v1.29 "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true +else + "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true +fi +"${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true +} + +# --------------------------------------------------------------------------- +# Building + + +function openim::build::clean() { +if openim::build::has_docker ; then + openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" + openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" + + V=2 openim::log::status "Cleaning all untagged docker images" + "${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true +fi + +if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then + openim::log::status "Removing _output directory" + rm -rf "${LOCAL_OUTPUT_ROOT}" +fi +} + +# Set up the context directory for the openim-build image and build it. +function openim::build::build_image() { +mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}" +# Make sure the context directory owned by the right user for syncing sources to container. +chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" + +cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/" + +cp "${OPENIM_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" +cp "${OPENIM_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/" +dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" +chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" + +openim::build::update_dockerfile +openim::build::set_proxy +openim::build::docker_build "${OPENIM_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' + +# Clean up old versions of everything +openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" "${OPENIM_BUILD_CONTAINER_NAME}" +openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" "${OPENIM_RSYNC_CONTAINER_NAME}" +openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" "${OPENIM_DATA_CONTAINER_NAME}" +openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" "${OPENIM_BUILD_IMAGE_TAG}" + +openim::build::ensure_data_container +openim::build::sync_to_container +} + +# Build a docker image from a Dockerfile. +# $1 is the name of the image to build +# $2 is the location of the "context" directory, with the Dockerfile at the root. +# $3 is the value to set the --pull flag for docker build; true by default +function openim::build::docker_build() { +local -r image=$1 +local -r context_dir=$2 +local -r pull="${3:-true}" +local -ra build_cmd=("${DOCKER[@]}" build -t "${image}" "--pull=${pull}" "${context_dir}") + +openim::log::status "Building Docker image ${image}" +local docker_output +docker_output=$("${build_cmd[@]}" 2>&1) || { + cat <&2 ++++ Docker build command failed for ${image} + +${docker_output} + +To retry manually, run: + +${build_cmd[*]} + +EOF + return 1 +} +} + +function openim::build::ensure_data_container() { +# If the data container exists AND exited successfully, we can use it. +# Otherwise nuke it and start over. +local ret=0 +local code=0 + +code=$(docker inspect \ + -f '{{.State.ExitCode}}' \ +"${OPENIM_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$? +if [[ "${ret}" == 0 && "${code}" != 0 ]]; then + openim::build::destroy_container "${OPENIM_DATA_CONTAINER_NAME}" + ret=1 +fi +if [[ "${ret}" != 0 ]]; then + openim::log::status "Creating data container ${OPENIM_DATA_CONTAINER_NAME}" + # We have to ensure the directory exists, or else the docker run will + # create it as root. + mkdir -p "${LOCAL_OUTPUT_GOPATH}" + # We want this to run as root to be able to chown, so non-root users can + # later use the result as a data container. This run both creates the data + # container and chowns the GOPATH. + # + # The data container creates volumes for all of the directories that store + # intermediates for the Go build. This enables incremental builds across + # Docker sessions. The *_cgo paths are re-compiled versions of the go std + # libraries for true static building. + local -ra docker_cmd=( + "${DOCKER[@]}" run + --volume "${REMOTE_ROOT}" # white-out the whole output dir + --volume /usr/local/go/pkg/linux_386_cgo + --volume /usr/local/go/pkg/linux_amd64_cgo + --volume /usr/local/go/pkg/linux_arm_cgo + --volume /usr/local/go/pkg/linux_arm64_cgo + --volume /usr/local/go/pkg/linux_ppc64le_cgo + --volume /usr/local/go/pkg/darwin_amd64_cgo + --volume /usr/local/go/pkg/darwin_386_cgo + --volume /usr/local/go/pkg/windows_amd64_cgo + --volume /usr/local/go/pkg/windows_386_cgo + --name "${OPENIM_DATA_CONTAINER_NAME}" + --hostname "${HOSTNAME}" + "${OPENIM_BUILD_IMAGE}" + chown -R "${USER_ID}":"${GROUP_ID}" + "${REMOTE_ROOT}" + /usr/local/go/pkg/ + ) + "${docker_cmd[@]}" +fi +} + +# Build all openim commands. +function openim::build::build_command() { +openim::log::status "Running build command..." +make -C "${OPENIM_ROOT}" multiarch +} diff --git a/scripts-new/coverage.awk b/scripts-new/coverage.awk new file mode 100644 index 000000000..49389054e --- /dev/null +++ b/scripts-new/coverage.awk @@ -0,0 +1,13 @@ +#!/usr/bin/env awk + +{ + print $0 + if (match($0, /^total:/)) { + sub(/%/, "", $NF); + printf("test coverage is %s%(quality gate is %s%)\n", $NF, target) + if (strtonum($NF) < target) { + printf("test coverage does not meet expectations: %d%, please add test cases!\n", target) + exit 1; + } + } +} diff --git a/scripts-new/coverage.sh b/scripts-new/coverage.sh new file mode 100644 index 000000000..e5cef0b5d --- /dev/null +++ b/scripts-new/coverage.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# http://stackoverflow.com/a/21142256/2055281 + +echo "mode: atomic" > coverage.txt + +for d in $(find ./* -maxdepth 10 -type d); do + if ls $d/*.go &> /dev/null; then + go test -coverprofile=profile.out -covermode=atomic $d + if [ -f profile.out ]; then + cat profile.out | grep -v "mode: " >> /tmp/coverage.txt + rm profile.out + fi + fi +done diff --git a/scripts-new/create-topic.sh b/scripts-new/create-topic.sh new file mode 100644 index 000000000..5c291ff08 --- /dev/null +++ b/scripts-new/create-topic.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Wait for Kafka to be ready +until /opt/bitnami/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092; do + echo "Waiting for Kafka to be ready..." + sleep 2 +done + +# Create topics +/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic latestMsgToRedis +/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic msgToPush +/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic offlineMsgToMongoMysql + +echo "Topics created." \ No newline at end of file diff --git a/scripts-new/define/binaries.sh b/scripts-new/define/binaries.sh new file mode 100644 index 000000000..30cec2f84 --- /dev/null +++ b/scripts-new/define/binaries.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Define an associative array to store the binaries and their counts. +# The count for openim-msgtransfer is set to 4, all others are set to 1. +declare -A binaries=( + [openim-api]=1 + [openim-cmdutils]=1 + [openim-crontask]=1 + [openim-msggateway]=1 + [openim-msgtransfer]=4 # openim-msgtransfer count is 4 + [openim-push]=1 + [openim-rpc-auth]=1 + [openim-rpc-conversation]=1 + [openim-rpc-friend]=1 + [openim-rpc-group]=1 + [openim-rpc-msg]=1 + [openim-rpc-third]=1 + [openim-rpc-user]=1 +) + diff --git a/scripts-new/docker-start-all.sh b/scripts-new/docker-start-all.sh new file mode 100644 index 000000000..8d91cd771 --- /dev/null +++ b/scripts-new/docker-start-all.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + + + +#fixme This scripts is the total startup scripts +#fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/install/common.sh" +openim::log::info "\n# Use Docker to start all OpenIM service" +trap 'openim::util::onCtrlC' INT +"${OPENIM_ROOT}"/scripts/start-all.sh 2>&1 & +tail -f ${DOCKER_LOG_FILE} diff --git a/scripts-new/ensure-tag.sh b/scripts-new/ensure-tag.sh new file mode 100644 index 000000000..5fedf7019 --- /dev/null +++ b/scripts-new/ensure-tag.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +version="${VERSION}" +if [ "${version}" == "" ];then + version=v$(${OPENIM_ROOT}/_output/tools/gsemver bump) +fi + +if [ -z "$(git tag -l ${version})" ];then + git tag -a -m "release version ${version}" ${version} +fi diff --git a/scripts-new/gen-swagger-docs.sh b/scripts-new/gen-swagger-docs.sh new file mode 100644 index 000000000..e768f9106 --- /dev/null +++ b/scripts-new/gen-swagger-docs.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Script to generate docs from the latest swagger spec. + + + + + +# The root of the build/dist directory +OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +source "${OPENIM_ROOT}"/scripts/lib/util.sh + +mkdir -p ${OPENIM_OUTPUT_TMP} +cd ${OPENIM_OUTPUT_TMP} + +# gendocs takes "input.json" as the input swagger spec. +# $1 is expected to be _ +cp ${OPENIM_OUTPUT_TMP}/swagger-source/"$1".json ${OPENIM_OUTPUT_TMP}/input.json + +./gradle-2.5/bin/gradle gendocs --info + +#insert a TOC for top level API objects +buf="== Top Level API Objects\n\n" +top_level_models=$(grep '&[A-Za-z]*{},' /register.go | sed 's/.*&//;s/{},//') + +# check if the top level models exist in the definitions.adoc. If they exist, +# their name will be . +VERSION="${1#*_}" +for m in ${top_level_models} +do + if grep -xq "=== ${VERSION}.${m}" ./definitions.adoc + then + buf+="* <<${VERSION}.${m}>>\n" + fi +done +sed -i "1i ${buf}" ./definitions.adoc + +# fix the links in .adoc, replace <> with link:definitions.html#_x_y[x.y], and lowercase the _x_y part +sed -i -e 's|<<\(.*\)\.\(.*\)>>|link:#_\L\1_\2\E[\1.\2]|g' ./definitions.adoc +sed -i -e 's|<<\(.*\)\.\(.*\)>>|link:../definitions#_\L\1_\2\E[\1.\2]|g' ./paths.adoc + +# fix the link to <> +sed -i -e 's|<>|link:#_any[any]|g' ./definitions.adoc +sed -i -e 's|<>|link:../definitions#_any[any]|g' ./paths.adoc + +# change the title of paths.adoc from "paths" to "operations" +sed -i 's|== Paths|== Operations|g' ./paths.adoc + +# $$ has special meaning in asciidoc, we need to escape it +sed -i 's|\$\$|+++$$+++|g' ./definitions.adoc + +echo -e "=== any\nRepresents an untyped JSON map - see the description of the field for more info about the structure of this object." >> ./definitions.adoc + +asciidoctor definitions.adoc +asciidoctor paths.adoc + +cp "$OPENIM_OUTPUT_TMP/definitions.html" "$OPENIM_OUTPUT_TMP/_output/" +cp "$OPENIM_OUTPUT_TMP/paths.html" "$OPENIM_OUTPUT_TMP/_output/operations.html" + +success "SUCCESS" \ No newline at end of file diff --git a/scripts-new/genconfig.sh b/scripts-new/genconfig.sh new file mode 100644 index 000000000..498b0b908 --- /dev/null +++ b/scripts-new/genconfig.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Function of this script: Generate the OPENIM component YAML configuration file according to the scripts/environment.sh configuration. +# eg:./scripts/genconfig.sh scripts/install/environment.sh scripts/template/config.yaml +# Read: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md + +env_file="$1" +template_file="$2" + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +if [ $# -ne 2 ];then + openim::log::error "Usage: scripts/genconfig.sh scripts/environment.sh configs/config.yaml" + exit 1 +fi + +if [ -z "${OPENIM_IP}" ]; then + openim::util::require-dig +fi + +source "${env_file}" + +declare -A envs + +set +u +for env in $(sed -n 's/^[^#].*${\(.*\)}.*/\1/p' ${template_file}) +do + if [ -z "$(eval echo \$${env})" ];then + openim::log::error "environment variable '${env}' not set" + missing=true + fi +done + +if [ "${missing}" ];then + openim::log::error "You may run 'source scripts/environment.sh' to set these environment" + exit 1 +fi + +eval "cat << EOF +$(cat ${template_file}) +EOF" \ No newline at end of file diff --git a/scripts-new/gendoc.sh b/scripts-new/gendoc.sh new file mode 100644 index 000000000..43b69c3ed --- /dev/null +++ b/scripts-new/gendoc.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/bin/bash + +DEFAULT_DIRS=("pkg") +BASE_URL="github.com/openimsdk/open-im-server/v3" +REMOVE_DOC=false + +usage() { + echo "Usage: $0 [OPTIONS]" + echo + echo "This script iterates over directories. By default, it generates doc.go files for 'pkg' and 'internal/pkg'." + echo + echo "Options:" + echo " -d DIRS, --dirs DIRS Specify directories to process, separated by commas (e.g., 'pkg,internal/pkg')." + echo " -u URL, --url URL Set the base URL for the import path. Default is '$BASE_URL'." + echo " -r, --remove Remove all doc.go files in the specified directories." + echo " -h, --help Show this help message." + echo +} + +process_dir() { + local dir="$1" + local base_url="$2" + local remove_doc="$3" + + find "$dir" -type d | while read -r d; do + if [ "$remove_doc" = true ]; then + if [ -f "$d/doc.go" ]; then + echo "Removing $d/doc.go" + rm -f "$d/doc.go" + fi + else + if [ ! -f "$d/doc.go" ] && ls "$d/"*.go &>/dev/null; then + echo "Creating $d/doc.go" + echo "package $(basename "$d") // import \"$base_url/$(echo "$d" | sed "s|^\./||")\"" >"$d/doc.go" + fi + fi + done +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -d|--dirs) + IFS=',' read -ra DIRS <<< "$2" + shift 2 + ;; + -u|--url) + BASE_URL="$2" + shift 2 + ;; + -r|--remove) + REMOVE_DOC=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; + esac +done + +DIRS=(${DIRS:-"${DEFAULT_DIRS[@]}"}) + +for dir in "${DIRS[@]}"; do + process_dir "$dir" "$BASE_URL" "$REMOVE_DOC" +done diff --git a/scripts-new/githooks/commit-msg.sh b/scripts-new/githooks/commit-msg.sh new file mode 100644 index 000000000..d2d96645b --- /dev/null +++ b/scripts-new/githooks/commit-msg.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============================================================================== +# +# Store this file as .git/hooks/commit-msg in your repository in order to +# enforce checking for proper commit message format before actual commits. +# You may need to make the scripts executable by 'chmod +x .git/hooks/commit-msg'. + +# commit-msg use go-gitlint tool, install go-gitlint via `go get github.com/llorllale/go-gitlint/cmd/go-gitlint` +# go-gitlint --msg-file="$1" + +# An example hook scripts to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. + +YELLOW="\e[93m" +GREEN="\e[32m" +RED="\e[31m" +ENDCOLOR="\e[0m" + +printMessage() { + printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n" +} + +printSuccess() { + printf "${GREEN}OpenIM : $1${ENDCOLOR}\n" +} + +printError() { + printf "${RED}OpenIM : $1${ENDCOLOR}\n" +} + +printMessage "Running the OpenIM commit-msg hook." + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | +sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { +echo >&2 Duplicate Signed-off-by lines. +exit 1 +} + +# TODO: go-gitlint dir set +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. +GITLINT_DIR="$OPENIM_ROOT/_output/tools/go-gitlint" + +$GITLINT_DIR \ +--msg-file=$1 \ +--subject-regex="^(build|chore|ci|docs|feat|feature|fix|perf|refactor|revert|style|bot|test)(.*)?:\s?.*" \ +--subject-maxlen=150 \ +--subject-minlen=10 \ +--body-regex=".*" \ +--max-parents=1 + +if [ $? -ne 0 ] +then +if ! command -v $GITLINT_DIR &>/dev/null; then + printError "$GITLINT_DIR not found. Please run 'make tools' OR 'make tools.verify.go-gitlint' make verto install it." +fi +printError "Please fix your commit message to match kubecub coding standards" +printError "https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md" +exit 1 +fi + +### Add Sign-off-by line to the end of the commit message +# Get local git config +NAME=$(git config user.name) +EMAIL=$(git config user.email) + +# Check if the commit message contains a sign-off line +grep -qs "^Signed-off-by: " "$1" +SIGNED_OFF_BY_EXISTS=$? + +# Add "Signed-off-by" line if it doesn't exist +if [ $SIGNED_OFF_BY_EXISTS -ne 0 ]; then +echo -e "\nSigned-off-by: $NAME <$EMAIL>" >> "$1" +fi \ No newline at end of file diff --git a/scripts-new/githooks/pre-commit.sh b/scripts-new/githooks/pre-commit.sh new file mode 100644 index 000000000..d8396b560 --- /dev/null +++ b/scripts-new/githooks/pre-commit.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============================================================================== +# This is a pre-commit hook that ensures attempts to commit files that are +# are larger than $limit to your _local_ repo fail, with a helpful error message. + +# You can override the default limit of 2MB by supplying the environment variable: +# GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB" +# +# ============================================================================== +# + +LC_ALL=C + +local_branch="$(git rev-parse --abbrev-ref HEAD)" +valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|bot|refactor|revert|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$" + +YELLOW="\e[93m" +GREEN="\e[32m" +RED="\e[31m" +ENDCOLOR="\e[0m" + +printMessage() { + printf "${YELLOW}openim : $1${ENDCOLOR}\n" +} + +printSuccess() { + printf "${GREEN}openim : $1${ENDCOLOR}\n" +} + +printError() { + printf "${RED}openim : $1${ENDCOLOR}\n" +} + +printMessage "Running local openim pre-commit hook." + +# flutter format . +# https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md +# TODO! GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB" +# Maximum file size limit in bytes +limit=${GIT_FILE_SIZE_LIMIT:-2000000} # Default 2MB +limitInMB=$(( $limit / 1000000 )) + +function file_too_large(){ + filename=$0 + filesize=$(( $1 / 2**20 )) + + cat < /dev/null 2>&1 +then + against=HEAD +else + against="$empty_tree" +fi + +# Set split so that for loop below can handle spaces in file names by splitting on line breaks +IFS=' +' + +shouldFail=false +for file in $( git diff-index --cached --name-only $against ); do + file_size=$(([ ! -f $file ] && echo 0) || (ls -la $file | awk '{ print $5 }')) + if [ "$file_size" -gt "$limit" ]; then + printError "File $file is $(( $file_size / 10**6 )) MB, which is larger than our configured limit of $limitInMB MB" + shouldFail=true + fi +done + +if $shouldFail +then + printMessage "If you really need to commit this file, you can override the size limit by setting the GIT_FILE_SIZE_LIMIT environment variable, e.g. GIT_FILE_SIZE_LIMIT=42000000 for 42MB. Or, commit with the --no-verify switch to skip the check entirely." + printError "Commit aborted" + exit 1; +fi + +if [[ ! $local_branch =~ $valid_branch_regex ]] +then + printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex. +Your commit will be rejected. You should rename your branch to a valid name(feat/name OR fix/name) and try again." + printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694" + exit 1 +fi \ No newline at end of file diff --git a/scripts-new/githooks/pre-push.sh b/scripts-new/githooks/pre-push.sh new file mode 100644 index 000000000..9bd938915 --- /dev/null +++ b/scripts-new/githooks/pre-push.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============================================================================== +# + +YELLOW="\e[93m" +GREEN="\e[32m" +RED="\e[31m" +ENDCOLOR="\e[0m" + +local_branch="$(git rev-parse --abbrev-ref HEAD)" +valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$" + +printMessage() { + printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n" +} + +printSuccess() { + printf "${GREEN}OpenIM : $1${ENDCOLOR}\n" +} + +printError() { + printf "${RED}OpenIM : $1${ENDCOLOR}\n" +} + +printMessage "Running local OpenIM pre-push hook." + +if [[ $(git status --porcelain) ]]; then + printError "This scripts needs to run against committed code only. Please commit or stash you changes." + exit 1 +fi + +COLOR_SUFFIX="\033[0m" + +BLACK_PREFIX="\033[30m" +RED_PREFIX="\033[31m" +GREEN_PREFIX="\033[32m" +BACKGROUND_GREEN="\033[33m" +BLUE_PREFIX="\033[34m" +PURPLE_PREFIX="\033[35m" +SKY_BLUE_PREFIX="\033[36m" +WHITE_PREFIX="\033[37m" +BOLD_PREFIX="\033[1m" +UNDERLINE_PREFIX="\033[4m" +ITALIC_PREFIX="\033[3m" + +# Function to print colored text +print_color() { + local text=$1 + local color=$2 + echo -e "${color}${text}${COLOR_SUFFIX}" +} + +# Function to print section separator +print_separator() { + print_color "==========================================================" ${PURPLE_PREFIX} +} + +# Get current time +time=$(date +"%Y-%m-%d %H:%M:%S") + +# Print section separator +print_separator + +# Print time of submission +print_color "PTIME: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}" +echo "" +author=$(git config user.name) +repository=$(basename -s .git $(git config --get remote.origin.url)) + +# Print additional information if needed +print_color "Repository: ${repository}" "${BLUE_PREFIX}" +echo "" + +print_color "Author: ${author}" "${PURPLE_PREFIX}" + +# Print section separator +print_separator + +file_list=$(git diff --name-status HEAD @{u}) +added_files=$(grep -c '^A' <<< "$file_list") +modified_files=$(grep -c '^M' <<< "$file_list") +deleted_files=$(grep -c '^D' <<< "$file_list") + +print_color "Added Files: ${added_files}" "${BACKGROUND_GREEN}" +print_color "Modified Files: ${modified_files}" "${BACKGROUND_GREEN}" +print_color "Deleted Files: ${deleted_files}" "${BACKGROUND_GREEN}" + +if [[ ! $local_branch =~ $valid_branch_regex ]] +then + printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex. +Your commit will be rejected. You should rename your branch to a valid name(feat/name OR fix/name) and try again." + printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694" + exit 1 +fi + +# +#printMessage "Running the Flutter analyzer" +#flutter analyze +# +#if [ $? -ne 0 ]; then +# printError "Flutter analyzer error" +# exit 1 +#fi +# +#printMessage "Finished running the Flutter analyzer" diff --git a/scripts-new/init-config.sh b/scripts-new/init-config.sh new file mode 100644 index 000000000..ef2cd0a40 --- /dev/null +++ b/scripts-new/init-config.sh @@ -0,0 +1,265 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script automatically initializes various configuration files and can generate example files. + + + + + +# Root directory of the OpenIM project +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + +# Source initialization script +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# Default environment file +readonly ENV_FILE=${ENV_FILE:-"${OPENIM_ROOT}/scripts/install/environment.sh"} + +# Templates for configuration files +declare -A TEMPLATES=( + ["${OPENIM_ROOT}/deployments/templates/env-template.yaml"]="${OPENIM_ROOT}/.env" + ["${OPENIM_ROOT}/deployments/templates/config.yaml"]="${OPENIM_ROOT}/config/config.yaml" + ["${OPENIM_ROOT}/deployments/templates/prometheus.yml"]="${OPENIM_ROOT}/config/prometheus.yml" + ["${OPENIM_ROOT}/deployments/templates/alertmanager.yml"]="${OPENIM_ROOT}/config/alertmanager.yml" +) + +# Templates for example files +declare -A EXAMPLES=( + ["${OPENIM_ROOT}/deployments/templates/env-template.yaml"]="${OPENIM_ROOT}/config/templates/env.template" + ["${OPENIM_ROOT}/deployments/templates/config.yaml"]="${OPENIM_ROOT}/config/templates/config.yaml.template" + ["${OPENIM_ROOT}/deployments/templates/prometheus.yml"]="${OPENIM_ROOT}/config/templates/prometheus.yml.template" + ["${OPENIM_ROOT}/deployments/templates/alertmanager.yml"]="${OPENIM_ROOT}/config/templates/alertmanager.yml.template" +) + +# Templates for config Copy file +declare -A COPY_TEMPLATES=( + ["${OPENIM_ROOT}/deployments/templates/email.tmpl"]="${OPENIM_ROOT}/config/email.tmpl" + ["${OPENIM_ROOT}/deployments/templates/instance-down-rules.yml"]="${OPENIM_ROOT}/config/instance-down-rules.yml" + ["${OPENIM_ROOT}/deployments/templates/notification.yaml"]="${OPENIM_ROOT}/config/notification.yaml" +) + +# Templates for config Copy file +declare -A COPY_EXAMPLES=( + ["${OPENIM_ROOT}/deployments/templates/email.tmpl"]="${OPENIM_ROOT}/config/templates/email.tmpl.template" + ["${OPENIM_ROOT}/deployments/templates/instance-down-rules.yml"]="${OPENIM_ROOT}/config/templates/instance-down-rules.yml.template" + ["${OPENIM_ROOT}/deployments/templates/notification.yaml"]="${OPENIM_ROOT}/config/templates/notification.yaml.template" +) + +# Command-line options +FORCE_OVERWRITE=false +SKIP_EXISTING=false +GENERATE_EXAMPLES=false +CLEAN_CONFIG=false +CLEAN_EXAMPLES=false + +FILES_PROCESSED=false + +# Function to display help information +show_help() { + echo "Usage: $(basename "$0") [options]" + echo "Options:" + echo " -h, --help Show this help message" + echo " --force Overwrite existing files without prompt" + echo " --skip Skip generation if file exists" + echo " --examples Generate example files" + echo " --clean-config Clean all configuration files" + echo " --clean-examples Clean all example files" +} + +# Function to generate and copy configuration files +generate_config_files() { + # Handle TEMPLATES array + for template in "${!TEMPLATES[@]}"; do + local output_file="${TEMPLATES[$template]}" + process_file "$template" "$output_file" true + done + + # Handle COPY_TEMPLATES array + for template in "${!COPY_TEMPLATES[@]}"; do + local output_file="${COPY_TEMPLATES[$template]}" + process_file "$template" "$output_file" false + done +} + +# Function to generate example files +generate_example_files() { + env_cmd="env -i" + + env_vars["OPENIM_IP"]="127.0.0.1" + env_vars["LOG_STORAGE_LOCATION"]="../../" + + for var in "${!env_vars[@]}"; do + env_cmd+=" $var='${env_vars[$var]}'" + done + + # Processing EXAMPLES array + for template in "${!EXAMPLES[@]}"; do + local example_file="${EXAMPLES[$template]}" + process_file "$template" "$example_file" true + done + + # Processing COPY_EXAMPLES array + for template in "${!COPY_EXAMPLES[@]}"; do + local example_file="${COPY_EXAMPLES[$template]}" + process_file "$template" "$example_file" false + done +} + +# Function to process a single file, either by generating or copying +process_file() { + local template=$1 + local output_file=$2 + local use_genconfig=$3 + + if [[ -f "${output_file}" ]]; then + if [[ "${FORCE_OVERWRITE}" == true ]]; then + openim::log::info "Force overwriting ${output_file}." + elif [[ "${SKIP_EXISTING}" == true ]]; then + #openim::log::info "Skipping generation of ${output_file} as it already exists." + return + else + echo -n "File ${output_file} already exists. Overwrite? (Y/N): " + read -r -n 1 REPLY + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + openim::log::info "Skipping generation of ${output_file}." + return + fi + fi + else + if [[ "${SKIP_EXISTING}" == true ]]; then + openim::log::info "Generating ${output_file} as it does not exist." + fi + fi + + if [[ "$use_genconfig" == true ]]; then + openim::log::info "⌚ Working with template file: ${template} to generate ${output_file}..." + if [[ ! -f "${OPENIM_ROOT}/scripts/genconfig.sh" ]]; then + openim::log::error "genconfig.sh script not found" + exit 1 + fi + if [[ -n "${env_cmd}" ]]; then + + { + printf "debugggggggggggggggggggg file: %s template: %s\n" "${ENV_FILE}" "${template}" + } | tee /tmp/debug.log + + + eval "$env_cmd ${OPENIM_ROOT}/scripts/genconfig.sh '${ENV_FILE}' '${template}' > '${output_file}'" || { + openim::log::error "Error processing template file ${template}" + exit 1 + } + else + "${OPENIM_ROOT}/scripts/genconfig.sh" "${ENV_FILE}" "${template}" > "${output_file}" || { + openim::log::error "Error processing template file ${template}" + exit 1 + } + fi + else + openim::log::info "📋 Copying ${template} to ${output_file}..." + cp "${template}" "${output_file}" || { + openim::log::error "Error copying template file ${template}" + exit 1 + } + fi + FILES_PROCESSED=true +} + +clean_config_files() { + local all_templates=("${TEMPLATES[@]}" "${COPY_TEMPLATES[@]}") + + for output_file in "${all_templates[@]}"; do + if [[ -f "${output_file}" ]]; then + rm -f "${output_file}" + openim::log::info "Removed configuration file: ${output_file}" + fi + done +} + +# Function to clean example files +clean_example_files() { + local all_examples=("${EXAMPLES[@]}" "${COPY_EXAMPLES[@]}") + + for example_file in "${all_examples[@]}"; do + if [[ -f "${example_file}" ]]; then + rm -f "${example_file}" + openim::log::info "Removed example file: ${example_file}" + fi + done +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + show_help + exit 0 + ;; + --force) + FORCE_OVERWRITE=true + shift + ;; + --skip) + SKIP_EXISTING=true + shift + ;; + --examples) + GENERATE_EXAMPLES=true + shift + ;; + --clean-config) + CLEAN_CONFIG=true + shift + ;; + --clean-examples) + CLEAN_EXAMPLES=true + shift + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + +# Clean configuration files if --clean-config option is provided +if [[ "${CLEAN_CONFIG}" == true ]]; then + clean_config_files +fi + +# Clean example files if --clean-examples option is provided +if [[ "${CLEAN_EXAMPLES}" == true ]]; then + clean_example_files +fi + +# Generate configuration files if requested +if [[ "${FORCE_OVERWRITE}" == true || "${SKIP_EXISTING}" == false ]] && [[ "${CLEAN_CONFIG}" == false ]]; then + generate_config_files +fi + +# Generate configuration files if requested +if [[ "${SKIP_EXISTING}" == true ]]; then + generate_config_files +fi + +# Generate example files if --examples option is provided +if [[ "${GENERATE_EXAMPLES}" == true ]] && [[ "${CLEAN_EXAMPLES}" == false ]]; then + generate_example_files +fi + +if [[ "${FILES_PROCESSED}" == true ]]; then + openim::log::success "Configuration and example files operation complete!" +fi diff --git a/scripts-new/init-env.sh b/scripts-new/init-env.sh new file mode 100644 index 000000000..1e802c3be --- /dev/null +++ b/scripts-new/init-env.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#FIXME This script is the startup script for multiple servers. +#FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P) +source "${OPENIM_ROOT}/scripts/install/common.sh" + +openim::log::info "\n# Begin Install OpenIM Config" + +for file in "${OPENIM_SERVER_TARGETS[@]}"; do + VARNAME="$(echo $file | tr '[:lower:]' '[:upper:]' | tr '.' '_' | tr '-' '_')" + VARVALUE="$OPENIM_OUTPUT_HOSTBIN/$file" + # /etc/profile.d/openim-env.sh + echo "export $VARNAME=$VARVALUE" > /etc/profile.d/openim-env.sh + source /etc/profile.d/openim-env.sh +done diff --git a/scripts-new/init-githooks.sh b/scripts-new/init-githooks.sh new file mode 100644 index 000000000..4ee470742 --- /dev/null +++ b/scripts-new/init-githooks.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ----------------------------------------------------------------------------- +# init-githooks.sh +# +# This script assists in managing Git hooks for the OpenIM project. +# When executed: +# 1. It prompts the user to enable git hooks. +# 2. If the user accepts, it copies predefined hook scripts to the appropriate +# Git directory, making them executable. +# 3. If requested, it can delete the added hooks. +# +# This script equal runs `make init-githooks` command. +# Usage: +# ./init-githooks.sh Prompt to enable git hooks. +# ./init-githooks.sh --delete Delete previously added git hooks. +# ./init-githooks.sh --help Show the help message. +# +# Example: `scripts/build-go.sh --help`. +# Documentation & related context can be found at: +# https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694 +# +# ----------------------------------------------------------------------------- + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +HOOKS_DIR="${OPENIM_ROOT}/.git/hooks" + +help_info() { + echo "Usage: $0 [options]" + echo + echo "This script helps to manage git hooks." + echo + echo "Options:" + echo " -h, --help Show this help message and exit." + echo " -d, --delete Delete the hooks that have been added." + echo " By default, it will prompt to enable git hooks." +} + +delete_hooks() { + for file in "${OPENIM_ROOT}"/scripts/githooks/*.sh; do + hook_name=$(basename "$file" .sh) # This removes the .sh extension + rm -f "$HOOKS_DIR/$hook_name" + done + echo "Git hooks have been deleted." +} + +enable_hooks() { + echo "Would you like to:" + echo "1) Enable git hooks mode" + echo "2) Delete existing git hooks" + echo "Please select a number (or any other key to exit):" + read -r choice + + case "$choice" in + 1) + for file in ${OPENIM_ROOT}/scripts/githooks/*.sh; do + hook_name=$(basename "$file" .sh) # This removes the .sh extension + cp -f "$file" "$HOOKS_DIR/$hook_name" + done + + chmod +x $HOOKS_DIR/* + + echo "Git hooks mode has been enabled." + echo "With git hooks enabled, every time you perform a git action (e.g. git commit), the corresponding hooks script will be triggered automatically." + echo "This means that if the size of the file you're committing exceeds the set limit (e.g. 42MB), the commit will be rejected." + ;; + 2) + delete_hooks + ;; + *) + echo "Exiting without making changes." + ;; + esac +} + + +case "$1" in + -h|--help) + help_info + ;; + -d|--delete) + delete_hooks + ;; + *) + enable_hooks + ;; +esac diff --git a/scripts-new/install-im-server.sh b/scripts-new/install-im-server.sh new file mode 100644 index 000000000..d11a49dc8 --- /dev/null +++ b/scripts-new/install-im-server.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Copyright © 2024 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# OpenIM Docker Deployment Script +# +# This script automates the process of building the OpenIM server image +# and deploying it using Docker Compose. +# +# Variables: +# - SERVER_IMAGE_VERSION: Version of the server image (default: test) +# - IMAGE_REGISTRY: Docker image registry (default: openim) +# - DOCKER_COMPOSE_FILE_URL: URL to the docker-compose.yml file +# +# Usage: +# SERVER_IMAGE_VERSION=latest IMAGE_REGISTRY=myregistry ./this_script.sh + + + + + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +trap 'openim::util::onCtrlC' INT + +chmod +x "${OPENIM_ROOT}"/scripts/*.sh + +openim::util::ensure_docker_daemon_connectivity + +# Default values for variables +: ${SERVER_IMAGE_VERSION:=test} +: ${IMAGE_REGISTRY:=openim} +: ${DOCKER_COMPOSE_FILE_URL:="https://raw.githubusercontent.com/openimsdk/openim-docker/main/docker-compose.yaml"} + +DOCKER_COMPOSE_COMMAND= +# Check if docker-compose command is available +openim::util::check_docker_and_compose_versions +if command -v docker compose &> /dev/null; then + openim::log::info "docker compose command is available" + DOCKER_COMPOSE_COMMAND="docker compose" +else + DOCKER_COMPOSE_COMMAND="docker-compose" +fi + +export SERVER_IMAGE_VERSION +export IMAGE_REGISTRY +"${OPENIM_ROOT}"/scripts/init-config.sh + +pushd "${OPENIM_ROOT}" +docker build -t "${IMAGE_REGISTRY}/openim-server:${SERVER_IMAGE_VERSION}" . +${DOCKER_COMPOSE_COMMAND} stop +curl "${DOCKER_COMPOSE_FILE_URL}" -o docker-compose.yml +${DOCKER_COMPOSE_COMMAND} up -d + +# Function to check container status +check_containers() { + if ! ${DOCKER_COMPOSE_COMMAND} ps | grep -q 'Up'; then + echo "Error: One or more docker containers failed to start." + ${DOCKER_COMPOSE_COMMAND} logs openim-server + ${DOCKER_COMPOSE_COMMAND} logs openim-chat + return 1 + fi + return 0 +} + +# Wait for a short period to allow containers to initialize +sleep 100 + +${DOCKER_COMPOSE_COMMAND} ps + +check_containers + +popd \ No newline at end of file diff --git a/scripts-new/install/README.md b/scripts-new/install/README.md new file mode 100644 index 000000000..8e710add8 --- /dev/null +++ b/scripts-new/install/README.md @@ -0,0 +1,116 @@ +# OpenIM Suite Scripts + +The OpenIM Suite represents a comprehensive collection of scripts, each tailored to manage and operate specific services within the OpenIM ecosystem. These scripts offer consistent, reliable, and efficient tools for initializing, controlling, and managing various OpenIM services on a Linux platform. + +## Features + +- **Robustness:** Built with Bash's error handling mechanisms (`errexit`, `nounset`, and `pipefail`), ensuring scripts fail fast and provide relevant error messages. +- **Modularity:** Each script is dedicated to a particular service, promoting clarity and ease of maintenance. +- **Comprehensive Logging:** Integrated logging utilities offer real-time insights into operations, enhancing transparency and debuggability. +- **Systemd Integration:** Where applicable, scripts integrate with the systemd service manager, offering standardized service controls like start, stop, restart, and status checks. + +## Scripts Overview + +1. **openim-api:** Control interface for the OpenIM API service. +2. **openim-cmdutils:** Utility toolkit for common OpenIM command-line operations. +3. **openim-crontask:** Manages the OpenIM CronTask service, with both direct and systemctl installation methods. +4. **openim-msggateway:** Script to operate the OpenIM Message Gateway service. +5. **openim-msgtransfer:** Manages the OpenIM Message Transfer functionalities. +6. **openim-push:** Interface for controlling the OpenIM Push Notification service. +7. **openim-rpc-auth:** Script dedicated to the OpenIM RPC Authentication service. +8. **openim-rpc-conversation:** Manages operations related to the OpenIM RPC Conversation service. +9. **openim-rpc-friend:** Control interface for the OpenIM RPC Friend functionalities. +10. **openim-rpc-group:** Script for managing the OpenIM RPC Group service. +11. **openim-rpc-msg:** Operates the OpenIM RPC Messaging service. +12. **openim-rpc-third:** Script dedicated to third-party integrations with OpenIM RPC. +13. **openim-rpc-user:** Control interface for OpenIM RPC User operations. + +## OpenIM Server Installation Script Usage + +The scripts within the OpenIM Suite generally adhere to two primary execution methodologies. To illustrate these methodologies, we'll use `openim-crontask` as a representative example. + +1. **Direct Script Execution:** Running the script directly, typically for straightforward start/stop operations. + + ```bash + ./[script-name].sh + ``` + +2. **Function-based Execution:** Invoking specific functions within the script for more specialized operations (e.g., install, uninstall). + + ```bash + ./scripts/install/install.sh [options] + ``` + +**Description:** +This script is designed to handle the installation, uninstallation, and status checking of OpenIM components on the server. OpenIM is a presumed communication or messaging platform. + +### Commands: +- **-i, --install**: + Initiate the installation of all OpenIM components. + +- **-u, --uninstall**: + Uninstall or remove all OpenIM components from the server. + +- **-s, --status**: + Check and report the current operational status of the installed OpenIM components. + +- **-h, --help**: + Display the help menu for available commands. + +### Example Usage: +To install all OpenIM components: +```bash +./scripts/install/install.sh -i +``` +or +```bash +./scripts/install/install.sh --install +``` +> **Note**: +> Ensure you have the necessary privileges to execute installation or uninstallation operations. It's generally recommended to take a backup before making major changes. + + +### 1. Direct Script Execution + +This method involves invoking the script directly, initiating its default behavior. For instance, with `openim-crontask`, direct execution will start the OpenIM CronTask as a background process. + +**Example:** + +```bash +./openim-crontask.sh +``` + +Upon execution, the script will source any necessary configurations, log the start of the CronTask, and finally run the CronTask in the background. The log messages will provide feedback about the process, ensuring the user is informed of the task's progress. + +### 2. Function-based Execution + +This approach is more specialized, enabling users to call specific functions defined within the script. This is particularly useful for tasks like installation, uninstallation, and status checks. + +For the `openim-crontask` script: + +- **Installation**: It includes building the service, generating configuration files, setting up systemd services, and starting the service. + + ```bash + ./openim-crontask.sh openim::crontask::install + ``` + +- **Uninstallation**: Stops the service, removes associated binaries, configuration files, and systemd service files. + + ```bash + ./openim-crontask.sh openim::crontask::uninstall + ``` + +- **Status Check**: Verifies the running status of the service, checking for active processes and listening ports. + + ```bash + ./openim-crontask.sh openim::crontask::status + ``` + +It's crucial to familiarize oneself with the available functions within each script. This ensures optimal utilization of the provided tools and a deeper understanding of the underlying operations. + + + +## Notes + +- Always ensure you have the correct permissions before executing any script. +- Environment variables may need to be set or sourced depending on your installation and configuration. \ No newline at end of file diff --git a/scripts-new/install/common.sh b/scripts-new/install/common.sh new file mode 100644 index 000000000..ed1ba1a4b --- /dev/null +++ b/scripts-new/install/common.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Common utilities, variables and checks for all build scripts. + + + + +# Sourced flag +COMMON_SOURCED=true +# The root of the build/dist directory +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +source "${OPENIM_ROOT}/scripts/lib/init.sh" +# Make sure the environment is only called via common to avoid too much nesting +source "${OPENIM_ROOT}/scripts/install/environment.sh" + +# This function returns a list of Prometheus ports for various services +# based on the provided configuration. Each service has its own dedicated +# port for monitoring purposes. +openim::common::prometheus_port() { + # Declare an array to hold all the Prometheus ports for different services + local targets=( + ${USER_PROM_PORT} # Prometheus port for user service + ${FRIEND_PROM_PORT} # Prometheus port for friend service + ${MESSAGE_PROM_PORT} # Prometheus port for message service + ${MSG_GATEWAY_PROM_PORT} # Prometheus port for message gateway service + ${GROUP_PROM_PORT} # Prometheus port for group service + ${AUTH_PROM_PORT} # Prometheus port for authentication service + ${PUSH_PROM_PORT} # Prometheus port for push notification service + ${CONVERSATION_PROM_PORT} # Prometheus port for conversation service + ${RTC_PROM_PORT} # Prometheus port for real-time communication service + ${THIRD_PROM_PORT} # Prometheus port for third-party integrations service + ${MSG_TRANSFER_PROM_PORT} # Prometheus port for message transfer service + ) + # Print the list of ports + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_PROM_PORT_TARGETS <<< "$(openim::common::prometheus_port)" +readonly OPENIM_PROM_PORT_TARGETS +readonly OPENIM_PROM_PORT_LISTARIES=("${OPENIM_PROM_PORT_TARGETS[@]##*/}") + +openim::common::service_name() { + local targets=( + openim-user + openim-friend + openim-msg + openim-msg-gateway + openim-group + openim-auth + openim-push + openim-conversation + openim-third + # openim-msg-transfer + + # api + openim-api + openim-ws + ) + echo "${targets[@]}" +} + +IFS=" " read -ra OPENIM_SERVER_NAME_TARGETS <<< "$(openim::common::service_name)" +readonly OPENIM_SERVER_NAME_TARGETS + +# Storing all the defined ports in an array for easy management and access. +# This array consolidates the port numbers for all the services defined above. +openim::common::service_port() { + local targets=( + ${OPENIM_USER_PORT} # User service + ${OPENIM_FRIEND_PORT} # Friend service + ${OPENIM_MESSAGE_PORT} # Message service + ${OPENIM_MESSAGE_GATEWAY_PORT} # Message gateway + ${OPENIM_GROUP_PORT} # Group service + ${OPENIM_AUTH_PORT} # Authorization service + ${OPENIM_PUSH_PORT} # Push service + ${OPENIM_CONVERSATION_PORT} # Conversation service + ${OPENIM_THIRD_PORT} # Third-party service + + # API PORT + ${API_OPENIM_PORT} # API service + ${OPENIM_WS_PORT} # WebSocket service + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_SERVER_PORT_TARGETS <<< "$(openim::common::service_port)" +readonly OPENIM_SERVER_PORT_TARGETS +readonly OPENIM_SERVER_PORT_LISTARIES=("${OPENIM_SERVER_PORT_TARGETS[@]##*/}") + + +OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER=() + +for target in "${OPENIM_SERVER_BINARIES_NO_TRANSFER[@]}"; do + OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER + + + +OPENIM_ALL_SERVICE_LIBRARIES=() +for target in "${OPENIM_SERVER_BINARIES_NO_CMDUTILS[@]}"; do + OPENIM_ALL_SERVICE_LIBRARIES+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_SERVICE_LIBRARIES + +openim::common::dependency_name() { + local targets=( + redis + zookeeper + kafka + mongodb + minio + ) + echo "${targets[@]}" +} + +IFS=" " read -ra OPENIM_DEPENDENCY_TARGETS <<< "$(openim::common::dependency_name)" +readonly OPENIM_DEPENDENCY_TARGETS + +# This function returns a list of ports for various services +# - zookeeper +# - kafka +# - mongodb +# - redis +# - minio +openim::common::dependency_port() { + local targets=( + ${REDIS_PORT} # Redis port + ${ZOOKEEPER_PORT} # Zookeeper port + ${KAFKA_PORT} # Kafka port + ${MONGO_PORT} # MongoDB port + ${MINIO_PORT} # MinIO port + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_DEPENDENCY_PORT_TARGETS <<< "$(openim::common::dependency_port)" +readonly OPENIM_DEPENDENCY_PORT_TARGETS +readonly OPENIM_DEPENDENCY_PORT_LISTARIES=("${OPENIM_DEPENDENCY_PORT_TARGETS[@]##*/}") + +# Execute commands that require root permission without entering a password +function openim::common::sudo { + echo ${LINUX_PASSWORD} | sudo -S $1 +} diff --git a/scripts-new/install/dependency.sh b/scripts-new/install/dependency.sh new file mode 100644 index 000000000..bad1cb6f9 --- /dev/null +++ b/scripts-new/install/dependency.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script will install the dependencies required for openim + + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +# Start MongoDB service +docker run -d \ +--name mongo \ +-p 37017:27017 \ +-v "${DATA_DIR}/components/mongodb/data/db:/data/db" \ +-v "${DATA_DIR}/components/mongodb/data/logs:/data/logs" \ +-v "${DATA_DIR}/components/mongodb/data/conf:/etc/mongo" \ +-v "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro" \ +-e TZ=Asia/Shanghai \ +-e wiredTigerCacheSizeGB=1 \ +-e MONGO_INITDB_ROOT_USERNAME=${OPENIM_USER} \ +-e MONGO_INITDB_ROOT_PASSWORD=${PASSWORD} \ +-e MONGO_INITDB_DATABASE=openim_v3 \ +-e MONGO_OPENIM_USERNAME=${OPENIM_USER} \ +-e MONGO_OPENIM_PASSWORD=${PASSWORD} \ +--restart always \ +mongo:6.0.2 --wiredTigerCacheSizeGB 1 --auth + +# Start Redis service +docker run -d \ +--name redis \ +-p 16379:6379 \ +-v "${DATA_DIR}/components/redis/data:/data" \ +-v "${DATA_DIR}/components/redis/config/redis.conf:/usr/local/redis/config/redis.conf" \ +-e TZ=Asia/Shanghai \ +--sysctl net.core.somaxconn=1024 \ +--restart always \ +redis:7.0.0 redis-server --requirepass ${PASSWORD} --appendonly yes + +# Start Zookeeper service +docker run -d \ +--name zookeeper \ +-p 2181:2181 \ +-v "/etc/localtime:/etc/localtime" \ +-e TZ=Asia/Shanghai \ +--restart always \ +wurstmeister/zookeeper + +# Start Kafka service +docker run -d \ +--name kafka \ +-p 9092:9092 \ +-e TZ=Asia/Shanghai \ +-e KAFKA_BROKER_ID=0 \ +-e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \ +-e KAFKA_CREATE_TOPICS="latestMsgToRedis:8:1,msgToPush:8:1,offlineMsgToMongoMysql:8:1" \ +-e KAFKA_ADVERTISED_LISTENERS="INSIDE://127.0.0.1:9092,OUTSIDE://103.116.45.174:9092" \ +-e KAFKA_LISTENERS="INSIDE://:9092,OUTSIDE://:9093" \ +-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP="INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT" \ +-e KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE \ +--restart always \ +--link zookeeper \ +wurstmeister/kafka + +# Start MinIO service +docker run -d \ +--name minio \ +-p 10005:9000 \ +-p 9090:9090 \ +-v "/mnt/data:/data" \ +-v "/mnt/config:/root/.minio" \ +-e MINIO_ROOT_USER=${OPENIM_USER} \ +-e MINIO_ROOT_PASSWORD=${PASSWORD} \ +--restart always \ +minio/minio server /data --console-address ':9090' diff --git a/scripts-new/install/environment.sh b/scripts-new/install/environment.sh new file mode 100644 index 000000000..64e76853d --- /dev/null +++ b/scripts-new/install/environment.sh @@ -0,0 +1,581 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is a file that initializes variables for the automation script that initializes the config file +# You need to supplement the script according to the specification. +# Read: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md +# 格式化 bash 注释:https://tool.lu/shell/ +# 配置中心文档:https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md + +OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" + +# 生成文件存放目录 +LOCAL_OUTPUT_ROOT="${OPENIM_ROOT}/${OUT_DIR:-_output}" +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +#TODO: Access to the OPENIM_IP networks outside, or you want to use the OPENIM_IP network +# OPENIM_IP=127.0.0.1 +if [ -z "${OPENIM_IP}" ]; then + OPENIM_IP=$(openim::util::get_server_ip) +fi + +# config.gateway custom bridge modes +# if [ -z "{IP_GATEWAY}" ] then +# IP_GATEWAY=$(openim::util::get_local_ip) +# fi + +function def() { + local var_name="$1" + local default_value="${2:-}" + eval "readonly $var_name=\"\${$var_name:-$(printf '%q' "$default_value")}\"" +} + +# OpenIM Docker Compose 数据存储的默认路径 +def "DATA_DIR" "${OPENIM_ROOT}" + +# 设置统一的用户名,方便记忆 +def "OPENIM_USER" "root" + +# 设置统一的密码,方便记忆 +readonly PASSWORD=${PASSWORD:-'openIM123'} + +# 设置统一的数据库名称,方便管理 +def "DATABASE_NAME" "openim_v3" + +# Linux系统 openim 用户 +def "LINUX_USERNAME" "openim" +readonly LINUX_PASSWORD=${LINUX_PASSWORD:-"${PASSWORD}"} + +# 设置安装目录 +def "INSTALL_DIR" "${LOCAL_OUTPUT_ROOT}/installs" +mkdir -p ${INSTALL_DIR} + +def "ENV_FILE" "${OPENIM_ROOT}/scripts/install/environment.sh" + +###################### Docker compose ################### +# OPENIM AND CHAT +def "CHAT_IMAGE_VERSION" "main" +def "SERVER_IMAGE_VERSION" "main" + +# Choose the appropriate image address, the default is GITHUB image, +# you can choose docker hub, for Chinese users can choose Ali Cloud +# export IMAGE_REGISTRY="ghcr.io/openimsdk" +# export IMAGE_REGISTRY="openim" +# export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk" +def "IMAGE_REGISTRY" "ghcr.io/openimsdk" +# def "IMAGE_REGISTRY" "openim" +# def "IMAGE_REGISTRY" "registry.cn-hangzhou.aliyuncs.com/openimsdk" + +# Choose the appropriate image tag, the default is the latest version +def "SERVER_IMAGE_TAG" "latest" + +###################### OpenIM Docker Network ###################### +# 设置 Docker 网络的网段 +readonly DOCKER_BRIDGE_SUBNET=${DOCKER_BRIDGE_SUBNET:-'172.28.0.0/16'} +IP_PREFIX=$(echo $DOCKER_BRIDGE_SUBNET | cut -d '/' -f 1) +SUBNET=$(echo $DOCKER_BRIDGE_SUBNET | cut -d '/' -f 2) +LAST_OCTET=$(echo $IP_PREFIX | cut -d '.' -f 4) + +generate_ip() { + local NEW_IP="$(echo $IP_PREFIX | cut -d '.' -f 1-3).$((LAST_OCTET++))" + echo $NEW_IP +} +LAST_OCTET=$((LAST_OCTET + 1)) +DOCKER_BRIDGE_GATEWAY=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +MONGO_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +REDIS_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +KAFKA_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +ZOOKEEPER_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +MINIO_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +OPENIM_WEB_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +OPENIM_SERVER_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +OPENIM_CHAT_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +PROMETHEUS_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +GRAFANA_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +NODE_EXPORTER_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=$(generate_ip) +LAST_OCTET=$((LAST_OCTET + 1)) +ALERT_MANAGER_NETWORK_ADDRESS=$(generate_ip) +###################### openim 配置 ###################### +# read: https://github.com/openimsdk/open-im-server/blob/main/deployment/README.md +def "OPENIM_DATA_DIR" "/data/openim" +def "OPENIM_INSTALL_DIR" "/opt/openim" +def "OPENIM_CONFIG_DIR" "/etc/openim/config" +def "OPENIM_LOG_DIR" "/var/log/openim" +def "CA_FILE" "${OPENIM_CONFIG_DIR}/cert/ca.pem" + +def "OPNEIM_CONFIG" "${OPENIM_ROOT}/config" +def "OPENIM_SERVER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # OpenIM服务地址 + +# OpenIM Websocket端口 +readonly OPENIM_WS_PORT=${OPENIM_WS_PORT:-'10001'} + +# OpenIM API端口 +readonly API_OPENIM_PORT=${API_OPENIM_PORT:-'10002'} +def "API_LISTEN_IP" "0.0.0.0" # API的监听IP + +###################### openim-chat 配置信息 ###################### +def "OPENIM_CHAT_DATA_DIR" "./openim-chat/${CHAT_IMAGE_VERSION}" +def "OPENIM_CHAT_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # OpenIM服务地址 +def "OPENIM_CHAT_API_PORT" "10008" # OpenIM API端口 +def "CHAT_API_LISTEN_IP" "" # OpenIM API的监听IP + +def "OPENIM_ADMIN_API_PORT" "10009" # OpenIM Admin API端口 +def "ADMIN_API_LISTEN_IP" "" # OpenIM Admin API的监听IP + +def "OPENIM_ADMIN_PORT" "30200" # OpenIM chat Admin端口 +def "OPENIM_CHAT_PORT" "30300" # OpenIM chat Admin的监听IP + +def "OPENIM_ADMIN_NAME" "admin" # openim-chat Admin用户名 +def "OPENIM_CHAT_NAME" "chat" # openim-chat chat用户名 + +# TODO 注意: 一般的配置都可以使用 def 函数来定义,如果是包含特殊字符,比如说: +# TODO readonly MSG_DESTRUCT_TIME=${MSG_DESTRUCT_TIME:-'0 2 * * *'} +# TODO 使用 readonly 来定义合适,负责无法正常解析, 并且 yaml 模板需要加 "" 来包裹 +###################### Env 配置信息 ###################### +def "ENVS_DISCOVERY" "zookeeper" + +###################### Zookeeper 配置信息 ###################### +def "ZOOKEEPER_SCHEMA" "openim" # Zookeeper的模式 +def "ZOOKEEPER_PORT" "12181" # Zookeeper的端口 +def "ZOOKEEPER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Zookeeper的地址 +def "ZOOKEEPER_USERNAME" "" # Zookeeper的用户名 +def "ZOOKEEPER_PASSWORD" "" # Zookeeper的密码 + +###################### MongoDB 配置信息 ###################### +def "MONGO_URI" # MongoDB的URI +def "MONGO_PORT" "37017" # MongoDB的端口 +def "MONGO_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # MongoDB的地址 +def "MONGO_DATABASE" "${DATABASE_NAME}" # MongoDB的数据库名 +def "MONGO_USERNAME" "root" # MongoDB的管理员身份用户名 +# MongoDB的管理员身份密码 +readonly MONGO_PASSWORD=${MONGO_PASSWORD:-"${PASSWORD}"} +# Mongo OpenIM 身份用户名 +def "MONGO_OPENIM_USERNAME" "openIM" +# Mongo OpenIM 身份密码 +readonly MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-"${PASSWORD}"} + +def "MONGO_MAX_POOL_SIZE" "100" # 最大连接池大小 + +###################### Object 配置信息 ###################### +# app要能访问到此ip和端口或域名 +readonly API_URL=${API_URL:-"http://${OPENIM_IP}:${API_OPENIM_PORT}"} + +def "OBJECT_ENABLE" "minio" # 对象是否启用 +# 对象的API地址 +readonly OBJECT_APIURL=${OBJECT_APIURL:-"${API_URL}"} +def "MINIO_BUCKET" "openim" # MinIO的存储桶名称 +def "MINIO_PORT" "10005" # MinIO的端口 +# MinIO的端点URL +def MINIO_ADDRESS "${DOCKER_BRIDGE_GATEWAY}" +readonly MINIO_ENDPOINT=${MINIO_ENDPOINT:-"http://${MINIO_ADDRESS}:${MINIO_PORT}"} +def "MINIO_ACCESS_KEY" "${OPENIM_USER}" # MinIO的访问密钥ID +readonly MINIO_SECRET_KEY=${MINIO_SECRET_KEY:-"${PASSWORD}"} +def "MINIO_SESSION_TOKEN" # MinIO的会话令牌 +readonly MINIO_SIGN_ENDPOINT=${MINIO_SIGN_ENDPOINT:-"http://${OPENIM_IP}:${MINIO_PORT}"} # signEndpoint为minio公网地址 +def "MINIO_PUBLIC_READ" "false" # 公有读 + +# 腾讯云COS的存储桶URL +def "COS_BUCKET_URL" "https://temp-1252357374.cos.ap-chengdu.myqcloud.com" +def "COS_SECRET_ID" # 腾讯云COS的密钥ID +def "COS_SECRET_KEY" # 腾讯云COS的密钥 +def "COS_SESSION_TOKEN" # 腾讯云COS的会话令牌 +def "COS_PUBLIC_READ" "false" # 公有读 +def "OSS_ENDPOINT" "https://oss-cn-chengdu.aliyuncs.com" # 阿里云OSS的端点URL +def "OSS_BUCKET" "demo-9999999" # 阿里云OSS的存储桶名称 +def "OSS_BUCKET_URL" "https://demo-9999999.oss-cn-chengdu.aliyuncs.com" # 阿里云OSS的存储桶URL +def "OSS_ACCESS_KEY_ID" # 阿里云OSS的访问密钥ID +def "OSS_ACCESS_KEY_SECRET" # 阿里云OSS的密钥 +def "OSS_SESSION_TOKEN" # 阿里云OSS的会话令牌 +def "OSS_PUBLIC_READ" "false" # 公有读 + +#七牛云配置信息 +def "KODO_ENDPOINT" "http://s3.cn-east-1.qiniucs.com" # 七牛云OSS的端点URL +def "KODO_BUCKET" "demo-9999999" # 七牛云OSS的存储桶名称 +def "KODO_BUCKET_URL" "http://your.domain.com" # 七牛云OSS的存储桶URL +def "KODO_ACCESS_KEY_ID" # 七牛云OSS的访问密钥ID +def "KODO_ACCESS_KEY_SECRET" # 七牛云OSS的密钥 +def "KODO_SESSION_TOKEN" # 七牛云OSS的会话令牌 +def "KODO_PUBLIC_READ" "false" # 公有读 + +# AWS Configuration Information +def "AWS_ENDPOINT" "" # AWS endpoint, generally not needed unless using a specific service +def "AWS_REGION" "us-east-1" # AWS Region +def "AWS_BUCKET" "demo-9999999" # AWS S3 Bucket Name +def "AWS_ACCESS_KEY_ID" # AWS Access Key ID +def "AWS_SECRET_ACCESS_KEY" # AWS Secret Access Key +def "AWS_PUBLIC_READ" "false" # Public read access + +###################### Redis 配置信息 ###################### +def "REDIS_PORT" "16379" # Redis的端口 +def "REDIS_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Redis的地址 +def "REDIS_USERNAME" # Redis的用户名 +readonly REDIS_PASSWORD=${REDIS_PASSWORD:-"${PASSWORD}"} + +###################### Kafka 配置信息 ###################### +def "KAFKA_USERNAME" # `Kafka` 的用户名 +def "KAFKA_PASSWORD" # `Kafka` 的密码 +def "KAFKA_PORT" "19094" # `Kafka` 的端口 +def "KAFKA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # `Kafka` 的地址 +def "KAFKA_LATESTMSG_REDIS_TOPIC" "latestMsgToRedis" # `Kafka` 的最新消息到Redis的主题 +def "KAFKA_OFFLINEMSG_MONGO_TOPIC" "offlineMsgToMongoMysql" # `Kafka` 的离线消息到Mongo的主题 +def "KAFKA_MSG_PUSH_TOPIC" "msgToPush" # `Kafka` 的消息到推送的主题 +def "KAFKA_CONSUMERGROUPID_REDIS" "redis" # `Kafka` 的消费组ID到Redis +def "KAFKA_CONSUMERGROUPID_MONGO" "mongo" # `Kafka` 的消费组ID到Mongo +def "KAFKA_CONSUMERGROUPID_MYSQL" "mysql" # `Kafka` 的消费组ID到MySql +def "KAFKA_CONSUMERGROUPID_PUSH" "push" # `Kafka` 的消费组ID到推送 + +###################### openim-web 配置信息 ###################### +def "OPENIM_WEB_PORT" "11001" # openim-web的端口 + +###################### openim-admin-front 配置信息 ###################### +def "OPENIM_ADMIN_FRONT_PORT" "11002" # openim-admin-front的端口 + +###################### RPC 配置信息 ###################### +def "RPC_REGISTER_IP" # RPC的注册IP +def "RPC_LISTEN_IP" "0.0.0.0" # RPC的监听IP + +###################### prometheus 配置 ###################### +def "PROMETHEUS_PORT" "19090" # Prometheus的端口 +def "PROMETHEUS_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Prometheus的地址 + +###################### node-exporter 配置 ###################### +def "NODE_EXPORTER_PORT" "19100" # node-exporter的端口 +def "NODE_EXPORTER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # node-exporter的地址 + +###################### alertmanagerS 配置 ###################### +def "ALERT_MANAGER_PORT" "19093" # node-exporter的端口 +def "ALERT_MANAGER_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # node-exporter的地址 + +###################### AlertManager Configuration Script ###################### +# 解析超时 +readonly ALERTMANAGER_RESOLVE_TIMEOUT=${ALERTMANAGER_RESOLVE_TIMEOUT:-'5m'} +# 发件人邮箱 +readonly ALERTMANAGER_SMTP_FROM=${ALERTMANAGER_SMTP_FROM:-'alert@openim.io'} +# SMTP服务器地址和端口 +readonly ALERTMANAGER_SMTP_SMARTHOST=${ALERTMANAGER_SMTP_SMARTHOST:-'smtp.163.com:465'} +# SMTP认证用户名 +readonly ALERTMANAGER_SMTP_AUTH_USERNAME=${SMTP_USERNAME:-"alert@openim.io"} +# SMTP认证密码 +readonly ALERTMANAGER_SMTP_AUTH_PASSWORD=${SMTP_PASSWORD:-"YOURAUTHPASSWORD"} +# SMTP是否需要TLS +readonly ALERTMANAGER_SMTP_REQUIRE_TLS=${ALERTMANAGER_SMTP_REQUIRE_TLS:-"false"} +# SMTP HELO/EHLO标识符 +readonly ALERTMANAGER_SMTP_HELLO=${ALERTMANAGER_SMTP_HELLO:-"xxx监控告警"} +# 邮箱接收人 +readonly ALERTMANAGER_EMAIL_TO=${ALERTMANAGER_EMAIL_TO:-"alert@example.com"} +# 邮箱主题 +readonly ALERTMANAGER_EMAIL_SUBJECT=${ALERTMANAGER_EMAIL_SUBJECT:-"{EMAIL_SUBJECT:-'[Alert] Notification'}"} +# 是否发送已解决的告警 +readonly ALERTMANAGER_SEND_RESOLVED=${ALERTMANAGER_SEND_RESOLVED:-"{SEND_RESOLVED:-'true'}"} + +###################### Grafana 配置信息 ###################### +def "GRAFANA_PORT" "13000" # Grafana的端口 +def "GRAFANA_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Grafana的地址 +###################### RPC Port Configuration Variables ###################### +# For launching multiple programs, just fill in multiple ports separated by commas +# For example: +# readonly OPENIM_USER_PORT=${OPENIM_USER_PORT:-'10110, 10111, 10112'} #Try not to have Spaces + +# OpenIM用户服务端口 +readonly OPENIM_USER_PORT=${OPENIM_USER_PORT:-'10110'} +# OpenIM朋友服务端口 +readonly OPENIM_FRIEND_PORT=${OPENIM_FRIEND_PORT:-'10120'} +# OpenIM消息服务端口 +readonly OPENIM_MESSAGE_PORT=${OPENIM_MESSAGE_PORT:-'10130'} +# OpenIM消息网关服务端口 +readonly OPENIM_MESSAGE_GATEWAY_PORT=${OPENIM_MESSAGE_GATEWAY_PORT:-'10140'} +# OpenIM组服务端口 +readonly OPENIM_GROUP_PORT=${OPENIM_GROUP_PORT:-'10150'} +# OpenIM授权服务端口 +readonly OPENIM_AUTH_PORT=${OPENIM_AUTH_PORT:-'10160'} +# OpenIM推送服务端口 +readonly OPENIM_PUSH_PORT=${OPENIM_PUSH_PORT:-'10170'} +# OpenIM对话服务端口 +readonly OPENIM_CONVERSATION_PORT=${OPENIM_CONVERSATION_PORT:-'10180'} +# OpenIM第三方服务端口 +readonly OPENIM_THIRD_PORT=${OPENIM_THIRD_PORT:-'10190'} + +###################### RPC Register Name Variables ###################### +def "OPENIM_USER_NAME" "User" # OpenIM用户服务名称 +def "OPENIM_FRIEND_NAME" "Friend" # OpenIM朋友服务名称 +def "OPENIM_MSG_NAME" "Msg" # OpenIM消息服务名称 +def "OPENIM_PUSH_NAME" "Push" # OpenIM推送服务名称 +def "OPENIM_MESSAGE_GATEWAY_NAME" "MessageGateway" # OpenIM消息网关服务名称 +def "OPENIM_GROUP_NAME" "Group" # OpenIM组服务名称 +def "OPENIM_AUTH_NAME" "Auth" # OpenIM授权服务名称 +def "OPENIM_CONVERSATION_NAME" "Conversation" # OpenIM对话服务名称 +def "OPENIM_THIRD_NAME" "Third" # OpenIM第三方服务名称 + +###################### Log Configuration Variables ###################### +def "LOG_STORAGE_LOCATION" "${OPENIM_ROOT}/_output/logs/" # 日志存储位置 +def "LOG_ROTATION_TIME" "24" # 日志轮替时间 +def "LOG_REMAIN_ROTATION_COUNT" "2" # 保留的日志轮替数量 +def "LOG_REMAIN_LOG_LEVEL" "6" # 保留的日志级别 +def "LOG_IS_STDOUT" "false" # 是否将日志输出到标准输出 +def "LOG_IS_JSON" "false" # 日志是否为JSON格式 +def "LOG_WITH_STACK" "false" # 日志是否带有堆栈信息 + +###################### Variables definition ###################### +def "WEBSOCKET_MAX_CONN_NUM" "100000" # Websocket最大连接数 +def "WEBSOCKET_MAX_MSG_LEN" "4096" # Websocket最大消息长度 +def "WEBSOCKET_TIMEOUT" "10" # Websocket超时 +def "PUSH_ENABLE" "getui" # 推送是否启用 +# GeTui推送URL +readonly GETUI_PUSH_URL=${GETUI_PUSH_URL:-'https://restapi.getui.com/v2/$appId'} +def "GETUI_MASTER_SECRET" "" # GeTui主密钥 +def "GETUI_APP_KEY" "" # GeTui应用密钥 +def "GETUI_INTENT" "" # GeTui推送意图 +def "GETUI_CHANNEL_ID" "" # GeTui渠道ID +def "GETUI_CHANNEL_NAME" "" # GeTui渠道名称 +def "FCM_SERVICE_ACCOUNT" "x.json" # FCM服务账户 +def "JPNS_APP_KEY" "" # JPNS应用密钥 +def "JPNS_MASTER_SECRET" "" # JPNS主密钥 +def "JPNS_PUSH_URL" "" # JPNS推送URL +def "JPNS_PUSH_INTENT" "" # JPNS推送意图 +def "IM_ADMIN_USERID" "imAdmin" # IM管理员ID +def "IM_ADMIN_NAME" "imAdmin" # IM管理员昵称 +def "MULTILOGIN_POLICY" "1" # 多登录策略 +def "CHAT_PERSISTENCE_MYSQL" "true" # 聊天持久化MySQL +def "MSG_CACHE_TIMEOUT" "86400" # 消息缓存超时 +def "GROUP_MSG_READ_RECEIPT" "true" # 群消息已读回执启用 +def "SINGLE_MSG_READ_RECEIPT" "true" # 单一消息已读回执启用 +def "RETAIN_CHAT_RECORDS" "365" # 保留聊天记录 +# 聊天记录清理时间 +readonly CHAT_RECORDS_CLEAR_TIME=${CHAT_RECORDS_CLEAR_TIME:-'0 2 * * 3'} +# 消息销毁时间 +readonly MSG_DESTRUCT_TIME=${MSG_DESTRUCT_TIME:-'0 2 * * *'} +# 密钥 +readonly SECRET=${SECRET:-"${PASSWORD}"} +def "TOKEN_EXPIRE" "90" # Token到期时间 +def "FRIEND_VERIFY" "false" # 朋友验证 +def "IOS_PUSH_SOUND" "xxx" # IOS推送声音 +def "IOS_BADGE_COUNT" "true" # IOS徽章计数 +def "IOS_PRODUCTION" "false" # IOS生产 +# callback 配置 +def "CALLBACK_ENABLE" "false" # 是否开启 Callback +def "CALLBACK_TIMEOUT" "5" # 最长超时时间 +def "CALLBACK_FAILED_CONTINUE" "true" # 失败后是否继续 +###################### Prometheus 配置信息 ###################### +# 是否启用 Prometheus +readonly PROMETHEUS_ENABLE=${PROMETHEUS_ENABLE:-'true'} +readonly GRAFANA_URL=${GRAFANA_URL:-"http://${OPENIM_IP}:${GRAFANA_PORT}/"} +# Api 服务的 Prometheus 端口 +readonly API_PROM_PORT=${API_PROM_PORT:-'20100'} +# User 服务的 Prometheus 端口 +readonly USER_PROM_PORT=${USER_PROM_PORT:-'20110'} +# Friend 服务的 Prometheus 端口 +readonly FRIEND_PROM_PORT=${FRIEND_PROM_PORT:-'20120'} +# Message 服务的 Prometheus 端口 +readonly MESSAGE_PROM_PORT=${MESSAGE_PROM_PORT:-'20130'} +# Message Gateway 服务的 Prometheus 端口 +readonly MSG_GATEWAY_PROM_PORT=${MSG_GATEWAY_PROM_PORT:-'20140'} +# Group 服务的 Prometheus 端口 +readonly GROUP_PROM_PORT=${GROUP_PROM_PORT:-'20150'} +# Auth 服务的 Prometheus 端口 +readonly AUTH_PROM_PORT=${AUTH_PROM_PORT:-'20160'} +# Push 服务的 Prometheus 端口 +readonly PUSH_PROM_PORT=${PUSH_PROM_PORT:-'20170'} +# Conversation 服务的 Prometheus 端口 +readonly CONVERSATION_PROM_PORT=${CONVERSATION_PROM_PORT:-'20230'} +# RTC 服务的 Prometheus 端口 +readonly RTC_PROM_PORT=${RTC_PROM_PORT:-'21300'} +# Third 服务的 Prometheus 端口 +readonly THIRD_PROM_PORT=${THIRD_PROM_PORT:-'21301'} + +# Message Transfer 服务的 Prometheus 端口列表 +readonly MSG_TRANSFER_PROM_PORT=${MSG_TRANSFER_PROM_PORT:-'21400, 21401, 21402, 21403'} +readonly MSG_TRANSFER_PROM_ADDRESS_PORT=${MSG_TRANSFER_PROM_ADDRESS_PORT:-"${DOCKER_BRIDGE_GATEWAY}:21400, ${DOCKER_BRIDGE_GATEWAY}:21401, ${DOCKER_BRIDGE_GATEWAY}:21402, ${DOCKER_BRIDGE_GATEWAY}:21403"} + +###################### OpenIM openim-api ###################### +def "OPENIM_API_HOST" "127.0.0.1" +def "OPENIM_API_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-api" # OpenIM openim-api 二进制文件路径 +def "OPENIM_API_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-api 配置文件路径 +def "OPENIM_API_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-api" # OpenIM openim-api 日志存储路径 +def "OPENIM_API_LOG_LEVEL" "info" # OpenIM openim-api 日志级别 +def "OPENIM_API_LOG_MAX_SIZE" "100" # OpenIM openim-api 日志最大大小(MB) +def "OPENIM_API_LOG_MAX_BACKUPS" "7" # OpenIM openim-api 日志最大备份数 +def "OPENIM_API_LOG_MAX_AGE" "7" # OpenIM openim-api 日志最大保存时间(天) +def "OPENIM_API_LOG_COMPRESS" "false" # OpenIM openim-api 日志是否压缩 +def "OPENIM_API_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-api 日志是否带有堆栈信息 + +###################### OpenIM openim-cmdutils ###################### +def "OPENIM_CMDUTILS_HOST" "127.0.0.1" +def "OPENIM_CMDUTILS_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-cmdutils" # OpenIM openim-cmdutils 二进制文件路径 +def "OPENIM_CMDUTILS_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-cmdutils 配置文件路径 +def "OPENIM_CMDUTILS_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-cmdutils" # OpenIM openim-cmdutils 日志存储路径 +def "OPENIM_CMDUTILS_LOG_LEVEL" "info" # OpenIM openim-cmdutils 日志级别 +def "OPENIM_CMDUTILS_LOG_MAX_SIZE" "100" # OpenIM openim-cmdutils 日志最大大小(MB) +def "OPENIM_CMDUTILS_LOG_MAX_BACKUPS" "7" # OpenIM openim-cmdutils 日志最大备份数 +def "OPENIM_CMDUTILS_LOG_MAX_AGE" "7" # OpenIM openim-cmdutils 日志最大保存时间(天) +def "OPENIM_CMDUTILS_LOG_COMPRESS" "false" # OpenIM openim-cmdutils 日志是否压缩 +def "OPENIM_CMDUTILS_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-cmdutils 日志是否带有堆栈信息 + +###################### OpenIM openim-crontask ###################### +def "OPENIM_CRONTASK_HOST" "127.0.0.1" +def "OPENIM_CRONTASK_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-crontask" # OpenIM openim-crontask 二进制文件路径 +def "OPENIM_CRONTASK_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-crontask 配置文件路径 +def "OPENIM_CRONTASK_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-crontask" # OpenIM openim-crontask 日志存储路径 +def "OPENIM_CRONTASK_LOG_LEVEL" "info" # OpenIM openim-crontask 日志级别 +def "OPENIM_CRONTASK_LOG_MAX_SIZE" "100" # OpenIM openim-crontask 日志最大大小(MB) +def "OPENIM_CRONTASK_LOG_MAX_BACKUPS" "7" # OpenIM openim-crontask 日志最大备份数 +def "OPENIM_CRONTASK_LOG_MAX_AGE" "7" # OpenIM openim-crontask 日志最大保存时间(天) +def "OPENIM_CRONTASK_LOG_COMPRESS" "false" # OpenIM openim-crontask 日志是否压缩 +def "OPENIM_CRONTASK_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-crontask 日志是否带有堆栈信息 + +###################### OpenIM openim-msggateway ###################### +def "OPENIM_MSGGATEWAY_HOST" "127.0.0.1" +def "OPENIM_MSGGATEWAY_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-msggateway" +def "OPENIM_MSGGATEWAY_CONFIG" "${OPENIM_ROOT}/config/" +def "OPENIM_MSGGATEWAY_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-msggateway" +def "OPENIM_MSGGATEWAY_LOG_LEVEL" "info" +def "OPENIM_MSGGATEWAY_LOG_MAX_SIZE" "100" +def "OPENIM_MSGGATEWAY_LOG_MAX_BACKUPS" "7" +def "OPENIM_MSGGATEWAY_LOG_MAX_AGE" "7" +def "OPENIM_MSGGATEWAY_LOG_COMPRESS" "false" +def "OPENIM_MSGGATEWAY_LOG_WITH_STACK" "${LOG_WITH_STACK}" + +# 定义 openim-msggateway 的数量, 默认为 4 +readonly OPENIM_MSGGATEWAY_NUM=${OPENIM_MSGGATEWAY_NUM:-'4'} + +###################### OpenIM openim-msgtransfer ###################### +def "OPENIM_MSGTRANSFER_HOST" "127.0.0.1" +def "OPENIM_MSGTRANSFER_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer" # OpenIM openim-msgtransfer 二进制文件路径 +def "OPENIM_MSGTRANSFER_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-msgtransfer 配置文件路径 +def "OPENIM_MSGTRANSFER_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-msgtransfer" # OpenIM openim-msgtransfer 日志存储路径 +def "OPENIM_MSGTRANSFER_LOG_LEVEL" "info" # OpenIM openim-msgtransfer 日志级别 +def "OPENIM_MSGTRANSFER_LOG_MAX_SIZE" "100" # OpenIM openim-msgtransfer 日志最大大小(MB) +def "OPENIM_MSGTRANSFER_LOG_MAX_BACKUPS" "7" # OpenIM openim-msgtransfer 日志最大备份数 +def "OPENIM_MSGTRANSFER_LOG_MAX_AGE" "7" # OpenIM openim-msgtransfer 日志最大保存时间(天) +def "OPENIM_MSGTRANSFER_LOG_COMPRESS" "false" # OpenIM openim-msgtransfer 日志是否压缩 +def "OPENIM_MSGTRANSFER_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-msgtransfer 日志是否带有堆栈信息 + +###################### OpenIM openim-push ###################### +def "OPENIM_PUSH_HOST" "127.0.0.1" +def "OPENIM_PUSH_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-push" # OpenIM openim-push 二进制文件路径 +def "OPENIM_PUSH_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-push 配置文件路径 +def "OPENIM_PUSH_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-push" # OpenIM openim-push 日志存储路径 +def "OPENIM_PUSH_LOG_LEVEL" "info" # OpenIM openim-push 日志级别 +def "OPENIM_PUSH_LOG_MAX_SIZE" "100" # OpenIM openim-push 日志最大大小(MB) +def "OPENIM_PUSH_LOG_MAX_BACKUPS" "7" # OpenIM openim-push 日志最大备份数 +def "OPENIM_PUSH_LOG_MAX_AGE" "7" # OpenIM openim-push 日志最大保存时间(天) +def "OPENIM_PUSH_LOG_COMPRESS" "false" # OpenIM openim-push 日志是否压缩 +def "OPENIM_PUSH_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-push 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-auth ###################### +def "OPENIM_RPC_AUTH_HOST" "127.0.0.1" +def "OPENIM_RPC_AUTH_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-auth" # OpenIM openim-rpc-auth 二进制文件路径 +def "OPENIM_RPC_AUTH_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-auth 配置文件路径 +def "OPENIM_RPC_AUTH_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-auth" # OpenIM openim-rpc-auth 日志存储路径 +def "OPENIM_RPC_AUTH_LOG_LEVEL" "info" # OpenIM openim-rpc-auth 日志级别 +def "OPENIM_RPC_AUTH_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-auth 日志最大大小(MB) +def "OPENIM_RPC_AUTH_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-auth 日志最大备份数 +def "OPENIM_RPC_AUTH_LOG_MAX_AGE" "7" # OpenIM openim-rpc-auth 日志最大保存时间(天) +def "OPENIM_RPC_AUTH_LOG_COMPRESS" "false" # OpenIM openim-rpc-auth 日志是否压缩 +def "OPENIM_RPC_AUTH_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-auth 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-conversation ###################### +def "OPENIM_RPC_CONVERSATION_HOST" "127.0.0.1" +def "OPENIM_RPC_CONVERSATION_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-conversation" # OpenIM openim-rpc-conversation 二进制文件路径 +def "OPENIM_RPC_CONVERSATION_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-conversation 配置文件路径 +def "OPENIM_RPC_CONVERSATION_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-conversation" # OpenIM openim-rpc-conversation 日志存储路径 +def "OPENIM_RPC_CONVERSATION_LOG_LEVEL" "info" # OpenIM openim-rpc-conversation 日志级别 +def "OPENIM_RPC_CONVERSATION_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-conversation 日志最大大小(MB) +def "OPENIM_RPC_CONVERSATION_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-conversation 日志最大备份数 +def "OPENIM_RPC_CONVERSATION_LOG_MAX_AGE" "7" # OpenIM openim-rpc-conversation 日志最大保存时间(天) +def "OPENIM_RPC_CONVERSATION_LOG_COMPRESS" "false" # OpenIM openim-rpc-conversation 日志是否压缩 +def "OPENIM_RPC_CONVERSATION_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-conversation 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-friend ###################### +def "OPENIM_RPC_FRIEND_HOST" "127.0.0.1" +def "OPENIM_RPC_FRIEND_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-friend" # OpenIM openim-rpc-friend 二进制文件路径 +def "OPENIM_RPC_FRIEND_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-friend 配置文件路径 +def "OPENIM_RPC_FRIEND_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-friend" # OpenIM openim-rpc-friend 日志存储路径 +def "OPENIM_RPC_FRIEND_LOG_LEVEL" "info" # OpenIM openim-rpc-friend 日志级别 +def "OPENIM_RPC_FRIEND_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-friend 日志最大大小(MB) +def "OPENIM_RPC_FRIEND_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-friend 日志最大备份数 +def "OPENIM_RPC_FRIEND_LOG_MAX_AGE" "7" # OpenIM openim-rpc-friend 日志最大保存时间(天) +def "OPENIM_RPC_FRIEND_LOG_COMPRESS" "false" # OpenIM openim-rpc-friend 日志是否压缩 +def "OPENIM_RPC_FRIEND_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-friend 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-group ###################### +def "OPENIM_RPC_GROUP_HOST" "127.0.0.1" +def "OPENIM_RPC_GROUP_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-group" # OpenIM openim-rpc-group 二进制文件路径 +def "OPENIM_RPC_GROUP_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-group 配置文件路径 +def "OPENIM_RPC_GROUP_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-group" # OpenIM openim-rpc-group 日志存储路径 +def "OPENIM_RPC_GROUP_LOG_LEVEL" "info" # OpenIM openim-rpc-group 日志级别 +def "OPENIM_RPC_GROUP_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-group 日志最大大小(MB) +def "OPENIM_RPC_GROUP_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-group 日志最大备份数 +def "OPENIM_RPC_GROUP_LOG_MAX_AGE" "7" # OpenIM openim-rpc-group 日志最大保存时间(天) +def "OPENIM_RPC_GROUP_LOG_COMPRESS" "false" # OpenIM openim-rpc-group 日志是否压缩 +def "OPENIM_RPC_GROUP_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-group 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-msg ###################### +def "OPENIM_RPC_MSG_HOST" "127.0.0.1" +def "OPENIM_RPC_MSG_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-msg" # OpenIM openim-rpc-msg 二进制文件路径 +def "OPENIM_RPC_MSG_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-msg 配置文件路径 +def "OPENIM_RPC_MSG_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-msg" # OpenIM openim-rpc-msg 日志存储路径 +def "OPENIM_RPC_MSG_LOG_LEVEL" "info" # OpenIM openim-rpc-msg 日志级别 +def "OPENIM_RPC_MSG_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-msg 日志最大大小(MB) +def "OPENIM_RPC_MSG_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-msg 日志最大备份数 +def "OPENIM_RPC_MSG_LOG_MAX_AGE" "7" # OpenIM openim-rpc-msg 日志最大保存时间(天) +def "OPENIM_RPC_MSG_LOG_COMPRESS" "false" # OpenIM openim-rpc-msg 日志是否压缩 +def "OPENIM_RPC_MSG_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-msg 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-third ###################### +def "OPENIM_RPC_THIRD_HOST" "127.0.0.1" +def "OPENIM_RPC_THIRD_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-third" # OpenIM openim-rpc-third 二进制文件路径 +def "OPENIM_RPC_THIRD_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-third 配置文件路径 +def "OPENIM_RPC_THIRD_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-third" # OpenIM openim-rpc-third 日志存储路径 +def "OPENIM_RPC_THIRD_LOG_LEVEL" "info" # OpenIM openim-rpc-third 日志级别 +def "OPENIM_RPC_THIRD_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-third 日志最大大小(MB) +def "OPENIM_RPC_THIRD_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-third 日志最大备份数 +def "OPENIM_RPC_THIRD_LOG_MAX_AGE" "7" # OpenIM openim-rpc-third 日志最大保存时间(天) +def "OPENIM_RPC_THIRD_LOG_COMPRESS" "false" # OpenIM openim-rpc-third 日志是否压缩 +def "OPENIM_RPC_THIRD_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-third 日志是否带有堆栈信息 + +###################### OpenIM openim-rpc-user ###################### +def "OPENIM_RPC_USER_HOST" "127.0.0.1" +def "OPENIM_RPC_USER_BINARY" "${OPENIM_OUTPUT_HOSTBIN}/openim-rpc-user" # OpenIM openim-rpc-user 二进制文件路径 +def "OPENIM_RPC_USER_CONFIG" "${OPENIM_ROOT}/config/" # OpenIM openim-rpc-user 配置文件路径 +def "OPENIM_RPC_USER_LOG_DIR" "${LOG_STORAGE_LOCATION}/openim-rpc-user" # OpenIM openim-rpc-user 日志存储路径 +def "OPENIM_RPC_USER_LOG_LEVEL" "info" # OpenIM openim-rpc-user 日志级别 +def "OPENIM_RPC_USER_LOG_MAX_SIZE" "100" # OpenIM openim-rpc-user 日志最大大小(MB) +def "OPENIM_RPC_USER_LOG_MAX_BACKUPS" "7" # OpenIM openim-rpc-user 日志最大备份数 +def "OPENIM_RPC_USER_LOG_MAX_AGE" "7" # OpenIM openim-rpc-user 日志最大保存时间(天) +def "OPENIM_RPC_USER_LOG_COMPRESS" "false" # OpenIM openim-rpc-user 日志是否压缩 +def "OPENIM_RPC_USER_LOG_WITH_STACK" "${LOG_WITH_STACK}" # OpenIM openim-rpc-user 日志是否带有堆栈信息 + +###################### 设计中...暂时不需要###################### + +# openimctl 配置 +def "CONFIG_USER_USERNAME" "admin" +def "CONFIG_USER_PASSWORD" "Admin@2021" +def "CONFIG_USER_CLIENT_CERTIFICATE" "${HOME}/.openim/cert/admin.pem" +def "CONFIG_USER_CLIENT_KEY" "${HOME}/.openim/cert/admin-key.pem" +def "CONFIG_SERVER_ADDRESS" "${OPENIM_APISERVER_HOST}:${OPENIM_APISERVER_SECURE_BIND_PORT}" +def "CONFIG_SERVER_CERTIFICATE_AUTHORITY" "${CA_FILE}" diff --git a/scripts-new/install/install-protobuf.sh b/scripts-new/install/install-protobuf.sh new file mode 100644 index 000000000..838b390b5 --- /dev/null +++ b/scripts-new/install/install-protobuf.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# -------------------------------------------------------------- +# OpenIM Protoc Tool v1.0.0 +# -------------------------------------------------------------- +# OpenIM has released its custom Protoc tool version v1.0.0. +# This tool is customized to meet the specific needs of OpenIM and resides in its separate repository. +# It can be downloaded from the following link: +# https://github.com/OpenIMSDK/Open-IM-Protoc/releases/tag/v1.0.0 +# +# About the tool: +# https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md +# Download link (Windows): https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/windows.zip +# Download link (Linux): https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/linux.zip +# +# Installation steps (taking Windows as an example): +# 1. Visit the above link and download the version suitable for Windows. +# 2. Extract the downloaded file. +# 3. Add the extracted tool to your PATH environment variable so that it can be run directly from the command line. +# +# Note: The specific installation and usage instructions may vary based on the tool's actual implementation. It's advised to refer to official documentation. +# -------------------------------------------------------------- + +PROTOC_DOWNLOAD_URL="https://github.com/OpenIMSDK/Open-IM-Protoc/releases/download/v1.0.0/linux.zip" +DOWNLOAD_DIR="/tmp/openim-protoc" +INSTALL_DIR="/usr/local/bin" + +function help_message { + echo "Usage: ./install-protobuf.sh [option]" + echo "Options:" + echo "-i, --install Install the OpenIM Protoc tool." + echo "-u, --uninstall Uninstall the OpenIM Protoc tool." + echo "-r, --reinstall Reinstall the OpenIM Protoc tool." + echo "-c, --check Check if the OpenIM Protoc tool is installed." + echo "-h, --help Display this help message." +} + +function install_protobuf { + echo "Installing OpenIM Protoc tool..." + + # Create temporary directory and download the zip file + mkdir -p $DOWNLOAD_DIR + wget $PROTOC_DOWNLOAD_URL -O $DOWNLOAD_DIR/linux.zip + + # Unzip the file + unzip -o $DOWNLOAD_DIR/linux.zip -d $DOWNLOAD_DIR + + # Move binaries to the install directory and make them executable + sudo cp $DOWNLOAD_DIR/linux/protoc $INSTALL_DIR/ + sudo cp $DOWNLOAD_DIR/linux/protoc-gen-go $INSTALL_DIR/ + sudo chmod +x $INSTALL_DIR/protoc + sudo chmod +x $INSTALL_DIR/protoc-gen-go + + # Clean up + rm -rf $DOWNLOAD_DIR + + echo "OpenIM Protoc tool installed successfully!" +} + +function uninstall_protobuf { + echo "Uninstalling OpenIM Protoc tool..." + + # Removing binaries from the install directory + sudo rm -f $INSTALL_DIR/protoc + sudo rm -f $INSTALL_DIR/protoc-gen-go + + echo "OpenIM Protoc tool uninstalled successfully!" +} + +function reinstall_protobuf { + echo "Reinstalling OpenIM Protoc tool..." + uninstall_protobuf + install_protobuf +} + +function check_protobuf { + echo "Checking for OpenIM Protoc tool installation..." + + which protoc > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "OpenIM Protoc tool is installed." + else + echo "OpenIM Protoc tool is not installed." + fi +} + +while [ "$1" != "" ]; do + case $1 in + -i | --install ) install_protobuf + ;; + -u | --uninstall ) uninstall_protobuf + ;; + -r | --reinstall ) reinstall_protobuf + ;; + -c | --check ) check_protobuf + ;; + -h | --help ) help_message + exit + ;; + * ) help_message + exit 1 + esac + shift +done diff --git a/scripts-new/install/install.sh b/scripts-new/install/install.sh new file mode 100644 index 000000000..bb09675bf --- /dev/null +++ b/scripts-new/install/install.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM Server Installation Script +# +# Description: +# This script is designed to handle the installation, Is a deployment solution +# that uses the Linux systen extension. uninstallation, and +# status checking of OpenIM components on the server. OpenIM is a presumed +# communication or messaging platform based on the context. +# +# Usage: +# To utilize this script, you need to invoke it with specific commands +# and options as detailed below. +# +# Commands: +# -i, --install : Use this command to initiate the installation of all +# OpenIM components. +# -u, --uninstall : Use this command to uninstall or remove all +# OpenIM components from the server. +# -s, --status : This command can be used to check and report the +# current operational status of the installed OpenIM components. +# -h, --help : For any assistance or to view the available commands, +# use this command to display the help menu. +# +# Example Usage: +# To install all OpenIM components: +# ./scripts/install/install.sh -i +# or +# ./scripts/install/install.sh --install +# +# Note: +# Ensure you have the necessary privileges to execute installation or +# uninstallation operations. It's generally recommended to take a backup +# before making major changes. +# +############################################################################### + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +${OPENIM_ROOT}/scripts/install/openim-man.sh +${OPENIM_ROOT}/scripts/install/openim-tools.sh +${OPENIM_ROOT}/scripts/install/test.sh + +# Detailed help function +function openim::install::show_help() { + echo "OpenIM Installer" + echo "Usage: $0 [options]" + echo "" + echo "Commands:" + echo " -i, --install Install all OpenIM components." + echo " -u, --uninstall Remove all OpenIM components." + echo " -s, --status Check the current status of OpenIM components." + echo " -h, --help Show this help menu." + echo "" + echo "Example: " + echo " $0 -i Will install all OpenIM components." + echo " $0 --install Same as above." +} + +function openim::install::install_openim() { + openim::common::sudo "mkdir -p ${OPENIM_DATA_DIR} ${OPENIM_INSTALL_DIR} ${OPENIM_CONFIG_DIR} ${OPENIM_LOG_DIR}" + openim::log::info "check openim dependency" + openim::common::sudo "cp -r ${OPENIM_ROOT}/config/* ${OPENIM_CONFIG_DIR}/" + + ${OPENIM_ROOT}/scripts/genconfig.sh ${ENV_FILE} ${OPENIM_ROOT}/deployments/templates/config.yaml > ${OPENIM_CONFIG_DIR}/config.yaml + ${OPENIM_ROOT}/scripts/genconfig.sh ${ENV_FILE} ${OPENIM_ROOT}/deployments/templates/prometheus.yml > ${OPENIM_CONFIG_DIR}/prometheus.yml + + openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]} + + ${OPENIM_ROOT}/scripts/install/openim-msggateway.sh openim::msggateway::install || return 1 + ${OPENIM_ROOT}/scripts/install/openim-msgtransfer.sh openim::msgtransfer::install || return 1 + ${OPENIM_ROOT}/scripts/install/openim-push.sh openim::push::install || return 1 + ${OPENIM_ROOT}/scripts/install/openim-crontask.sh openim::crontask::install || return 1 + ${OPENIM_ROOT}/scripts/install/openim-rpc.sh openim::rpc::install || return 1 + ${OPENIM_ROOT}/scripts/install/openim-api.sh openim::api::install || return 1 + + openim::common::sudo "cp -r ${OPENIM_ROOT}/deployments/templates/openim.target /etc/systemd/system/openim.target" + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart openim.target" + openim::common::sudo "systemctl enable openim.target" + openim::log::success "openim install success" +} + +function openim::uninstall::uninstall_openim() { + openim::log::info "uninstall openim" + + ${OPENIM_ROOT}/scripts/install/openim-msggateway.sh openim::msggateway::uninstall || return 1 + ${OPENIM_ROOT}/scripts/install/openim-msgtransfer.sh openim::msgtransfer::uninstall || return 1 + ${OPENIM_ROOT}/scripts/install/openim-push.sh openim::push::uninstall || return 1 + ${OPENIM_ROOT}/scripts/install/openim-crontask.sh openim::crontask::uninstall || return 1 + ${OPENIM_ROOT}/scripts/install/openim-rpc.sh openim::rpc::uninstall || return 1 + ${OPENIM_ROOT}/scripts/install/openim-api.sh openim::api::uninstall || return 1 + + set +o errexit + openim::common::sudo "systemctl stop openim.target" + openim::common::sudo "systemctl disable openim.target" + openim::common::sudo "rm -f /etc/systemd/system/openim.target" + + openim::log::success "openim uninstall success" +} + +function openim::install::status() { + openim::log::info "check openim status" + + ${OPENIM_ROOT}/scripts/install/openim-msggateway.sh openim::msggateway::status || return 1 + ${OPENIM_ROOT}/scripts/install/openim-msgtransfer.sh openim::msgtransfer::status || return 1 + ${OPENIM_ROOT}/scripts/install/openim-push.sh openim::push::status || return 1 + ${OPENIM_ROOT}/scripts/install/openim-crontask.sh openim::crontask::status || return 1 + ${OPENIM_ROOT}/scripts/install/openim-rpc.sh openim::rpc::status || return 1 + ${OPENIM_ROOT}/scripts/install/openim-api.sh openim::api::status || return 1 + + openim::log::success "openim status success" +} + +# If no arguments are provided, show help +if [[ $# -eq 0 ]]; then + openim::install::show_help + exit 0 +fi + +# Argument parsing to call functions based on user input +while (( "$#" )); do + case "$1" in + -i|--install) + openim::install::install_openim + shift + ;; + -u|--uninstall) + openim::uninstall::uninstall_openim + shift + ;; + -s|--status) + openim::install::status + shift + ;; + -h|--help|*) + openim::install::show_help + exit 0 + ;; + esac +done \ No newline at end of file diff --git a/scripts-new/install/openim-api.sh b/scripts-new/install/openim-api.sh new file mode 100644 index 000000000..8403382e6 --- /dev/null +++ b/scripts-new/install/openim-api.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-api" + +readonly OPENIM_API_PORT_TARGETS=( + ${API_OPENIM_PORT} +) +readonly OPENIM_API_PORT_LISTARIES=("${OPENIM_API_PORT_TARGETS[@]##*/}") + +readonly OPENIM_API_SERVICE_TARGETS=( + openim-api +) +readonly OPENIM_API_SERVICE_LISTARIES=("${OPENIM_API_SERVICE_TARGETS[@]##*/}") + +readonly OPENIM_API_PROMETHEUS_PORT_TARGETS=( + ${API_PROM_PORT} +) +readonly OPENIM_API_PROMETHEUS_PORT_LISTARIES=("${OPENIM_API_PROMETHEUS_PORT_TARGETS[@]##*/}") + +function openim::api::start() { + rm -rf "$TMP_LOG_FILE" + + echo "++ OPENIM_API_SERVICE_LISTARIES: ${OPENIM_API_SERVICE_LISTARIES[@]}" + echo "++ OPENIM_API_PORT_LISTARIES: ${OPENIM_API_PORT_LISTARIES[@]}" + echo "++ OpenIM API config path: ${OPENIM_API_CONFIG}" + + openim::log::info "Starting ${SERVER_NAME} ..." + + readonly OPENIM_API_SERVER_LIBRARIES="${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}" + + + printf "+------------------------+--------------+\n" + printf "| Service Name | Port |\n" + printf "+------------------------+--------------+\n" + + + local length=${#OPENIM_API_SERVICE_LISTARIES[@]} + for ((i=0; i> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + if [ $? -ne 0 ]; then + openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}." + return 1 + fi + return 0 +} + +###################################### Linux Systemd ###################################### +SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" + +# Print the necessary information after installation +function openim::api::info() { +cat << EOF +openim-api listen on: ${OPENIM_API_HOST}:${API_OPENIM_PORT} +EOF +} + +# install openim-api +function openim::api::install() { + openim::log::info "Installing ${SERVER_NAME} ..." + + pushd "${OPENIM_ROOT}" + + # 1. Build openim-api + make build BINS=${SERVER_NAME} + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME} ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/${SERVER_NAME}/${SERVER_NAME}" + + # 2. Generate and install the openim-api configuration file (config) + openim::log::status "${SERVER_NAME} config file: ${OPENIM_CONFIG_DIR}/config.yaml" + + # 3. Create and install the ${SERVER_NAME} systemd unit file + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${SERVER_NAME} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATH}" + openim::log::status "${SERVER_NAME} systemd file: ${SYSTEM_FILE_PATH}" + + # 4. Start the openim-api service + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart ${SERVER_NAME}" + openim::common::sudo "systemctl enable ${SERVER_NAME}" + openim::api::status || return 1 + openim::api::info + + openim::log::info "install ${SERVER_NAME} successfully" + popd +} + +# Unload +function openim::api::uninstall() { + openim::log::info "Uninstalling ${SERVER_NAME} ..." + + set +o errexit + openim::common::sudo "systemctl stop ${SERVER_NAME}" + openim::common::sudo "systemctl disable ${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" + openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" + + openim::log::info "uninstall ${SERVER_NAME} successfully" +} + +# Status Check +function openim::api::status() { + openim::log::info "Checking ${SERVER_NAME} status ..." + + # Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully. + systemctl status ${SERVER_NAME}|grep -q 'active' || { + openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" + return 1 + } + + openim::util::check_ports ${OPENIM_API_PORT_LISTARIES[@]} +} + +if [[ "$*" =~ openim::api:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-crontask.sh b/scripts-new/install/openim-crontask.sh new file mode 100644 index 000000000..d785b37a4 --- /dev/null +++ b/scripts-new/install/openim-crontask.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM CronTask Control Script +# +# Description: +# This script provides a control interface for the OpenIM CronTask service within a Linux environment. It supports two installation methods: installation via function calls to systemctl, and direct installation through background processes. +# +# Features: +# 1. Robust error handling leveraging Bash built-ins such as 'errexit', 'nounset', and 'pipefail'. +# 2. Capability to source common utility functions and configurations, ensuring environmental consistency. +# 3. Comprehensive logging tools, offering clear operational insights. +# 4. Support for creating, managing, and interacting with Linux systemd services. +# 5. Mechanisms to verify the successful running of the service. +# +# Usage: +# 1. Direct Script Execution: +# This will start the OpenIM CronTask directly through a background process. +# Example: ./openim-crontask.sh openim::crontask::start +# +# 2. Controlling through Functions for systemctl operations: +# Specific operations like installation, uninstallation, and status check can be executed by passing the respective function name as an argument to the script. +# Example: ./openim-crontask.sh openim::crontask::install +# +# Note: Ensure that the appropriate permissions and environmental variables are set prior to script execution. +# + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-crontask" + +function openim::crontask::start() { + + rm -rf "$TMP_LOG_FILE" + + openim::log::info "Start OpenIM Cron, binary root: ${SERVER_NAME}" + openim::log::status "Start OpenIM Cron, path: ${OPENIM_CRONTASK_BINARY}" + + openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}" + #nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + cmd="${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + return 0 + +} + +###################################### Linux Systemd ###################################### +SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" + +# Print the necessary information after installation +function openim::crontask::info() { +cat << EOF +openim-crontask listen on: ${OPENIM_CRONTASK_HOST} +EOF +} + +# install openim-crontask +function openim::crontask::install() { + pushd "${OPENIM_ROOT}" + + # 1. Build openim-crontask + make build BINS=${SERVER_NAME} + + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME} ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/${SERVER_NAME}/${SERVER_NAME}" + + # 2. Generate and install the openim-crontask configuration file (openim-crontask.yaml) + openim::log::status "${SERVER_NAME} config file: ${OPENIM_CONFIG_DIR}/config.yaml" + + # 3. Create and install the ${SERVER_NAME} systemd unit file + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${SERVER_NAME} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATH}" + openim::log::status "${SERVER_NAME} systemd file: ${SYSTEM_FILE_PATH}" + + # 4. Start the openim-crontask service + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart ${SERVER_NAME}" + openim::common::sudo "systemctl enable ${SERVER_NAME}" + openim::crontask::status || return 1 + openim::crontask::info + + openim::log::info "install ${SERVER_NAME} successfully" + popd +} + + +# Unload +function openim::crontask::uninstall() { + set +o errexit + openim::common::sudo "systemctl stop ${SERVER_NAME}" + openim::common::sudo "systemctl disable ${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" + openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" + + openim::log::info "uninstall ${SERVER_NAME} successfully" +} + +# Status Check +function openim::crontask::status() { + # Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully. + if systemctl is-active --quiet "${SERVER_NAME}"; then + openim::log::info "${SERVER_NAME} is running successfully." + else + openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" + return 1 + fi +} + +if [[ "$*" =~ openim::crontask:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-man.sh b/scripts-new/install/openim-man.sh new file mode 100644 index 000000000..642588a28 --- /dev/null +++ b/scripts-new/install/openim-man.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# openim-man.sh Script to manage man pages for openim +# +# Description: +# This script manages the man pages for the OpenIM software suite. +# It provides facilities to install, uninstall, and verify the +# installation status of the man pages related to OpenIM components. +# +# Usage: +# ./openim-man.sh openim::man::install - Install man pages +# ./openim-man.sh openim::man::uninstall - Uninstall man pages +# ./openim-man.sh openim::man::status - Check installation status +# +# Dependencies: +# - Assumes there's a common.sh in "${OPENIM_ROOT}/scripts/install/" +# containing shared functions and variables. +# - Relies on the script "${OPENIM_ROOT}/scripts/update-generated-docs.sh" +# to generate the man pages. +# +# Notes: +# - This script must be run with appropriate permissions to modify the +# system man directories. +# - Always ensure you're in the script's directory or provide the correct +# path when executing. +################################################################################ + +# Define the root of the build/dist directory +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) + +# Ensure the common script is sourced +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}/scripts/install/common.sh" + +# Print usage information after installation +function openim::man::info() { +cat <<- EOF +Usage: + man openim-server to see openim-server help # Display the man page for openim-server +EOF +} + +# Install the man pages for openim +function openim::man::install() { + # Navigate to the openim root directory + pushd "${OPENIM_ROOT}" > /dev/null + + # Generate man pages for each component + "${OPENIM_ROOT}/scripts/update-generated-docs.sh" + openim::common::sudo "cp docs/man/man1/* /usr/share/man/man1/" + + # Verify installation status + if openim::man::status; then + openim::log::info "Installed openim-server man page successfully" + openim::man::info + fi + + # Return to the original directory + popd > /dev/null +} + +# Uninstall the man pages for openim +function openim::man::uninstall() { + # Turn off exit-on-error temporarily to handle non-existing files gracefully + set +o errexit + openim::common::sudo "rm -f /usr/share/man/man1/openim-*" + + + openim::log::info "Uninstalled openim man pages successfully" +} + +# Check the installation status of the man pages +function openim::man::status() { + if ! ls /usr/share/man/man1/openim-* &> /dev/null; then + openim::log::error "OpenIM man files not found. Perhaps they were not installed correctly." + return 1 + fi + return 0 +} + +# Execute the appropriate function based on the given arguments +if [[ "$*" =~ openim::man:: ]]; then + eval "$*" +fi diff --git a/scripts-new/install/openim-msggateway.sh b/scripts-new/install/openim-msggateway.sh new file mode 100644 index 000000000..ed7910805 --- /dev/null +++ b/scripts-new/install/openim-msggateway.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Common utilities, variables and checks for all build scripts. + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + + +SERVER_NAME="openim-msggateway" + +function openim::msggateway::start() { + + rm -rf "$TMP_LOG_FILE" + + openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" + openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGGATEWAY_BINARY}" + + + # OpenIM message gateway service port + OPENIM_MESSAGE_GATEWAY_PORTS=$(openim::util::list-to-string ${OPENIM_MESSAGE_GATEWAY_PORT} ) + read -a OPENIM_MSGGATEWAY_PORTS_ARRAY <<< ${OPENIM_MESSAGE_GATEWAY_PORTS} + openim::util::stop_services_on_ports ${OPENIM_MSGGATEWAY_PORTS_ARRAY[*]} + # OpenIM WS port + OPENIM_WS_PORTS=$(openim::util::list-to-string ${OPENIM_WS_PORT} ) + read -a OPENIM_WS_PORTS_ARRAY <<< ${OPENIM_WS_PORTS} + + # Message Gateway Prometheus port of the service + MSG_GATEWAY_PROM_PORTS=$(openim::util::list-to-string ${MSG_GATEWAY_PROM_PORT} ) + read -a MSG_GATEWAY_PROM_PORTS_ARRAY <<< ${MSG_GATEWAY_PROM_PORTS} + + openim::log::status "OpenIM Mssage Getway ports: ${OPENIM_MESSAGE_GATEWAY_PORTS[*]}" + openim::log::status "OpenIM WS ports: ${OPENIM_WS_PORTS[*]}" + openim::log::status "OpenIM Prometheus ports: ${MSG_GATEWAY_PROM_PORTS[*]}" + + openim::log::status "OpenIM Msggateway config path: ${OPENIM_MSGGATEWAY_CONFIG}" + + if [ ${#OPENIM_MSGGATEWAY_PORTS_ARRAY[@]} -ne ${#OPENIM_WS_PORTS_ARRAY[@]} ]; then + openim::log::error_exit "ws_ports does not match push_rpc_ports or prome_ports in quantity!!!" + fi + + for ((i = 0; i < ${#OPENIM_WS_PORTS_ARRAY[@]}; i++)); do + openim::log::info "start push process, port: ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]}, prometheus port: ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" + + PROMETHEUS_PORT_OPTION="" + if [[ -n "${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" ]]; then + PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" + fi + cmd="${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + # nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + + done + return 0 +} + +###################################### Linux Systemd ###################################### +SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" + +# Print the necessary information after installation +function openim::msggateway::info() { +cat << EOF +openim-msggateway listen on: ${OPENIM_MSGGATEWAY_HOST}:${OPENIM_MESSAGE_GATEWAY_PORT} +EOF +} + +# install openim-msggateway +function openim::msggateway::install() { + pushd "${OPENIM_ROOT}" + + # 1. Build openim-msggateway + make build BINS=${SERVER_NAME} + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME} ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + + # 2. Generate and install the openim-msggateway configuration file (openim-msggateway.yaml) + # nono + + # 3. Create and install the ${SERVER_NAME} systemd unit file + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${SERVER_NAME} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATH}" + openim::log::status "${SERVER_NAME} systemd file: ${SYSTEM_FILE_PATH}" + + # 4. Start the openim-msggateway service + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart ${SERVER_NAME}" + openim::common::sudo "systemctl enable ${SERVER_NAME}" + openim::msggateway::status || return 1 + openim::msggateway::info + + openim::log::info "install ${SERVER_NAME} successfully" + popd +} + + +# Unload +function openim::msggateway::uninstall() { + set +o errexit + openim::common::sudo "systemctl stop ${SERVER_NAME}" + openim::common::sudo "systemctl disable ${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" + openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" + + openim::log::info "uninstall ${SERVER_NAME} successfully" +} + +# Status Check +function openim::msggateway::status() { + # Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully. + systemctl status ${SERVER_NAME}|grep -q 'active' || { + openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" + + return 1 + } + + # The listening port is hardcode in the configuration file + if echo | telnet ${OPENIM_MSGGATEWAY_HOST} ${OPENIM_MESSAGE_GATEWAY_PORT} 2>&1|grep refused &>/dev/null;then + openim::log::error "cannot access health check port, ${SERVER_NAME} maybe not startup" + return 1 + fi +} + +if [[ "$*" =~ openim::msggateway:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-msgtransfer.sh b/scripts-new/install/openim-msgtransfer.sh new file mode 100644 index 000000000..8403d1cfc --- /dev/null +++ b/scripts-new/install/openim-msgtransfer.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Use: +# ./scripts/install/openim-msgtransfer.sh openim::msgtransfer::start + +# Common utilities, variables and checks for all build scripts. + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-msgtransfer" + +function openim::msgtransfer::start() { + + rm -rf "$TMP_LOG_FILE" + + openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" + openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGTRANSFER_BINARY}" + + # Message Transfer Prometheus port list + MSG_TRANSFER_PROM_PORTS=(openim::util::list-to-string ${MSG_TRANSFER_PROM_PORT} ) + + openim::log::status "OpenIM Prometheus ports: ${MSG_TRANSFER_PROM_PORTS[*]}" + + openim::log::status "OpenIM Msggateway config path: ${OPENIM_MSGTRANSFER_CONFIG}" + + openim::log::info "openim maggateway num: ${OPENIM_MSGGATEWAY_NUM}" + + if [ "${OPENIM_MSGGATEWAY_NUM}" -lt 1 ]; then + opeim::log::error "OPENIM_MSGGATEWAY_NUM must be greater than 0" + fi + + if [ ${OPENIM_MSGGATEWAY_NUM} -ne $((${#MSG_TRANSFER_PROM_PORTS[@]} - 1)) ]; then + openim::log::error "OPENIM_MSGGATEWAY_NUM must be equal to the number of MSG_TRANSFER_PROM_PORTS" + fi + + for (( i=0; i<$OPENIM_MSGGATEWAY_NUM; i++ )) do + openim::log::info "prometheus port: ${MSG_TRANSFER_PROM_PORTS[$i]}" + PROMETHEUS_PORT_OPTION="" + if [[ -n "${MSG_TRANSFER_PROM_PORTS[$i+1]}" ]]; then + PROMETHEUS_MSG_TRANSFER_PORT="${MSG_TRANSFER_PROM_PORTS[$i+1]%,}" + openim::util::stop_services_on_ports ${PROMETHEUS_MSG_TRANSFER_PORT} + PROMETHEUS_PORT_OPTION="--prometheus_port ${PROMETHEUS_MSG_TRANSFER_PORT}" + fi + cmd="${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i}" + #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + #nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + done + return 0 +} + +function openim::msgtransfer::check() { + PIDS=$(pgrep -f "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer") + if [ -z "$PIDS" ]; then + NUM_PROCESSES=0 + else + NUM_PROCESSES=$(echo "$PIDS" | wc -l) + fi + if [ "$NUM_PROCESSES" -eq "$OPENIM_MSGGATEWAY_NUM" ]; then + for PID in $PIDS; do + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + ps -p $PID -o pid,cmd + elif [[ "$OSTYPE" == "darwin"* ]]; then + ps -p $PID -o pid,comm + else + openim::log::error "Unsupported OS type: $OSTYPE" + fi + done + else + openim::log::error "Expected $OPENIM_MSGGATEWAY_NUM OpenIM msgtransfer processes, but found $NUM_PROCESSES msgtransfer processes running" + return 1 + fi + return 0 +} + +function openim::msgtransfer::check_for_stop() { + PIDS=$(pgrep -f "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer") || PIDS="0" + if [ "$PIDS" = "0" ]; then + return 0 + fi + + NUM_PROCESSES=$(echo "$PIDS" | wc -l | xargs) + + if [ "$NUM_PROCESSES" -gt 0 ]; then + openim::log::error "Found $NUM_PROCESSES processes for $OPENIM_OUTPUT_HOSTBIN/openim-msgtransfer" + for PID in $PIDS; do + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,cmd)\033[0m" + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,comm)\033[0m" + else + openim::log::error "Unsupported OS type: $OSTYPE" + fi + done + openim::log::error "Processes have not been stopped properly." + else + openim::log::success "All openim-msgtransfer processes have been stopped properly." + fi + return 0 +} + + +###################################### Linux Systemd ###################################### +SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" + +# Print the necessary information after installation +function openim::msgtransfer::info() { +cat << EOF +openim-msgtransfer listen on: ${OPENIM_MSGTRANSFER_HOST} +EOF +} + +# install openim-msgtransfer +function openim::msgtransfer::install() { + pushd "${OPENIM_ROOT}" + + # 1. Build openim-msgtransfer + make build BINS=${SERVER_NAME} + + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME} ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/${SERVER_NAME}/${SERVER_NAME}" + + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/bin/${SERVER_NAME}" + + # 2. Generate and install the openim-msgtransfer configuration file (openim-msgtransfer.yaml) + # nono + + # 3. Create and install the ${SERVER_NAME} systemd unit file + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${SERVER_NAME} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATH}" + openim::log::status "${SERVER_NAME} systemd file: ${SYSTEM_FILE_PATH}" + + # 4. Start the openim-msgtransfer service + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart ${SERVER_NAME}" + openim::common::sudo "systemctl enable ${SERVER_NAME}" + openim::msgtransfer::status || return 1 + openim::msgtransfer::info + + openim::log::info "install ${SERVER_NAME} successfully" + popd +} + + +# Unload +function openim::msgtransfer::uninstall() { + set +o errexit + openim::common::sudo "systemctl stop ${SERVER_NAME}" + openim::common::sudo "systemctl disable ${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" + openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" + + openim::log::info "uninstall ${SERVER_NAME} successfully" +} + +# Status Check +function openim::msgtransfer::status() { + # Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully. + if systemctl is-active --quiet "${SERVER_NAME}"; then + openim::log::info "${SERVER_NAME} is running successfully." + else + openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" + return 1 + fi +} + +if [[ "$*" =~ openim::msgtransfer:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-push.sh b/scripts-new/install/openim-push.sh new file mode 100644 index 000000000..aafb24c7b --- /dev/null +++ b/scripts-new/install/openim-push.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM Push Control Script +# +# Description: +# This script provides a control interface for the OpenIM Push service within a Linux environment. It supports two installation methods: installation via function calls to systemctl, and direct installation through background processes. +# +# Features: +# 1. Robust error handling leveraging Bash built-ins such as 'errexit', 'nounset', and 'pipefail'. +# 2. Capability to source common utility functions and configurations, ensuring environmental consistency. +# 3. Comprehensive logging tools, offering clear operational insights. +# 4. Support for creating, managing, and interacting with Linux systemd services. +# 5. Mechanisms to verify the successful running of the service. +# +# Usage: +# 1. Direct Script Execution: +# This will start the OpenIM push directly through a background process. +# Example: ./openim-push.sh +# +# 2. Controlling through Functions for systemctl operations: +# Specific operations like installation, uninstallation, and status check can be executed by passing the respective function name as an argument to the script. +# Example: ./openim-push.sh openim::push::install +# +# ENVIRONMENT VARIABLES: +# export OPENIM_PUSH_BINARY="8080 8081 8082" +# export OPENIM_PUSH_PORT="9090 9091 9092" +# +# Note: Ensure that the appropriate permissions and environmental variables are set prior to script execution. +# + + + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-push" + +function openim::push::start() { + + rm -rf "$TMP_LOG_FILE" + + openim::log::status "Start OpenIM Push, binary root: ${SERVER_NAME}" + openim::log::info "Start OpenIM Push, path: ${OPENIM_PUSH_BINARY}" + + openim::log::status "prepare start push process, path: ${OPENIM_PUSH_BINARY}" + openim::log::status "prepare start push process, port: ${OPENIM_PUSH_PORT}, prometheus port: ${PUSH_PROM_PORT}" + + OPENIM_PUSH_PORTS_ARRAY=$(openim::util::list-to-string ${OPENIM_PUSH_PORT} ) + PUSH_PROM_PORTS_ARRAY=$(openim::util::list-to-string ${PUSH_PROM_PORT} ) + + openim::log::status "push port list: ${OPENIM_PUSH_PORTS_ARRAY[@]}" + openim::log::status "prometheus port list: ${PUSH_PROM_PORTS_ARRAY[@]}" + + if [ ${#OPENIM_PUSH_PORTS_ARRAY[@]} -ne ${#PUSH_PROM_PORTS_ARRAY[@]} ]; then + openim::log::error "The length of the two port lists is different!" + fi + + for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do + openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" + cmd="${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]}" + #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + #nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + done + return 0 + +} + +###################################### Linux Systemd ###################################### +SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" + +# Print the necessary information after installation +function openim::push::info() { +cat << EOF +openim-push listen on: ${OPENIM_PUSH_HOST}:${OPENIM_PUSH_PORT} +EOF +} + +# install openim-push +function openim::push::install() { + pushd "${OPENIM_ROOT}" + + # 1. Build openim-push + make build BINS=${SERVER_NAME} + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME} ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::log::status "${SERVER_NAME} binary: ${OPENIM_INSTALL_DIR}/${SERVER_NAME}/${SERVER_NAME}" + + # 2. Generate and install the openim-push configuration file (config) + openim::log::status "${SERVER_NAME} config file: ${OPENIM_CONFIG_DIR}/config.yaml" + + # 3. Create and install the ${SERVER_NAME} systemd unit file + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${SERVER_NAME} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATH}" + openim::log::status "${SERVER_NAME} systemd file: ${SYSTEM_FILE_PATH}" + + # 4. Start the openim-push service + openim::common::sudo "systemctl daemon-reload" + openim::common::sudo "systemctl restart ${SERVER_NAME}" + openim::common::sudo "systemctl enable ${SERVER_NAME}" + openim::push::status || return 1 + openim::push::info + + openim::log::info "install ${SERVER_NAME} successfully" + popd +} + +# Unload +function openim::push::uninstall() { + set +o errexit + openim::common::sudo "systemctl stop ${SERVER_NAME}" + openim::common::sudo "systemctl disable ${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" + openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" + + openim::log::info "uninstall ${SERVER_NAME} successfully" +} + +# Status Check +function openim::push::status() { + # Check the running status of the ${SERVER_NAME}. If active (running) is displayed, the ${SERVER_NAME} is started successfully. + systemctl status ${SERVER_NAME}|grep -q 'active' || { + openim::log::error "${SERVER_NAME} failed to start, maybe not installed properly" + return 1 + } + + # The listening port is hardcode in the configuration file + if echo | telnet ${OPENIM_MSGGATEWAY_HOST} ${OPENIM_PUSH_PORT} 2>&1|grep refused &>/dev/null;then # Assuming a different port for push + openim::log::error "cannot access health check port, ${SERVER_NAME} maybe not startup" + return 1 + fi +} + +if [[ "$*" =~ openim::push:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-rpc.sh b/scripts-new/install/openim-rpc.sh new file mode 100644 index 000000000..1265a11c6 --- /dev/null +++ b/scripts-new/install/openim-rpc.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM RPC Service Control Script +# +# Description: +# This script provides a control interface for the OpenIM RPC service within a Linux environment. It offers functionalities to start multiple RPC services, each denoted by their respective names under openim::rpc::service_name. +# +# Features: +# 1. Robust error handling using Bash built-ins like 'errexit', 'nounset', and 'pipefail'. +# 2. The capability to source common utility functions and configurations to ensure uniform environmental settings. +# 3. Comprehensive logging functionalities, providing a detailed understanding of operational processes. +# 4. Provision for declaring and managing a set of RPC services, each associated with its unique name and corresponding ports. +# 5. The ability to define and associate Prometheus ports for service monitoring purposes. +# 6. Functionalities to start each RPC service, along with its designated ports, in a sequence. +# +# Usage: +# 1. Direct Script Execution: +# This initiates all the RPC services declared under the function openim::rpc::service_name. +# Example: ./openim-rpc-{rpc-name}.sh openim::rpc::start +# 2. Controlling through Functions for systemctl operations: +# Specific operations like installation, uninstallation, and status check can be executed by passing the respective function name as an argument to the script. +# Example: ./openim-rpc-{rpc-name}.sh openim::rpc::install +# +# Note: Before executing this script, ensure that the necessary permissions are granted and relevant environmental variables are set. +# + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-rpc" +readonly OPENIM_RPC_CONFIG="${OPENIM_ROOT}"/config + +openim::rpc::service_name() { + local targets=( + openim-rpc-user + openim-rpc-friend + openim-rpc-msg + openim-rpc-group + openim-rpc-auth + openim-rpc-conversation + openim-rpc-third + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_RPC_SERVICE_TARGETS <<< "$(openim::rpc::service_name)" +readonly OPENIM_RPC_SERVICE_TARGETS +readonly OPENIM_RPC_SERVICE_LISTARIES=("${OPENIM_RPC_SERVICE_TARGETS[@]##*/}") + + +OPENIM_ALL_RPC_FULL_PATH=() +for target in openim::rpc::service_name; do + OPENIM_ALL_RPC_FULL_PATH+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_RPC_FULL_PATH + + + + +# Make sure the environment is only called via common to avoid too much nesting +openim::rpc::service_port() { + local targets=( + ${OPENIM_USER_PORT} # User service 10110 + ${OPENIM_FRIEND_PORT} # Friend service 10120 + ${OPENIM_MESSAGE_PORT} # Message service 10130 + # ${OPENIM_MESSAGE_GATEWAY_PORT} # Message gateway 10140 + ${OPENIM_GROUP_PORT} # Group service 10150 + ${OPENIM_AUTH_PORT} # Authorization service 10160 + # ${OPENIM_PUSH_PORT} # Push service 10170 + ${OPENIM_CONVERSATION_PORT} # Conversation service 10180 + ${OPENIM_THIRD_PORT} # Third-party service 10190 + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_RPC_PORT_TARGETS <<< "$(openim::rpc::service_port)" +readonly OPENIM_RPC_PORT_TARGETS +readonly OPENIM_RPC_PORT_LISTARIES=("${OPENIM_RPC_PORT_TARGETS[@]##*/}") + +openim::rpc::prometheus_port() { + # Declare an array to hold all the Prometheus ports for different services + local targets=( + ${USER_PROM_PORT} # Prometheus port for user service + ${FRIEND_PROM_PORT} # Prometheus port for friend service + ${MESSAGE_PROM_PORT} # Prometheus port for message service + ${GROUP_PROM_PORT} # Prometheus port for group service + ${AUTH_PROM_PORT} # Prometheus port for authentication service + ${CONVERSATION_PROM_PORT} # Prometheus port for conversation service + ${THIRD_PROM_PORT} # Prometheus port for third-party integrations service + ) + # Print the list of ports + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_RPC_PROM_PORT_TARGETS <<< "$(openim::rpc::prometheus_port)" +readonly OPENIM_RPC_PROM_PORT_TARGETS +readonly OPENIM_RPC_PROM_PORT_LISTARIES=("${OPENIM_RPC_PROM_PORT_TARGETS[@]##*/}") + +function openim::rpc::start() { + rm -rf "$TMP_LOG_FILE" + + echo "OPENIM_RPC_SERVICE_LISTARIES: ${OPENIM_RPC_SERVICE_LISTARIES[@]}" + echo "OPENIM_RPC_PROM_PORT_LISTARIES: ${OPENIM_RPC_PROM_PORT_LISTARIES[@]}" + echo "OPENIM_RPC_PORT_LISTARIES: ${OPENIM_RPC_PORT_LISTARIES[@]}" + + openim::log::info "Starting ${SERVER_NAME} ..." + + printf "+------------------------+-------+-----------------+\n" + printf "| Service Name | Port | Prometheus Port |\n" + printf "+------------------------+-------+-----------------+\n" + + length=${#OPENIM_RPC_SERVICE_LISTARIES[@]} + + for ((i=0; i<$length; i++)); do + printf "| %-22s | %-5s | %-15s |\n" "${OPENIM_RPC_SERVICE_LISTARIES[$i]}" "${OPENIM_RPC_PORT_LISTARIES[$i]}" "${OPENIM_RPC_PROM_PORT_LISTARIES[$i]}" + printf "+------------------------+-------+-----------------+\n" + done + + + # start all rpc services + for ((i = 0; i < ${#OPENIM_RPC_SERVICE_LISTARIES[*]}; i++)); do + + openim::log::info "OpenIM ${OPENIM_RPC_SERVICE_LISTARIES[$i]} config path: ${OPENIM_RPC_CONFIG}" + # Get the service and Prometheus ports. + OPENIM_RPC_SERVICE_PORTS=( $(openim::util::list-to-string ${OPENIM_RPC_PORT_LISTARIES[$i]}) ) + read -a OPENIM_RPC_SERVICE_PORTS_ARRAY <<< ${OPENIM_RPC_SERVICE_PORTS} + + OPENIM_RPC_PROM_PORTS=( $(openim::util::list-to-string ${OPENIM_RPC_PROM_PORT_LISTARIES[$i]}) ) + read -a OPENIM_RPC_PROM_PORTS_ARRAY <<< ${OPENIM_RPC_PROM_PORTS} + + for ((j = 0; j < ${#OPENIM_RPC_SERVICE_PORTS_ARRAY[@]}; j++)); do + openim::log::info "Starting ${OPENIM_RPC_SERVICE_LISTARIES[$i]} service, port: ${OPENIM_RPC_SERVICE_PORTS[j]}, prometheus port: ${OPENIM_RPC_PROM_PORTS[j]}, binary root: ${OPENIM_OUTPUT_HOSTBIN}/${OPENIM_RPC_SERVICE_LISTARIES[$i]}" + result=$(openim::rpc::start_service "${OPENIM_RPC_SERVICE_LISTARIES[$i]}" "${OPENIM_RPC_SERVICE_PORTS[j]}" "${OPENIM_RPC_PROM_PORTS[j]}") + if [[ $? -ne 0 ]]; then + openim::log::error "start ${SERVER_NAME} failed" + else + openim::log::info "$result" + fi + done + done + + + return 0 +} + +function openim::rpc::start_service() { + local binary_name="$1" + local service_port="$2" + local prometheus_port="$3" + + local cmd="${OPENIM_OUTPUT_HOSTBIN}/${binary_name} --port ${service_port} -c ${OPENIM_RPC_CONFIG}" + + if [ -n "${prometheus_port}" ]; then + printf "Specifying prometheus port: %s\n" "${prometheus_port}" + cmd="${cmd} --prometheus_port ${prometheus_port}" + fi + #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 & + return 0 +} + +###################################### Linux Systemd ###################################### +declare -A SYSTEM_FILE_PATHS +for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + SYSTEM_FILE_PATHS["$service"]="/etc/systemd/system/${service}.service" +done + +# Print the necessary information after installation +function openim::rpc::info() { + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + echo "${service} listen on: ${OPENIM_RPC_PORT_LISTARIES[@]}" + done +} + +# install openim-rpc +function openim::rpc::install() { + pushd "${OPENIM_ROOT}" + + # 1. Build openim-rpc + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + make build BINS=${service} + openim::common::sudo "cp -r ${OPENIM_OUTPUT_HOSTBIN}/${service} ${OPENIM_INSTALL_DIR}/${service}" + openim::log::status "${service} binary: ${OPENIM_INSTALL_DIR}/${service}/${service}" + done + + # 2. Generate and install the openim-rpc configuration file (config) + openim::log::status "openim-rpc config file: ${OPENIM_CONFIG_DIR}/config.yaml" + + # 3. Create and install the systemd unit files + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + echo ${LINUX_PASSWORD} | sudo -S bash -c \ + "SERVER_NAME=${service} ./scripts/genconfig.sh ${ENV_FILE} deployments/templates/openim.service > ${SYSTEM_FILE_PATHS[$service]}" + openim::log::status "${service} systemd file: ${SYSTEM_FILE_PATHS[$service]}" + done + + # 4. Start the openim-rpc services + openim::common::sudo "systemctl daemon-reload" + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + openim::common::sudo "systemctl restart ${service}" + openim::common::sudo "systemctl enable ${service}" + done + openim::rpc::status || return 1 + openim::rpc::info + + openim::log::info "install openim-rpc successfully" + popd +} + +# Unload +function openim::rpc::uninstall() { + set +o errexit + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + openim::common::sudo "systemctl stop ${service}" + openim::common::sudo "systemctl disable ${service}" + openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${service}" + openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${service}.yaml" + openim::common::sudo "rm -f ${SYSTEM_FILE_PATHS[$service]}" + done + + openim::log::info "uninstall openim-rpc successfully" +} + +# Status Check +function openim::rpc::status() { + for service in "${OPENIM_RPC_SERVICE_LISTARIES[@]}"; do + # Check the running status of the ${service}. If active (running) is displayed, the ${service} is started successfully. + systemctl status ${service}|grep -q 'active' || { + openim::log::error "${service} failed to start, maybe not installed properly" + return 1 + } + + # The listening port is hardcoded in the configuration file + if echo | telnet ${OPENIM_MSGGATEWAY_HOST} ${OPENIM_RPC_PORT_LISTARIES[@]} 2>&1|grep refused &>/dev/null;then + openim::log::error "cannot access health check port, ${service} maybe not startup" + return 1 + fi + done +} + +if [[ "$*" =~ openim::rpc:: ]];then + eval $* +fi diff --git a/scripts-new/install/openim-tools.sh b/scripts-new/install/openim-tools.sh new file mode 100644 index 000000000..57de9772d --- /dev/null +++ b/scripts-new/install/openim-tools.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM Tools Control Script +# +# Description: +# This script is responsible for managing the lifecycle of OpenIM tools, which include starting, stopping, +# and handling pre and post operations. It's designed to be modular and extensible, ensuring that the +# individual operations can be managed separately, and integrated seamlessly with Linux systemd. +# +# Features: +# 1. Robust error handling using Bash built-ins like 'errexit', 'nounset', and 'pipefail'. +# 2. The capability to source common utility functions and configurations to ensure uniform environmental settings. +# 3. Comprehensive logging functionalities, providing a detailed understanding of operational processes. +# 4. Provision for declaring and managing a set of OpenIM tools, each associated with its unique name and corresponding ports. +# 5. The ability to define and associate Prometheus ports for service monitoring purposes. +# 6. Functionalities to start each OpenIM tool, along with its designated ports, in a sequence. +# +# Usage: +# 1. Direct Script Execution: +# This initiates all the OpenIM tools declared under the function openim::tools::service_name. +# Example: ./openim-tools.sh openim::tools::start +# 2. Controlling through Functions for systemctl operations: +# Specific operations like installation, uninstallation, and status check can be executed by passing the respective function name as an argument to the script. +# Example: ./openim-tools.sh openim::tools::install +# + + +OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +[[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh + +SERVER_NAME="openim-tools" + +openim::tools::start_name() { + local targets=( + imctl + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_TOOLS_NAME_TARGETS <<< "$(openim::tools::start_name)" +readonly OPENIM_TOOLS_NAME_TARGETS +readonly OPENIM_TOOLS_NAME_LISTARIES=("${OPENIM_TOOLS_NAME_TARGETS[@]##*/}") + +openim::tools::pre_start_name() { + local targets=( + ncpu + component + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_TOOLS_PRE_START_NAME_TARGETS <<< "$(openim::tools::pre_start_name)" +readonly OPENIM_TOOLS_PRE_START_NAME_TARGETS +readonly OPENIM_TOOLS_PRE_START_NAME_LISTARIES=("${OPENIM_TOOLS_PRE_START_NAME_TARGETS[@]##*/}") + +openim::tools::post_start_name() { + local targets=( + infra + versionchecker + ) + echo "${targets[@]}" +} +IFS=" " read -ra OPENIM_TOOLS_POST_START_NAME_TARGETS <<< "$(openim::tools::post_start_name)" +readonly OPENIM_TOOLS_POST_START_NAME_TARGETS +readonly OPENIM_TOOLS_POST_START_NAME_LISTARIES=("${OPENIM_TOOLS_POST_START_NAME_TARGETS[@]##*/}") + +function openim::tools::start_service() { + local binary_name="$1" + local config="$2" + local service_port="$3" + local prometheus_port="$4" + + local cmd="${OPENIM_OUTPUT_HOSTBIN_TOOLS}/${binary_name}" + #openim::log::info "Starting PATH: ${OPENIM_OUTPUT_HOSTBIN_TOOLS}/${binary_name}..." + + if [ -n "${config}" ]; then + # printf "Specifying config: %s\n" "${config}" + cmd="${cmd} -c ${config}/config.yaml" + fi + + if [ -n "${service_port}" ]; then + #printf "Specifying service port: %s\n" "${service_port}" + cmd="${cmd} --port ${service_port}" + fi + + if [ -n "${prometheus_port}" ]; then + #printf "Specifying prometheus port: %s\n" "${prometheus_port}" + cmd="${cmd} --prometheus_port ${prometheus_port}" + fi + #openim::log::status "Starting binary ${binary_name}..." + + + # openim::log::info "cmd: $cmd" + ${cmd} + + + local status=$? + if [ $status -eq 0 ]; then + openim::log::colorless "Service ${binary_name} started successfully." + return 0 + else + openim::log::error "Failed to start service ${binary_name}." + return 1 + fi +} + +function openim::tools::start() { + openim::log::info "Starting OpenIM Tools..." + for tool in "${OPENIM_TOOLS_NAME_LISTARIES[@]}"; do + openim::log::colorless "Starting tool ${tool}..." + # openim::tools::start_service ${tool} + #sleep 0.2 + done +} + + +function openim::tools::pre-start() { + #openim::log::info "Preparing to start OpenIM Tools..." + for tool in "${OPENIM_TOOLS_PRE_START_NAME_LISTARIES[@]}"; do + openim::log::colorless "Starting tool: ${tool}" + if ! openim::tools::start_service ${tool} ${OPNEIM_CONFIG}; then + openim::log::error "Failed to start ${tool}, aborting..." + return 1 + fi + done + #openim::log::info "All tools started successfully." +} + +function openim::tools::post-start() { + #openim::log::info "Post-start actions for OpenIM Tools..." + for tool in "${OPENIM_TOOLS_POST_START_NAME_LISTARIES[@]}"; do + openim::log::colorless "Starting tool: ${tool}" + openim::tools::start_service ${tool} + done +} + +function openim::tools::stop() { + openim::log::info "Stopping OpenIM Tools..." + for tool in "${OPENIM_TOOLS_NAME_LISTARIES[@]}"; do + openim::log::info "Stopping ${tool}..." + # Similarly, place the actual command to stop the tool here. + echo "Stopping service for ${tool}" + sleep 0.2 + done +} + +function openim::tools::pre-stop() { + openim::log::info "Preparing to stop OpenIM Tools..." + for tool in "${OPENIM_TOOLS_PRE_START_NAME_LISTARIES[@]}"; do + openim::log::info "Setting up pre-stop for ${tool}..." + echo "Pre-stop actions for ${tool}" + sleep 0.2 + done +} + +function openim::tools::post-stop() { + openim::log::info "Post-stop actions for OpenIM Tools..." + for tool in "${OPENIM_TOOLS_POST_START_NAME_LISTARIES[@]}"; do + openim::log::info "Executing post-stop for ${tool}..." + echo "Post-stop cleanup for ${tool}" + sleep 0.2 + done +} + +if [[ "$*" =~ openim::tools:: ]];then + eval $* +fi diff --git a/scripts-new/install/test.sh b/scripts-new/install/test.sh new file mode 100644 index 000000000..51d541ece --- /dev/null +++ b/scripts-new/install/test.sh @@ -0,0 +1,1466 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenIM RPC Service Test Control Script +# +# This control script is designed to conduct various tests on the OpenIM RPC services. +# It includes functions to perform smoke tests, API tests, and comprehensive service tests. +# The script is intended to be used in a Linux environment with appropriate permissions and +# environmental variables set. +# +# It provides robust error handling and logging to facilitate debugging and service monitoring. +# Functions within the script can be called directly or passed as arguments to perform +# systematic testing, ensuring the integrity of the RPC services. +# +# Test Functions: +# - openim::test::smoke: Runs basic tests to ensure the fundamental functionality of the service. +# - openim::test::api: Executes a series of API tests covering authentication, user, friend, +# group, and message functionalities. +# - openim::test::test: Performs a complete test suite, invoking utility checks and all defined +# test cases, and reports on their success. +# + +# The root of the build/dist directory +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. +[[ -z ${COMMON_SOURCED} ]] && source ${OPENIM_ROOT}/scripts/install/common.sh + +# API Server API Address:Port +INSECURE_OPENIMAPI="http://${OPENIM_API_HOST}:${API_OPENIM_PORT}" +INSECURE_OPENIMAUTO=${OPENIM_RPC_AUTH_HOST}:${OPENIM_AUTH_PORT} +CCURL="curl -f -s -XPOST" # Create +UCURL="curl -f -s -XPUT" # Update +RCURL="curl -f -s -XGET" # Retrieve +DCURL="curl -f -s -XDELETE" # Delete + +openim::test::check_error() { + local response=$1 + local err_code=$(echo "$response" | jq '.errCode') + openim::log::status "Response from user registration: $response" + if [[ "$err_code" != "0" ]]; then + openim::log::error_exit "Error occurred: $response, You can read the error code in the API documentation https://docs.openim.io/restapi/errcode" + else + openim::log::success "Operation was successful." + fi +} + +# The `openim::test::auth` function serves as a test suite for authentication-related operations. +function openim::test::auth() { + # 1. Retrieve and set the authentication token. + openim::test::get_token + + # 2. Force logout the test user from a specific platform. + openim::test::force_logout + + # Log the completion of the auth test suite. + openim::log::success "Auth test suite completed successfully." +} + +#################################### Auth Module #################################### + +# Define a function to get a token for a specific user +openim::test::get_token() { + local user_id="${1:-imAdmin}" # Default user ID if not provided + token_response=$( + ${CCURL} "${OperationID}" "${Header}" ${INSECURE_OPENIMAPI}/auth/user_token \ + -d'{"secret": "'"$SECRET"'","platformID": 1,"userID": "'$user_id'"}' + ) + token=$(echo $token_response | grep -Po 'token[" :]+\K[^"]+') + echo "$token" +} + +Header="-HContent-Type: application/json" +OperationID="-HoperationID: 1646445464564" +Token="-Htoken: $(openim::test::get_token)" + +# Forces a user to log out from the specified platform by user ID. +openim::test::force_logout() { + local request_body=$( + cat </dev/null || { + openim::log::usage "chat must be in your PATH" + openim::log::info "You can use 'scripts/install-chat.sh' to install a copy in third_party/." + exit 1 + } + + # validate chat port is free + local port_check_command + if command -v ss &> /dev/null && ss -Version | grep 'iproute2' &> /dev/null; then + port_check_command="ss" + elif command -v netstat &>/dev/null; then + port_check_command="netstat" + else + openim::log::usage "unable to identify if chat is bound to port ${CHAT_PORT}. unable to find ss or netstat utilities." + exit 1 + fi + if ${port_check_command} -nat | grep "LISTEN" | grep "[\.:]${CHAT_PORT:?}" >/dev/null 2>&1; then + openim::log::usage "unable to start chat as port ${CHAT_PORT} is in use. please stop the process listening on this port and retry." + openim::log::usage "$(${port_check_command} -nat | grep "LISTEN" | grep "[\.:]${CHAT_PORT:?}")" + exit 1 + fi + + # need set the env of "CHAT_UNSUPPORTED_ARCH" on unstable arch. + arch=$(uname -m) + if [[ $arch =~ arm* ]]; then + export CHAT_UNSUPPORTED_ARCH=arm + fi + # validate installed version is at least equal to minimum + version=$(chat --version | grep Version | head -n 1 | cut -d " " -f 3) + if [[ $(openim::chat::version "${CHAT_VERSION}") -gt $(openim::chat::version "${version}") ]]; then + export PATH="${OPENIM_ROOT}"/third_party/chat:${PATH} + hash chat + echo "${PATH}" + version=$(chat --version | grep Version | head -n 1 | cut -d " " -f 3) + if [[ $(openim::chat::version "${CHAT_VERSION}") -gt $(openim::chat::version "${version}") ]]; then + openim::log::usage "chat version ${CHAT_VERSION} or greater required." + openim::log::info "You can use 'scripts/install-chat.sh' to install a copy in third_party/." + exit 1 + fi + fi +} + +openim::chat::version() { + printf '%s\n' "${@}" | awk -F . '{ printf("%d%03d%03d\n", $1, $2, $3) }' +} + +openim::chat::start() { + # validate before running + openim::chat::validate + + # Start chat + CHAT_DIR=${CHAT_DIR:-$(mktemp -d 2>/dev/null || mktemp -d -t test-chat.XXXXXX)} + if [[ -d "${ARTIFACTS:-}" ]]; then + CHAT_LOGFILE="${ARTIFACTS}/chat.$(uname -n).$(id -un).log.DEBUG.$(date +%Y%m%d-%H%M%S).$$" + else + CHAT_LOGFILE=${CHAT_LOGFILE:-"/dev/null"} + fi + openim::log::info "chat --advertise-client-urls ${OPENIM_INTEGRATION_CHAT_URL} --data-dir ${CHAT_DIR} --listen-client-urls http://${CHAT_HOST}:${CHAT_PORT} --log-level=${CHAT_LOGLEVEL} 2> \"${CHAT_LOGFILE}\" >/dev/null" + chat --advertise-client-urls "${OPENIM_INTEGRATION_CHAT_URL}" --data-dir "${CHAT_DIR}" --listen-client-urls "${OPENIM_INTEGRATION_CHAT_URL}" --log-level="${CHAT_LOGLEVEL}" 2> "${CHAT_LOGFILE}" >/dev/null & + CHAT_PID=$! + + echo "Waiting for chat to come up." + openim::util::wait_for_url "${OPENIM_INTEGRATION_CHAT_URL}/health" "chat: " 0.25 80 + curl -fs -X POST "${OPENIM_INTEGRATION_CHAT_URL}/v3/kv/put" -d '{"key": "X3Rlc3Q=", "value": ""}' +} + +openim::chat::start_scraping() { + if [[ -d "${ARTIFACTS:-}" ]]; then + CHAT_SCRAPE_DIR="${ARTIFACTS}/chat-scrapes" + else + CHAT_SCRAPE_DIR=$(mktemp -d -t test.XXXXXX)/chat-scrapes + fi + openim::log::info "Periodically scraping chat to ${CHAT_SCRAPE_DIR} ." + mkdir -p "${CHAT_SCRAPE_DIR}" + ( + while sleep 30; do + openim::chat::scrape + done + ) & + CHAT_SCRAPE_PID=$! +} + +openim::chat::scrape() { + curl -s -S "${OPENIM_INTEGRATION_CHAT_URL}/metrics" > "${CHAT_SCRAPE_DIR}/next" && mv "${CHAT_SCRAPE_DIR}/next" "${CHAT_SCRAPE_DIR}/$(date +%s).scrape" +} + +openim::chat::stop() { + if [[ -n "${CHAT_SCRAPE_PID:-}" ]] && [[ -n "${CHAT_SCRAPE_DIR:-}" ]] ; then + kill "${CHAT_SCRAPE_PID}" &>/dev/null || : + wait "${CHAT_SCRAPE_PID}" &>/dev/null || : + openim::chat::scrape || : + ( + # shellcheck disable=SC2015 + cd "${CHAT_SCRAPE_DIR}"/.. && \ + tar czf chat-scrapes.tgz chat-scrapes && \ + rm -rf chat-scrapes || : + ) + fi + if [[ -n "${CHAT_PID-}" ]]; then + kill "${CHAT_PID}" &>/dev/null || : + wait "${CHAT_PID}" &>/dev/null || : + fi +} + +openim::chat::clean_chat_dir() { + if [[ -n "${CHAT_DIR-}" ]]; then + rm -rf "${CHAT_DIR}" + fi +} + +openim::chat::cleanup() { + openim::chat::stop + openim::chat::clean_chat_dir +} + +openim::chat::install() { + ( + local os + local arch + + os=$(openim::util::host_os) + arch=$(openim::util::host_arch) + + cd "${OPENIM_ROOT}/third_party" || return 1 + if [[ $(readlink chat) == chat-v${CHAT_VERSION}-${os}-* ]]; then + openim::log::info "chat v${CHAT_VERSION} already installed. To use:" + openim::log::info "export PATH=\"$(pwd)/chat:\${PATH}\"" + return #already installed + fi + + if [[ ${os} == "darwin" ]]; then + download_file="chat-v${CHAT_VERSION}-${os}-${arch}.zip" + url="https://github.com/chat-io/chat/releases/download/v${CHAT_VERSION}/${download_file}" + openim::util::download_file "${url}" "${download_file}" + unzip -o "${download_file}" + ln -fns "chat-v${CHAT_VERSION}-${os}-${arch}" chat + rm "${download_file}" + elif [[ ${os} == "linux" ]]; then + url="https://github.com/coreos/chat/releases/download/v${CHAT_VERSION}/chat-v${CHAT_VERSION}-${os}-${arch}.tar.gz" + download_file="chat-v${CHAT_VERSION}-${os}-${arch}.tar.gz" + openim::util::download_file "${url}" "${download_file}" + tar xzf "${download_file}" + ln -fns "chat-v${CHAT_VERSION}-${os}-${arch}" chat + rm "${download_file}" + else + openim::log::info "${os} is NOT supported." + fi + openim::log::info "chat v${CHAT_VERSION} installed. To use:" + openim::log::info "export PATH=\"$(pwd)/chat:\${PATH}\"" + ) +} \ No newline at end of file diff --git a/scripts-new/lib/color.sh b/scripts-new/lib/color.sh new file mode 100644 index 000000000..744fccf5a --- /dev/null +++ b/scripts-new/lib/color.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# this script is used to install the dependencies of the project +# +# Usage: `scripts/color.sh`. +################################################################################ + +# shellcheck disable=SC2034 +if [ -z "${COLOR_OPEN+x}" ]; then + COLOR_OPEN=1 +fi + +# Function for colored echo +openim::color::echo() { + COLOR=$1 + [ $COLOR_OPEN -eq 1 ] && echo -e "${COLOR} $(date '+%Y-%m-%d %H:%M:%S') $@ ${COLOR_SUFFIX}" + shift +} + +# Define color variables +# --- Feature --- +COLOR_NORMAL='\033[0m';COLOR_BOLD='\033[1m';COLOR_DIM='\033[2m';COLOR_UNDER='\033[4m'; +COLOR_ITALIC='\033[3m';COLOR_NOITALIC='\033[23m';COLOR_BLINK='\033[5m'; +COLOR_REVERSE='\033[7m';COLOR_CONCEAL='\033[8m';COLOR_NOBOLD='\033[22m'; +COLOR_NOUNDER='\033[24m';COLOR_NOBLINK='\033[25m'; + +# --- Front color --- +COLOR_BLACK='\033[30m'; +COLOR_RED='\033[31m'; +COLOR_GREEN='\033[32m'; +COLOR_YELLOW='\033[33m'; +COLOR_BLUE='\033[34m'; +COLOR_MAGENTA='\033[35m'; +COLOR_CYAN='\033[36m'; +COLOR_WHITE='\033[37m'; + +# --- background color --- +COLOR_BBLACK='\033[40m';COLOR_BRED='\033[41m'; +COLOR_BGREEN='\033[42m';COLOR_BYELLOW='\033[43m'; +COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m'; +COLOR_BCYAN='\033[46m';COLOR_BWHITE='\033[47m'; + +# --- Color definitions --- +# Color definitions +COLOR_SUFFIX="\033[0m" # End all colors and special effects +BLACK_PREFIX="\033[30m" # Black prefix +RED_PREFIX="\033[31m" # Red prefix +GREEN_PREFIX="\033[32m" # Green prefix +YELLOW_PREFIX="\033[33m" # Yellow prefix +BLUE_PREFIX="\033[34m" # Blue prefix +SKY_BLUE_PREFIX="\033[36m" # Sky blue prefix +WHITE_PREFIX="\033[37m" # White prefix +BOLD_PREFIX="\033[1m" # Bold prefix +UNDERLINE_PREFIX="\033[4m" # Underline prefix +ITALIC_PREFIX="\033[3m" # Italic prefix +CYAN_PREFIX="\033[0;36m" # Cyan prefix + +# Print colors you can use +openim::color::print_color() { + echo + echo -e ${bmagenta}--back-color:${normal} + echo "bblack; bgreen; bblue; bcyan; bred; byellow; bmagenta; bwhite" + echo + echo -e ${red}--font-color:${normal} + echo "black; red; green; yellow; blue; magenta; cyan; white" + echo + echo -e ${bold}--font:${normal} + echo "normal; italic; reverse; nounder; bold; noitalic; conceal; noblink; + dim; blink; nobold; under" + echo +} + +# test functions +openim::color::test() { + echo "Starting the color tests..." + + echo "Testing normal echo without color" + openim::color::echo $COLOR_NORMAL "This is a normal text" + + echo "Testing bold echo" + openim::color::echo $COLOR_BOLD "This is bold text" + + echo "Testing dim echo" + openim::color::echo $COLOR_DIM "This is dim text" + + echo "Testing underlined echo" + openim::color::echo $COLOR_UNDER "This is underlined text" + + echo "Testing italic echo" + openim::color::echo $COLOR_ITALIC "This is italic text" + + echo "Testing red color" + openim::color::echo $COLOR_RED "This is red text" + + echo "Testing green color" + openim::color::echo $COLOR_GREEN "This is green text" + + echo "Testing yellow color" + openim::color::echo $COLOR_YELLOW "This is yellow text" + + echo "Testing blue color" + openim::color::echo $COLOR_BLUE "This is blue text" + + echo "Testing magenta color" + openim::color::echo $COLOR_MAGENTA "This is magenta text" + + echo "Testing cyan color" + openim::color::echo $COLOR_CYAN "This is cyan text" + + echo "Testing black background" + openim::color::echo $COLOR_BBLACK "This is text with black background" + + echo "Testing red background" + openim::color::echo $COLOR_BRED "This is text with red background" + + echo "Testing green background" + openim::color::echo $COLOR_BGREEN "This is text with green background" + + echo "Testing blue background" + openim::color::echo $COLOR_BBLUE "This is text with blue background" + + echo "All tests completed!" +} + +# openim::color::test diff --git a/scripts-new/lib/golang.sh b/scripts-new/lib/golang.sh new file mode 100644 index 000000000..51db58d89 --- /dev/null +++ b/scripts-new/lib/golang.sh @@ -0,0 +1,384 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The golang package that we are building. +OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" +readonly OPENIM_GO_PACKAGE=github.com/openimsdk/open-im-server + +# The server platform we are building on. +readonly OPENIM_SUPPORTED_SERVER_PLATFORMS=( + linux/amd64 + linux/arm64 + linux/s390x + linux_mips64 + linux_mips64le + darwin_amd64 + windows_amd64 + linux_amd64 + linux_arm64 + linux_ppc64le +) + +# If we update this we should also update the set of platforms whose standard +readonly OPENIM_SUPPORTED_CLIENT_PLATFORMS=( + linux/amd64 + linux/arm64 + linux/s390x + linux/ppc64le + windows/amd64 +) + +# openim chat +readonly OPENIM_CHAT_SUPPORTED_PLATFORMS=( + linux/amd64 + linux/arm64 + linux/s390x + linux/ppc64le + windows/amd64 +) + +# Which platforms we should compile test targets for. +# Not all client platforms need these tests +readonly KUBE_SUPPORTED_TEST_PLATFORMS=( + linux/amd64 + linux/arm64 + linux/s390x + linux/ppc64le + darwin/amd64 + darwin/arm64 + windows/amd64 + windows/arm64 +) + +# The set of server targets that we are only building for Linux +# If you update this list, please also update build/BUILD. +# TODO: Label +openim::golang::server_targets() { + local targets=( + openim-api + openim-cmdutils + openim-crontask + openim-msggateway + openim-msgtransfer + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user + ) + echo "${targets[@]}" +} + +openim::golang::server_targets_no_transfer() { + local targets=( + openim-api + openim-crontask + openim-msggateway + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user + ) + echo "${targets[@]}" +} + +openim::golang::server_targets_no_cmdutils() { + local targets=( + openim-api + openim-crontask + openim-msggateway + openim-msgtransfer + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user + ) + echo "${targets[@]}" +} + + +IFS=" " read -ra OPENIM_SERVER_TARGETS_NO_CMDUTILS <<< "$(openim::golang::server_targets_no_cmdutils)" +readonly OPENIM_SERVER_TARGETS_NO_CMDUTILS +readonly OPENIM_SERVER_BINARIES_NO_CMDUTILS=("${OPENIM_SERVER_TARGETS_NO_CMDUTILS[@]##*/}") + + + + + +IFS=" " read -ra OPENIM_SERVER_TARGETS_NO_TRANSFER <<< "$(openim::golang::server_targets_no_transfer)" +readonly OPENIM_SERVER_TARGETS_NO_TRANSFER +readonly OPENIM_SERVER_BINARIES_NO_TRANSFER=("${OPENIM_SERVER_TARGETS_NO_TRANSFER[@]##*/}") + + + + +IFS=" " read -ra OPENIM_SERVER_TARGETS <<< "$(openim::golang::server_targets)" +readonly OPENIM_SERVER_TARGETS +readonly OPENIM_SERVER_BINARIES=("${OPENIM_SERVER_TARGETS[@]##*/}") + +# TODO: Label +START_SCRIPTS_PATH="${OPENIM_ROOT}/scripts/install/" +openim::golang::start_script_list() { + local targets=( + openim-api.sh + openim-rpc.sh + openim-push.sh + openim-msgtransfer.sh + openim-msggateway.sh + openim-crontask.sh + #openim-tools.sh + ) + local result=() + for target in "${targets[@]}"; do + result+=("${START_SCRIPTS_PATH}${target}") + done + + echo "${result[@]}" +} + +# Populate the OPENIM_SERVER_SCRIPT_START_LIST with the full path names of the scripts. +IFS=" " read -ra OPENIM_SERVER_SCRIPT_START_LIST <<< "$(openim::golang::start_script_list)" +readonly OPENIM_SERVER_SCRIPT_START_LIST + +# Extract just the script names from the full paths. +readonly OPENIM_SERVER_SCRIPTARIES=("${OPENIM_SERVER_SCRIPT_START_LIST[@]##*/}") + +openim::golang::check_openim_binaries() { + local missing_binaries=() + for binary in "${OPENIM_SERVER_BINARIES[@]}"; do + if [[ ! -x "${OPENIM_OUTPUT_HOSTBIN}/${binary}" ]]; then + missing_binaries+=("${binary}") + fi + done + + if [[ ${#missing_binaries[@]} -ne 0 ]]; then + echo "The following binaries were not found in ${OPENIM_OUTPUT_HOSTBIN}:" + for missing in "${missing_binaries[@]}"; do + echo " - ${missing}" + done + return 1 + else + echo -e "All binaries have been installed in: \n${OPENIM_OUTPUT_HOSTBIN}\n${OPENIM_OUTPUT_HOSTBIN_TOOLS}" + return 0 + fi +} + +openim::golang::tools_targets() { + local targets=( + yamlfmt + changelog + infra + ncpu + ) + echo "${targets[@]}" +} + +IFS=" " read -ra OPENIM_TOOLS_TARGETS <<< "$(openim::golang::tools_targets)" +readonly OPENIM_TOOLS_TARGETS +readonly OPENIM_TOOLS_BINARIES=("${OPENIM_TOOLS_TARGETS[@]##*/}") + +# The set of server targets we build docker images for +openim::golang::server_image_targets() { + # NOTE: this contains cmd targets for openim::build::get_docker_wrapped_binaries + local targets=( + cmd/openim-api + cmd/openim-cmdutils + cmd/openim-crontask + cmd/openim-msggateway + cmd/openim-msgtransfer + cmd/openim-push + cmd/openim-rpc-auth + cmd/openim-rpc-conversation + cmd/openim-rpc-friend + cmd/openim-rpc-group + cmd/openim-rpc-msg + cmd/openim-rpc-third + cmd/openim-rpc-user + ) + echo "${targets[@]}" +} + +IFS=" " read -ra OPENIM_SERVER_IMAGE_TARGETS <<< "$(openim::golang::server_image_targets)" +readonly OPENIM_SERVER_IMAGE_TARGETS +readonly OPENIM_SERVER_IMAGE_BINARIES=("${OPENIM_SERVER_IMAGE_TARGETS[@]##*/}") + +# shellcheck disable=SC2034 # Variables sourced in other scripts. + +# ------------ +# NOTE: All functions that return lists should use newlines. +# bash functions can't return arrays, and spaces are tricky, so newline +# separators are the preferred pattern. +# To transform a string of newline-separated items to an array, use openim::util::read-array: +# openim::util::read-array FOO < <(openim::golang::dups a b c a) +# +# ALWAYS remember to quote your subshells. Not doing so will break in +# bash 4.3, and potentially cause other issues. +# ------------ + +# Returns a sorted newline-separated list containing only duplicated items. +openim::golang::dups() { + # We use printf to insert newlines, which are required by sort. + printf "%s\n" "$@" | sort | uniq -d +} + +# echo "aa: $OPENIM_SERVER_IMAGE_TARGETS" +# echo "aa: $OPENIM_SERVER_IMAGE_BINARIES" + +openim::golang::dups $OPENIM_SERVER_IMAGE_TARGETS +openim::golang::dups $OPENIM_SERVER_IMAGE_BINARIES + +# Returns a sorted newline-separated list with duplicated items removed. +openim::golang::dedup() { + # We use printf to insert newlines, which are required by sort. + printf "%s\n" "$@" | sort -u +} + +# openim::golang::dedup $OPENIM_SERVER_IMAGE_TARGETS +# openim::golang::dedup $OPENIM_SERVER_IMAGE_BINARIES + +# Depends on values of user-facing OPENIM_BUILD_PLATFORMS, OPENIM_FASTBUILD, +# and OPENIM_BUILDER_OS. +# Configures OPENIM_SERVER_PLATFORMS and OPENIM_CLIENT_PLATFORMS, then sets them +# to readonly. +# The configured vars will only contain platforms allowed by the +# OPENIM_SUPPORTED* vars at the top of this file. +declare -a OPENIM_SERVER_PLATFORMS +declare -a OPENIM_CLIENT_PLATFORMS +openim::golang::setup_platforms() { + if [[ -n "${OPENIM_BUILD_PLATFORMS:-}" ]]; then + # OPENIM_BUILD_PLATFORMS needs to be read into an array before the next + # step, or quoting treats it all as one element. + local -a platforms + IFS=" " read -ra platforms <<< "${OPENIM_BUILD_PLATFORMS}" + + # Deduplicate to ensure the intersection trick with openim::golang::dups + # is not defeated by duplicates in user input. + openim::util::read-array platforms < <(openim::golang::dedup "${platforms[@]}") + + # Use openim::golang::dups to restrict the builds to the platforms in + # OPENIM_SUPPORTED_*_PLATFORMS. Items should only appear at most once in each + # set, so if they appear twice after the merge they are in the intersection. + openim::util::read-array OPENIM_SERVER_PLATFORMS < <(openim::golang::dups \ + "${platforms[@]}" \ + "${OPENIM_SUPPORTED_SERVER_PLATFORMS[@]}" \ + ) + readonly OPENIM_SERVER_PLATFORMS + + openim::util::read-array OPENIM_CLIENT_PLATFORMS < <(openim::golang::dups \ + "${platforms[@]}" \ + "${OPENIM_SUPPORTED_CLIENT_PLATFORMS[@]}" \ + ) + readonly OPENIM_CLIENT_PLATFORMS + + elif [[ "${OPENIM_FASTBUILD:-}" == "true" ]]; then + OPENIM_SERVER_PLATFORMS=(linux/amd64) + readonly OPENIM_SERVER_PLATFORMS + OPENIM_CLIENT_PLATFORMS=(linux/amd64) + readonly OPENIM_CLIENT_PLATFORMS + else + OPENIM_SERVER_PLATFORMS=("${OPENIM_SUPPORTED_SERVER_PLATFORMS[@]}") + readonly OPENIM_SERVER_PLATFORMS + + OPENIM_CLIENT_PLATFORMS=("${OPENIM_SUPPORTED_CLIENT_PLATFORMS[@]}") + readonly OPENIM_CLIENT_PLATFORMS + fi +} + +openim::golang::setup_platforms + +# The set of client targets that we are building for all platforms +# If you update this list, please also update build/BUILD. +readonly OPENIM_CLIENT_TARGETS=( + changelog + component + imctl + infra + ncpu + versionchecker + yamlfmt +) +readonly OPENIM_CLIENT_BINARIES=("${OPENIM_CLIENT_TARGETS[@]##*/}") + +readonly OPENIM_ALL_TARGETS=( + "${OPENIM_SERVER_TARGETS[@]}" + "${OPENIM_CLIENT_TARGETS[@]}" +) +readonly OPENIM_ALL_BINARIES=("${OPENIM_ALL_TARGETS[@]##*/}") + +# Asks golang what it thinks the host platform is. The go tool chain does some +# slightly different things when the target platform matches the host platform. +openim::golang::host_platform() { + echo "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" +} + +# Ensure the go tool exists and is a viable version. +openim::golang::verify_go_version() { + if [[ -z "$(command -v go)" ]]; then + openim::log::usage_from_stdin < 0 to enable rsync +# compression for build container +OPENIM_RSYNC_COMPRESS="${KUBE_RSYNC_COMPRESS:-0}" + +# Set no_proxy for localhost if behind a proxy, otherwise, +# the connections to localhost in scripts will time out +export no_proxy="127.0.0.1,localhost${no_proxy:+,${no_proxy}}" + +# This is a symlink to binaries for "this platform", e.g. build tools. +export THIS_PLATFORM_BIN="${OPENIM_ROOT}/_output/bin/platforms" +export THIS_PLATFORM_BIN_TOOLS="${OPENIM_ROOT}/_output/bin/tools" + +. $(dirname ${BASH_SOURCE})/color.sh +. $(dirname ${BASH_SOURCE})/util.sh +. $(dirname ${BASH_SOURCE})/logging.sh + +openim::log::install_errexit +openim::util::ensure-bash-version + +. $(dirname ${BASH_SOURCE})/version.sh +. $(dirname ${BASH_SOURCE})/golang.sh +. $(dirname ${BASH_SOURCE})/chat.sh + +OPENIM_OUTPUT_HOSTBIN="${OPENIM_OUTPUT_BINPATH}/$(openim::util::host_platform)" +export OPENIM_OUTPUT_HOSTBIN +OPENIM_OUTPUT_HOSTBIN_TOOLS="${OPENIM_OUTPUT_BINTOOLPATH}/$(openim::util::host_platform)" +export OPENIM_OUTPUT_HOSTBIN_TOOLS + +export OPENIM_NONSERVER_GROUP_VERSIONS + + +# This emulates "readlink -f" which is not available on MacOS X. +# Test: +# T=/tmp/$$.$RANDOM +# mkdir $T +# touch $T/file +# mkdir $T/dir +# ln -s $T/file $T/linkfile +# ln -s $T/dir $T/linkdir +# function testone() { +# X=$(readlink -f $1 2>&1) +# Y=$(kube::readlinkdashf $1 2>&1) +# if [ "$X" != "$Y" ]; then +# echo readlinkdashf $1: expected "$X", got "$Y" +# fi +# } +# testone / +# testone /tmp +# testone $T +# testone $T/file +# testone $T/dir +# testone $T/linkfile +# testone $T/linkdir +# testone $T/nonexistant +# testone $T/linkdir/file +# testone $T/linkdir/dir +# testone $T/linkdir/linkfile +# testone $T/linkdir/linkdir +function openim::readlinkdashf { + # run in a subshell for simpler 'cd' + ( + if [[ -d "${1}" ]]; then # This also catch symlinks to dirs. + cd "${1}" + pwd -P + else + cd "$(dirname "${1}")" + local f + f=$(basename "${1}") + if [[ -L "${f}" ]]; then + readlink "${f}" + else + echo "$(pwd -P)/${f}" + fi + fi + ) +} + +# This emulates "readlink -f" which is not available on MacOS X. +# Test: +# T=/tmp/$$.$RANDOM +# mkdir $T +# touch $T/file +# mkdir $T/dir +# ln -s $T/file $T/linkfile +# ln -s $T/dir $T/linkdir +# function testone() { +# X=$(readlink -f $1 2>&1) +# Y=$(kube::readlinkdashf $1 2>&1) +# if [ "$X" != "$Y" ]; then +# echo readlinkdashf $1: expected "$X", got "$Y" +# fi +# } +# testone / +# testone /tmp +# testone $T +# testone $T/file +# testone $T/dir +# testone $T/linkfile +# testone $T/linkdir +# testone $T/nonexistant +# testone $T/linkdir/file +# testone $T/linkdir/dir +# testone $T/linkdir/linkfile +# testone $T/linkdir/linkdir +function openim::readlinkdashf { + # run in a subshell for simpler 'cd' + ( + if [[ -d "${1}" ]]; then # This also catch symlinks to dirs. + cd "${1}" + pwd -P + else + cd "$(dirname "${1}")" + local f + f=$(basename "${1}") + if [[ -L "${f}" ]]; then + readlink "${f}" + else + echo "$(pwd -P)/${f}" + fi + fi + ) +} + +# This emulates "realpath" which is not available on MacOS X +# Test: +# T=/tmp/$$.$RANDOM +# mkdir $T +# touch $T/file +# mkdir $T/dir +# ln -s $T/file $T/linkfile +# ln -s $T/dir $T/linkdir +# function testone() { +# X=$(realpath $1 2>&1) +# Y=$(kube::realpath $1 2>&1) +# if [ "$X" != "$Y" ]; then +# echo realpath $1: expected "$X", got "$Y" +# fi +# } +# testone / +# testone /tmp +# testone $T +# testone $T/file +# testone $T/dir +# testone $T/linkfile +# testone $T/linkdir +# testone $T/nonexistant +# testone $T/linkdir/file +# testone $T/linkdir/dir +# testone $T/linkdir/linkfile +# testone $T/linkdir/linkdir +openim::realpath() { + if [[ ! -e "${1}" ]]; then + echo "${1}: No such file or directory" >&2 + return 1 + fi + openim::readlinkdashf "${1}" +} + +# Marker function to indicate init.sh has been fully sourced +openim::init::loaded() { + return 0 +} \ No newline at end of file diff --git a/scripts-new/lib/logging.sh b/scripts-new/lib/logging.sh new file mode 100644 index 000000000..0d15463eb --- /dev/null +++ b/scripts-new/lib/logging.sh @@ -0,0 +1,271 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Controls verbosity of the script output and logging. +OPENIM_VERBOSE="${OPENIM_VERBOSE:-5}" + +# Enable logging by default. Set to false to disable. +ENABLE_LOGGING="${ENABLE_LOGGING:-true}" + +# If OPENIM_OUTPUT is not set, set it to the default value +if [ -z "${OPENIM_OUTPUT+x}" ]; then + OPENIM_OUTPUT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../_output" && pwd -P)" +fi + +# Set the log file path +LOG_FILE="${OPENIM_OUTPUT}/logs/openim-$(date '+%Y%m%d').log" +STDERR_LOG_FILE="${OPENIM_OUTPUT}/logs/openim-error-$(date '+%Y%m%d').log" +TMP_LOG_FILE="${OPENIM_OUTPUT}/logs/openim-tmp-$(date '+%Y%m%d').log" +DOCKER_LOG_FILE="${OPENIM_OUTPUT}/logs/openim-docker.log" +if [[ ! -d "${OPENIM_OUTPUT}/logs" ]]; then + mkdir -p "${OPENIM_OUTPUT}/logs" + touch "$LOG_FILE" + touch "$STDERR_LOG_FILE" + touch "$TMP_LOG_FILE" +fi + +if [[ ! -f "$DOCKER_LOG_FILE" ]]; then + touch "$DOCKER_LOG_FILE" +fi + + + +# Define the logging function +function echo_log() { + if $ENABLE_LOGGING; then + echo -e "$@" | tee -a "${LOG_FILE}" + else + echo -e "$@" + fi +} + +# MAX_LOG_SIZE=10485760 # 10MB + +# Clear logs from 5 days ago +# find $OPENIM_OUTPUT_LOGS -type f -name "*.log" -mtime +5 -exec rm -f {} \; + +# Handler for when we exit automatically on an error. +# Borrowed from https://gist.github.com/ahendrix/7030300 +openim::log::errexit() { + local err="${PIPESTATUS[*]}" + + # If the shell we are in doesn't have errexit set (common in subshells) then + # don't dump stacks. + set +o | grep -qe "-o errexit" || return + + set +o xtrace + local code="${1:-1}" + # Print out the stack trace described by $function_stack + if [ ${#FUNCNAME[@]} -gt 2 ] + then + openim::log::error "Call tree:" + for ((i=1;i<${#FUNCNAME[@]}-1;i++)) + do + openim::log::error " ${i}: ${BASH_SOURCE[${i}+1]}:${BASH_LINENO[${i}]} ${FUNCNAME[${i}]}(...)" + done + fi + openim::log::error_exit "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status ${err}" "${1:-1}" 1 +} + +openim::log::install_errexit() { + # trap ERR to provide an error handler whenever a command exits nonzero this + # is a more verbose version of + # trap 'openim::log::errexit' ERR + + # setting errtrace allows our ERR trap handler to be propagated to functions, + # expansions and subshells + #set -o errtrace + return 0 +} + +# Print out the stack trace +# +# Args: +# $1 The number of stack frames to skip when printing. +openim::log::stack() { + local stack_skip=${1:-0} + stack_skip=$((stack_skip + 1)) + if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then + echo_log "Call stack:" >&2 + local i + for ((i=1 ; i <= ${#FUNCNAME[@]} - stack_skip ; i++)) + do + local frame_no=$((i - 1 + stack_skip)) + local source_file=${BASH_SOURCE[${frame_no}]} + local source_lineno=${BASH_LINENO[$((frame_no - 1))]} + local funcname=${FUNCNAME[${frame_no}]} + echo_log " ${i}: ${source_file}:${source_lineno} ${funcname}(...)" >&2 + done + fi +} + +# Log an error and exit. +# Args: +# $1 Message to log with the error +# $2 The error code to return +# $3 The number of stack frames to skip when printing. +openim::log::error_exit() { + local message="${1:-}" + local code="${2:-1}" + local stack_skip="${3:-0}" + stack_skip=$((stack_skip + 1)) + + if [[ ${OPENIM_VERBOSE} -ge 4 ]]; then + local source_file=${BASH_SOURCE[${stack_skip}]} + local source_line=${BASH_LINENO[$((stack_skip - 1))]} + echo_log -e "${COLOR_RED}!!! Error in ${source_file}:${source_line} ${COLOR_SUFFIX}" >&2 + [[ -z ${1-} ]] || { + echo_log " ${1}" >&2 + } + + openim::log::stack ${stack_skip} + + echo_log "Exiting with status ${code}" >&2 + fi + + exit "${code}" +} + +# Log an error but keep going. Don't dump the stack or exit. +openim::log::error() { + # Define red color + red='\033[0;31m' + # No color (reset) + nc='\033[0m' # No Color + + timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]") + # Apply red color for error message + echo_log "${red}!!! ${timestamp} ${1-}${nc}" >&2 + shift + for message; do + # Apply red color for subsequent lines of the error message + echo_log "${red} ${message}${nc}" >&2 + done +} + + +# Print an usage message to stderr. The arguments are printed directly. +openim::log::usage() { + echo_log >&2 + local message + for message; do + echo_log "${message}" >&2 + done + echo_log >&2 +} + +openim::log::usage_from_stdin() { + local messages=() + while read -r line; do + messages+=("${line}") + done + + openim::log::usage "${messages[@]}" +} + +# Print out some info that isn't a top level status line +openim::log::info() { + local V="${V:-0}" + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then + return + fi + + for message; do + echo_log "${message}" + done +} + +# Just like openim::log::info, but no \n, so you can make a progress bar +openim::log::progress() { + for message; do + echo_log -e -n "${message}" + done +} + +# Print out some info that isn't a top level status line +openim::log::info_from_stdin() { + local messages=() + while read -r line; do + messages+=("${line}") + done + + openim::log::info "${messages[@]}" +} + +# Print a status line. Formatted to show up in a stream of output. +openim::log::status() { + local V="${V:-0}" + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then + return + fi + + local COLOR_BLUE="\033[0;34m" + local COLOR_RESET="\033[0m" + + local timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]") + + echo_log() { + echo -e "$@" + } + + + echo_log "${COLOR_BLUE}${timestamp} ${1}${COLOR_RESET}" + shift + for message; do + echo_log "${COLOR_BLUE}${message}${COLOR_RESET}" + done +} + + + +openim::log::success() { + local V="${V:-0}" + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then + return + fi + local timestamp=$(date +"%m%d %H:%M:%S") + local reset_color='\033[0m' + echo_log -e "${COLOR_GREEN}[success ${timestamp}]${COLOR_SUFFIX}==> ${COLOR_GREEN}$@${reset_color}" +} + + + +function openim::log::test_log() { + echo_log "test log" + openim::log::info "openim::log::info" + openim::log::progress "openim::log::progress" + openim::log::status "openim::log::status" + openim::log::success "openim::log::success" + openim::log::error "openim::log::error" + openim::log::error_exit "openim::log::error_exit" +} + +# openim::log::test_log + +function openim::log::print_blue() { + echo -e "\033[0;36m$1\033[0m" +} + + +openim::log::colorless() { + local V="${V:-0}" + if [[ ${OPENIM_VERBOSE} < ${V} ]]; then + return + fi + timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]") + echo_log -e "${timestamp} ${1} " +} + + diff --git a/scripts-new/lib/path.sh b/scripts-new/lib/path.sh new file mode 100644 index 000000000..7e45b1cc5 --- /dev/null +++ b/scripts-new/lib/path.sh @@ -0,0 +1,34 @@ + +#!/usr/bin/env bash + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/util.sh" + + +get_conf_dir() { + local project_path="$1" + echo "${project_path}/conf/" +} + +get_log_dir() { + local project_path="$1" + echo "${project_path}/_output/logs/" +} + +get_bin_dir() { + local project_path="$1" + echo "${project_path}/_output/bin/" +} + + +get_bin_full_path() { + local project_path="$1" + local bin_name="$2" + + local host_platform=$(openim::util::host_platform) + local bin_full_path="${project_path}/get_bin_path/${host_platform}/${bin_name}" + echo "${bin_full_path}" +} + + + diff --git a/scripts-new/lib/release.sh b/scripts-new/lib/release.sh new file mode 100644 index 000000000..3e6cde103 --- /dev/null +++ b/scripts-new/lib/release.sh @@ -0,0 +1,670 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +############################################################################### +# Most of the ::release:: namespace functions have been moved to +# github.com/openim/release. Have a look in that repo and specifically in +# lib/releaselib.sh for ::release::-related functionality. +############################################################################### +# example: ./coscli cp/sync -r /home/off-line/docker-off-line/ cos://openim-1306374445/openim/image/amd/off-line/off-line/ -e cos.ap-guangzhou.myqcloud.com +# https://cloud.tencent.com/document/product/436/71763 + +readonly BUCKET="openim-1306374445" +readonly REGION="ap-guangzhou" +readonly COS_RELEASE_DIR="openim-release" +# readonly COS_RELEASE_DIR="openim-advanced-release" # !pro + +# default cos command tool coscli or coscmd +readonly COSTOOL="coscli" + +# This is where the final release artifacts are created locally +readonly RELEASE_STAGE="${LOCAL_OUTPUT_ROOT}/release-stage" +readonly RELEASE_TARS="${LOCAL_OUTPUT_ROOT}/release-tars" +readonly RELEASE_IMAGES="${LOCAL_OUTPUT_ROOT}/release-images" + +# OpenIM github account info +readonly OPENIM_GITHUB_ORG=openimsdk +readonly OPENIM_GITHUB_REPO=open-im-server +# readonly OPENIM_GITHUB_REPO=open-im-server-enterprise # !pro + +readonly ARTIFACT=openim.tar.gz +# readonly ARTIFACT=openim-enterprise.tar.gz # !pro + +readonly CHECKSUM=${ARTIFACT}.sha1sum + +OPENIM_BUILD_CONFORMANCE=${OPENIM_BUILD_CONFORMANCE:-y} +OPENIM_BUILD_PULL_LATEST_IMAGES=${OPENIM_BUILD_PULL_LATEST_IMAGES:-y} + +if [ -z "${OPENIM_ROOT}" ]; then + OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" +fi + +if [ -z "${TOOLS_DIR}" ]; then + TOOLS_DIR="${OPENIM_ROOT}/_output/tools" +fi + +# Validate a ci version +# +# Globals: +# None +# Arguments: +# version +# Returns: +# If version is a valid ci version +# Sets: (e.g. for '1.2.3-alpha.4.56+abcdef12345678') +# VERSION_MAJOR (e.g. '1') +# VERSION_MINOR (e.g. '2') +# VERSION_PATCH (e.g. '3') +# VERSION_PRERELEASE (e.g. 'alpha') +# VERSION_PRERELEASE_REV (e.g. '4') +# VERSION_BUILD_INFO (e.g. '.56+abcdef12345678') +# VERSION_COMMITS (e.g. '56') +function openim::release::parse_and_validate_ci_version() { + # Accept things like "v1.2.3-alpha.4.56+abcdef12345678" or "v1.2.3-beta.4" + local -r version_regex="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*)\\+[0-9a-f]{7,40})?$" + local -r version="${1-}" + [[ "${version}" =~ ${version_regex} ]] || { + openim::log::error "Invalid ci version: '${version}', must match regex ${version_regex}" + return 1 + } + + # The VERSION variables are used when this file is sourced, hence + # the shellcheck SC2034 'appears unused' warning is to be ignored. + + # shellcheck disable=SC2034 + VERSION_MAJOR="${BASH_REMATCH[1]}" + # shellcheck disable=SC2034 + VERSION_MINOR="${BASH_REMATCH[2]}" + # shellcheck disable=SC2034 + VERSION_PATCH="${BASH_REMATCH[3]}" + # shellcheck disable=SC2034 + VERSION_PRERELEASE="${BASH_REMATCH[4]}" + # shellcheck disable=SC2034 + VERSION_PRERELEASE_REV="${BASH_REMATCH[5]}" + # shellcheck disable=SC2034 + VERSION_BUILD_INFO="${BASH_REMATCH[6]}" + # shellcheck disable=SC2034 + VERSION_COMMITS="${BASH_REMATCH[7]}" +} + +# --------------------------------------------------------------------------- +# Build final release artifacts +function openim::release::clean_cruft() { + # Clean out cruft + find "${RELEASE_STAGE}" -name '*~' -exec rm {} \; + find "${RELEASE_STAGE}" -name '#*#' -exec rm {} \; + find "${RELEASE_STAGE}" -name '.DS*' -exec rm {} \; +} + +function openim::release::package_tarballs() { + # Clean out any old releases + rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}" + mkdir -p "${RELEASE_TARS}" + openim::release::package_src_tarball & + openim::release::package_client_tarballs & + openim::release::package_openim_manifests_tarball & + openim::release::package_server_tarballs & + openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; } + + openim::release::package_final_tarball & # _final depends on some of the previous phases + openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; } +} + +function openim::release::upload_tarballs() { + openim::log::info "upload ${RELEASE_TARS}/* to cos bucket ${BUCKET}." + for file in $(ls ${RELEASE_TARS}/*) + do + if [ "${COSTOOL}" == "coscli" ];then + echo "++++ ${TOOLS_DIR}/coscli cp ${file} cos://${BUCKET}/${COS_RELEASE_DIR}/${OPENIM_GIT_VERSION}/${file##*/}" + ${TOOLS_DIR}/coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/${OPENIM_GIT_VERSION}/${file##*/}" + ${TOOLS_DIR}/coscli cp "${file}" "cos://${BUCKET}/${COS_RELEASE_DIR}/latest/${file##*/}" + else + coscmd upload "${file}" "${COS_RELEASE_DIR}/${OPENIM_GIT_VERSION}/" + coscmd upload "${file}" "${COS_RELEASE_DIR}/latest/" + fi + done +} + +# Package the source code we built, for compliance/licensing/audit/yadda. +function openim::release::package_src_tarball() { + local -r src_tarball="${RELEASE_TARS}/openim-src.tar.gz" + openim::log::status "Building tarball: src" + if [[ "${OPENIM_GIT_TREE_STATE-}" = 'clean' ]]; then + git archive -o "${src_tarball}" HEAD + else + find "${OPENIM_ROOT}" -mindepth 1 -maxdepth 1 \ + ! \( \ + \( -path "${OPENIM_ROOT}"/_\* -o \ + -path "${OPENIM_ROOT}"/.git\* -o \ + -path "${OPENIM_ROOT}"/.github\* -o \ + -path "${OPENIM_ROOT}"/components\* -o \ + -path "${OPENIM_ROOT}"/logs\* -o \ + -path "${OPENIM_ROOT}"/_output\* -o \ + -path "${OPENIM_ROOT}"/.gitignore\* -o \ + -path "${OPENIM_ROOT}"/.gsemver.yml\* -o \ + -path "${OPENIM_ROOT}"/.config\* -o \ + -path "${OPENIM_ROOT}"/.chglog\* -o \ + -path "${OPENIM_ROOT}"/.gitlint -o \ + -path "${OPENIM_ROOT}"/.golangci.yml -o \ + -path "${OPENIM_ROOT}"/build/goreleaser.yaml -o \ + -path "${OPENIM_ROOT}"/.note.md -o \ + -path "${OPENIM_ROOT}"/.todo.md \ + \) -prune \ + \) -print0 \ + | "${TAR}" czf "${src_tarball}" --transform "s|${OPENIM_ROOT#/*}|openim|" --null -T - + fi +} + +# Package up all of the server binaries +function openim::release::package_server_tarballs() { + # Find all of the built client binaries + local long_platforms=("${LOCAL_OUTPUT_BINPATH}"/*/*) + + if [[ -n ${OPENIM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${OPENIM_BUILD_PLATFORMS}" + fi + + for platform_long in "${long_platforms[@]}"; do + local platform + local platform_tag + platform=${platform_long##${LOCAL_OUTPUT_BINPATH}/} # Strip LOCAL_OUTPUT_BINPATH + platform_tag=${platform/\//-} # Replace a "/" for a "-" + + openim::log::status "Starting tarball: server $platform_tag" + + ( + local release_stage="${RELEASE_STAGE}/server/${platform_tag}/openim" + openim::log::info "release_stage: ${release_stage}" + + rm -rf "${release_stage}" + mkdir -p "${release_stage}/server/bin" + + local server_bins=("${OPENIM_SERVER_BINARIES[@]}") + + openim::log::info " Copy client binaries: ${client_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" + openim::log::info " Copy client binaries to: ${release_stage}/server/bin" + + # Copy server binaries + cp "${server_bins[@]/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + "${release_stage}/server/bin/" + + openim::release::clean_cruft + + local package_name="${RELEASE_TARS}/openim-server-${platform_tag}.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." + ) & + done + openim::log::status "Waiting on tarballs" + openim::util::wait-for-jobs || { openim::log::error "server tarball creation failed"; exit 1; } +} + +# Package up all of the cross compiled clients. Over time this should grow into +# a full SDK +# Package up all of the cross compiled clients. Over time this should grow into +# a full SDK +function openim::release::package_client_tarballs() { + # Find all of the built client binaries + local long_platforms=("${LOCAL_OUTPUT_BINTOOLSPATH}"/*/*) + if [[ -n ${OPENIM_BUILD_PLATFORMS-} ]]; then + read -ra long_platforms <<< "${OPENIM_BUILD_PLATFORMS}" + fi + # echo "++++ LOCAL_OUTPUT_BINTOOLSPATH: ${LOCAL_OUTPUT_BINTOOLSPATH}" + # LOCAL_OUTPUT_BINTOOLSPATH: /data/workspaces/open-im-server/_output/bin/tools + # echo "++++ long_platforms: ${long_platforms[@]}" + # long_platforms: /data/workspaces/open-im-server/_output/bin/tools/darwin/amd64 /data/workspaces/open-im-server/_output/bin/tools/darwin/arm64 /data/workspaces/open-im-server/_output/bin/tools/linux/amd64 /data/workspaces/open-im-server/_output/bin/tools/linux/arm64 /data/workspaces/open-im-server/_output/bin/tools/linux/mips64 /data/workspaces/open-im-server/_output/bin/tools/linux/mips64le /data/workspaces/open-im-server/_output/bin/tools/linux/ppc64le /data/workspaces/open-im-server/_output/bin/tools/linux/s390x /data/workspaces/open-im-server/_output/bin/tools/windows/amd64 + + for platform_long in "${long_platforms[@]}"; do + local platform + local platform_tag + platform=${platform_long##${LOCAL_OUTPUT_BINTOOLSPATH}/} # Strip LOCAL_OUTPUT_BINTOOLSPATH + platform_tag=${platform/\//-} # Replace a "/" for a "-" + openim::log::status "Starting tarball: client $platform_tag" # darwin-amd64 + + ( + local release_stage="${RELEASE_STAGE}/client/${platform_tag}/openim" + + openim::log::info "release_stage: ${release_stage}" + # ++++ release_stage: /data/workspaces/open-im-server/_output/release-stage/client/darwin-amd64/openim + rm -rf "${release_stage}" + mkdir -p "${release_stage}/client/bin" + + local client_bins=("${OPENIM_CLIENT_BINARIES[@]}") + + # client_bins: changelog component imctl infra ncpu versionchecker yamlfmt + # Copy client binclient_bins:aries + openim::log::info " Copy client binaries: ${client_bins[@]/#/${LOCAL_OUTPUT_BINTOOLSPATH}/${platform}/}" + openim::log::info " Copy client binaries to: ${release_stage}/client/bin" + + cp "${client_bins[@]/#/${LOCAL_OUTPUT_BINTOOLSPATH}/${platform}/}" \ + "${release_stage}/client/bin/" + + openim::release::clean_cruft + + local package_name="${RELEASE_TARS}/openim-client-${platform_tag}.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." + ) & + done + openim::log::status "Waiting on tarballs" + openim::util::wait-for-jobs || { openim::log::error "client tarball creation failed"; exit 1; } +} + +# Package up all of the server binaries in docker images +function openim::release::build_server_images() { + # Clean out any old images + rm -rf "${RELEASE_IMAGES}" + local platform + for platform in "${OPENIM_SERVER_PLATFORMS[@]}"; do + local platform_tag + local arch + platform_tag=${platform/\//-} # Replace a "/" for a "-" + arch=$(basename "${platform}") + openim::log::status "Building images: $platform_tag" + + local release_stage + release_stage="${RELEASE_STAGE}/server/${platform_tag}/openim" + rm -rf "${release_stage}" + mkdir -p "${release_stage}/server/bin" + + # This fancy expression will expand to prepend a path + # (${LOCAL_OUTPUT_BINPATH}/${platform}/) to every item in the + # OPENIM_SERVER_IMAGE_BINARIES array. + cp "${OPENIM_SERVER_IMAGE_BINARIES[@]/bin/#/${LOCAL_OUTPUT_BINPATH}/${platform}/}" \ + "${release_stage}/server/bin/" + + openim::release::create_docker_images_for_server "${release_stage}/server/bin" "${arch}" + done +} + +function openim::release::md5() { + if which md5 >/dev/null 2>&1; then + md5 -q "$1" + else + md5sum "$1" | awk '{ print $1 }' + fi +} + +function openim::release::sha1() { + if which sha1sum >/dev/null 2>&1; then + sha1sum "$1" | awk '{ print $1 }' + else + shasum -a1 "$1" | awk '{ print $1 }' + fi +} + +function openim::release::sha256() { + if which sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{ print $1 }' + else + shasum -a256 "$1" | awk '{ print $1 }' + fi +} + + +function openim::release::build_conformance_image() { + local -r arch="$1" + local -r registry="$2" + local -r version="$3" + local -r save_dir="${4-}" + openim::log::status "Building conformance image for arch: ${arch}" + ARCH="${arch}" REGISTRY="${registry}" VERSION="${version}" \ + make -C cluster/images/conformance/ build >/dev/null + + local conformance_tag + conformance_tag="${registry}/conformance-${arch}:${version}" + if [[ -n "${save_dir}" ]]; then + "${DOCKER[@]}" save "${conformance_tag}" > "${save_dir}/conformance-${arch}.tar" + fi + openim::log::status "Deleting conformance image ${conformance_tag}" + "${DOCKER[@]}" rmi "${conformance_tag}" &>/dev/null || true +} + +# This builds all the release docker images (One docker image per binary) +# Args: +# $1 - binary_dir, the directory to save the tared images to. +# $2 - arch, architecture for which we are building docker images. +function openim::release::create_docker_images_for_server() { + # Create a sub-shell so that we don't pollute the outer environment + ( + local binary_dir + local arch + local binaries + local images_dir + binary_dir="$1" + arch="$2" + binaries=$(openim::build::get_docker_wrapped_binaries "${arch}") + images_dir="${RELEASE_IMAGES}/${arch}" + mkdir -p "${images_dir}" + + # k8s.gcr.io is the constant tag in the docker archives, this is also the default for config scripts in GKE. + # We can use OPENIM_DOCKER_REGISTRY to include and extra registry in the docker archive. + # If we use OPENIM_DOCKER_REGISTRY="k8s.gcr.io", then the extra tag (same) is ignored, see release_docker_image_tag below. + local -r docker_registry="k8s.gcr.io" + # Docker tags cannot contain '+' + local docker_tag="${OPENIM_GIT_VERSION/+/_}" + if [[ -z "${docker_tag}" ]]; then + openim::log::error "git version information missing; cannot create Docker tag" + return 1 + fi + + # provide `--pull` argument to `docker build` if `OPENIM_BUILD_PULL_LATEST_IMAGES` + # is set to y or Y; otherwise try to build the image without forcefully + # pulling the latest base image. + local docker_build_opts + docker_build_opts= + if [[ "${OPENIM_BUILD_PULL_LATEST_IMAGES}" =~ [yY] ]]; then + docker_build_opts='--pull' + fi + + for wrappable in $binaries; do + + local binary_name=${wrappable%%,*} + local base_image=${wrappable##*,} + local binary_file_path="${binary_dir}/${binary_name}" + local docker_build_path="${binary_file_path}.dockerbuild" + local docker_file_path="${docker_build_path}/Dockerfile" + local docker_image_tag="${docker_registry}/${binary_name}-${arch}:${docker_tag}" + + openim::log::status "Starting docker build for image: ${binary_name}-${arch}" + ( + rm -rf "${docker_build_path}" + mkdir -p "${docker_build_path}" + ln "${binary_file_path}" "${docker_build_path}/${binary_name}" + ln "${OPENIM_ROOT}/build/nsswitch.conf" "${docker_build_path}/nsswitch.conf" + chmod 0644 "${docker_build_path}/nsswitch.conf" + cat < "${docker_file_path}" +FROM ${base_image} +COPY ${binary_name} /usr/local/bin/${binary_name} +EOF + # ensure /etc/nsswitch.conf exists so go's resolver respects /etc/hosts + if [[ "${base_image}" =~ busybox ]]; then + echo "COPY nsswitch.conf /etc/" >> "${docker_file_path}" + fi + + "${DOCKER[@]}" build ${docker_build_opts:+"${docker_build_opts}"} -q -t "${docker_image_tag}" "${docker_build_path}" >/dev/null + # If we are building an official/alpha/beta release we want to keep + # docker images and tag them appropriately. + local -r release_docker_image_tag="${OPENIM_DOCKER_REGISTRY-$docker_registry}/${binary_name}-${arch}:${OPENIM_DOCKER_IMAGE_TAG-$docker_tag}" + if [[ "${release_docker_image_tag}" != "${docker_image_tag}" ]]; then + openim::log::status "Tagging docker image ${docker_image_tag} as ${release_docker_image_tag}" + "${DOCKER[@]}" rmi "${release_docker_image_tag}" 2>/dev/null || true + "${DOCKER[@]}" tag "${docker_image_tag}" "${release_docker_image_tag}" 2>/dev/null + fi + "${DOCKER[@]}" save -o "${binary_file_path}.tar" "${docker_image_tag}" "${release_docker_image_tag}" + echo "${docker_tag}" > "${binary_file_path}.docker_tag" + rm -rf "${docker_build_path}" + ln "${binary_file_path}.tar" "${images_dir}/" + + openim::log::status "Deleting docker image ${docker_image_tag}" + "${DOCKER[@]}" rmi "${docker_image_tag}" &>/dev/null || true + ) & + done + + if [[ "${OPENIM_BUILD_CONFORMANCE}" =~ [yY] ]]; then + openim::release::build_conformance_image "${arch}" "${docker_registry}" \ + "${docker_tag}" "${images_dir}" & + fi + + openim::util::wait-for-jobs || { openim::log::error "previous Docker build failed"; return 1; } + openim::log::status "Docker builds done" + ) + +} + +# This will pack openim-system manifests files for distros such as COS. +function openim::release::package_openim_manifests_tarball() { + openim::log::status "Building tarball: manifests" + + local src_dir="${OPENIM_ROOT}/deployments" + + local release_stage="${RELEASE_STAGE}/manifests/openim" + rm -rf "${release_stage}" + + local dst_dir="${release_stage}" + mkdir -p "${dst_dir}" + cp -r ${src_dir}/* "${dst_dir}" + #cp "${src_dir}/openim-api.yaml" "${dst_dir}" + #cp "${src_dir}/openim-cmdutils.yaml" "${dst_dir}" + #cp "${src_dir}/openim-crontask.yaml" "${dst_dir}" + #cp "${src_dir}/openim-msggateway.yaml" "${dst_dir}" + #cp "${src_dir}/openim-msgtransfer.yaml" "${dst_dir}" + #cp "${src_dir}/openim-push.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-auth.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-conversation.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-friend.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-group.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-msg.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-third.yaml" "${dst_dir}" + #cp "${src_dir}/openim-rpc-user.yaml" "${dst_dir}" + #cp "${OPENIM_ROOT}/cluster/gce/gci/health-monitor.sh" "${dst_dir}/health-monitor.sh" + + openim::release::clean_cruft + + local package_name="${RELEASE_TARS}/openim-manifests.tar.gz" + openim::release::create_tarball "${package_name}" "${release_stage}/.." +} + +# This is all the platform-independent stuff you need to run/install openim. +# Arch-specific binaries will need to be downloaded separately (possibly by +# using the bundled cluster/get-openim-binaries.sh script). +# Included in this tarball: +# - Cluster spin up/down scripts and configs for various cloud providers +# - Tarballs for manifest configs that are ready to be uploaded +# - Examples (which may or may not still work) +# - The remnants of the docs/ directory +function openim::release::package_final_tarball() { + openim::log::status "Building tarball: final" + + # This isn't a "full" tarball anymore, but the release lib still expects + # artifacts under "full/openim/" + local release_stage="${RELEASE_STAGE}/full/openim" + openim::log::info "release_stage(final): ${release_stage}" + rm -rf "${release_stage}" + mkdir -p "${release_stage}" + + mkdir -p "${release_stage}/client" + cat < "${release_stage}/client/README" +Client binaries are no longer included in the OpenIM final tarball. + +Run release/get-openim-binaries.sh to download client and server binaries. +EOF + + # We want everything in /scripts. + mkdir -p "${release_stage}/release" + mkdir -p "${OPENIM_ROOT}/scripts/release" + cp -R "${OPENIM_ROOT}/scripts/release" "${release_stage}/" + cat < "${release_stage}/release/get-openim-binaries.sh" +#!/usr/bin/env bash +# This file download openim client and server binaries from tencent cos bucket. + +os=linux arch=amd64 version=${OPENIM_GIT_VERSION} && wget https://${BUCKET}.cos.${REGION}.myqcloud.com/${COS_RELEASE_DIR}/\$version/{openim-client-\$os-\$arch.tar.gz,openim-server-\$os-\$arch.tar.gz} +EOF + chmod +x ${release_stage}/release/get-openim-binaries.sh + + mkdir -p "${release_stage}/server" + cp "${RELEASE_TARS}/openim-manifests.tar.gz" "${release_stage}/server/" + cat < "${release_stage}/server/README" +Server binary tarballs are no longer included in the OpenIM final tarball. + +Run release/get-openim-binaries.sh to download client and server binaries. +EOF + + # Include scripts/lib as a dependency for the cluster/ scripts + #mkdir -p "${release_stage}/hack" + #cp -R "${OPENIM_ROOT}/scripts/lib" "${release_stage}/scripts/" + + cp -R "${OPENIM_ROOT}"/{docs,config,scripts,deployments,README.md,LICENSE} "${release_stage}/" + + echo "${OPENIM_GIT_VERSION}" > "${release_stage}/version" + + openim::release::clean_cruft + + local package_name="${RELEASE_TARS}/${ARTIFACT}" + openim::release::create_tarball "${package_name}" "${release_stage}/.." +} + +# Build a release tarball. $1 is the output tar name. $2 is the base directory +# of the files to be packaged. This assumes that ${2}/iamis what is +# being packaged. +function openim::release::create_tarball() { + openim::build::ensure_tar + + local tarfile=$1 + local stagingdir=$2 + + "${TAR}" czf "${tarfile}" -C "${stagingdir}" openim --owner=0 --group=0 +} + +function openim::release::install_github_release(){ + GO111MODULE=on go install github.com/github-release/github-release@latest +} + +# Require the following tools: +# - github-release +# - gsemver +# - git-chglog +# - coscmd or coscli +function openim::release::verify_prereqs(){ + if [ -z "$(which ${TOOLS_DIR}/github-release 2>/dev/null)" ]; then + openim::log::info "'github-release' tool not installed, try to install it." + + if ! openim::release::install_github_release; then + openim::log::error "failed to install 'github-release'" + return 1 + fi + fi + + if [ -z "$(which ${TOOLS_DIR}/git-chglog 2>/dev/null)" ]; then + openim::log::info "'git-chglog' tool not installed, try to install it." + + if ! go install github.com/git-chglog/git-chglog/cmd/git-chglog@latest &>/dev/null; then + openim::log::error "failed to install 'git-chglog'" + return 1 + fi + fi + + if [ -z "$(which ${TOOLS_DIR}/gsemver 2>/dev/null)" ]; then + openim::log::info "'gsemver' tool not installed, try to install it." + + if ! go install github.com/arnaud-deprez/gsemver@latest &>/dev/null; then + openim::log::error "failed to install 'gsemver'" + return 1 + fi + fi + + if [ -z "$(which ${TOOLS_DIR}/${COSTOOL} 2>/dev/null)" ]; then + openim::log::info "${COSTOOL} tool not installed, try to install it." + + if ! make -C "${OPENIM_ROOT}" tools.install.${COSTOOL}; then + openim::log::error "failed to install ${COSTOOL}" + return 1 + fi + fi + + if [ -z "${TENCENT_SECRET_ID}" -o -z "${TENCENT_SECRET_KEY}" ];then + openim::log::info "You need set env: TENCENT_SECRET_ID(cos secretid) and TENCENT_SECRET_KEY(cos secretkey)" + openim::log::error "can not find env: TENCENT_SECRET_ID and TENCENT_SECRET_KEY" + return 1 + fi + + if [ "${COSTOOL}" == "coscli" ];then + if [ ! -f "${HOME}/.cos.yaml" ];then + cat << EOF > "${HOME}/.cos.yaml" +cos: + base: + secretid: ${TENCENT_SECRET_ID} + secretkey: ${TENCENT_SECRET_KEY} + sessiontoken: "" + buckets: + - name: ${BUCKET} + alias: ${BUCKET} + region: ${REGION} +EOF + fi + else + if [ ! -f "${HOME}/.cos.conf" ];then + cat << EOF > "${HOME}/.cos.conf" +[common] +secret_id = ${TENCENT_SECRET_ID} +secret_key = ${TENCENT_SECRET_KEY} +bucket = ${BUCKET} +region =${REGION} +max_thread = 5 +part_size = 1 +schema = https +EOF + fi + fi +} + +# Create a github release with specified tarballs. +# NOTICE: Must export 'GITHUB_TOKEN' env in the shell, details: +# https://github.com/github-release/github-release +function openim::release::github_release() { + # create a github release + if [ -z "${GITHUB_TOKEN}" ];then + openim::log::error "can not find env: GITHUB_TOKEN" + return 1 + fi + openim::log::info "create a new github release with tag ${OPENIM_GIT_VERSION}" + ${TOOLS_DIR}/github-release release \ + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ + --description "" \ + --pre-release \ + --draft + + # update openim tarballs + openim::log::info "upload ${ARTIFACT} to release ${OPENIM_GIT_VERSION}" + ${TOOLS_DIR}/github-release upload \ + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ + --name ${ARTIFACT} \ + --label "openim-${OPENIM_GIT_VERSION}" \ + --file ${RELEASE_TARS}/${ARTIFACT} + + for file in ${RELEASE_TARS}/*.tar.gz; do + if [[ -f "$file" ]]; then + filename=$(basename "$file") + openim::log::info "Update file ${filename} to release vertion ${OPENIM_GIT_VERSION}" + ${TOOLS_DIR}/github-release upload \ + --user ${OPENIM_GITHUB_ORG} \ + --repo ${OPENIM_GITHUB_REPO} \ + --tag ${OPENIM_GIT_VERSION} \ + --name "${filename}" \ + --file "${file}" + fi + done +} + +function openim::release::generate_changelog() { + openim::log::info "generate CHANGELOG-${OPENIM_GIT_VERSION#v}.md and commit it" + + local major_version=$(echo ${OPENIM_GIT_VERSION} | cut -d '+' -f 1) + + ${TOOLS_DIR}/git-chglog --config ${OPENIM_ROOT}/CHANGELOG/.chglog/config.yml ${OPENIM_GIT_VERSION} > ${OPENIM_ROOT}/CHANGELOG/CHANGELOG-${major_version#v}.md + + set +o errexit + git add "${OPENIM_ROOT}"/CHANGELOG/CHANGELOG-${major_version#v}.md + git commit -a -m "docs(changelog): add CHANGELOG-${major_version#v}.md" + echo "" + echo "##########################################################################" + echo "git commit -a -m \"docs(changelog): add CHANGELOG-${major_version#v}.md\"" + openim::log::info "You need git push CHANGELOG-${major_version#v}.md to remote" + echo "##########################################################################" + echo "" +} diff --git a/scripts-new/lib/util.sh b/scripts-new/lib/util.sh new file mode 100644 index 000000000..13bd263ca --- /dev/null +++ b/scripts-new/lib/util.sh @@ -0,0 +1,2896 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# this script is used to check whether the code is formatted by gofmt or not +# +# Usage: source scripts/lib/util.sh +################################################################################ + +# TODO Debug: Just for testing, please comment out +# OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +# source "${OPENIM_ROOT}/scripts/lib/logging.sh" + +#1、将IP写在一个文件里,比如文件名为hosts_file,一行一个IP地址。 +#2、修改ssh-mutual-trust.sh里面的用户名及密码,默认为root用户及密码123。 +# hosts_file_path="path/to/your/hosts/file" +# openim:util::setup_ssh_key_copy "$hosts_file_path" "root" "123" +function openim:util::setup_ssh_key_copy() { + local hosts_file="$1" + local username="${2:-root}" + local password="${3:-123}" + + local sshkey_file=~/.ssh/id_rsa.pub + + # check sshkey file + if [[ ! -e $sshkey_file ]]; then + expect -c " + spawn ssh-keygen -t rsa + expect \"Enter*\" { send \"\n\"; exp_continue; } + " + fi + + # get hosts list + local hosts=$(awk '/^[^#]/ {print $1}' "${hosts_file}") + + ssh_key_copy() { + local target=$1 + + # delete history + sed -i "/$target/d" ~/.ssh/known_hosts + + # copy key + expect -c " + set timeout 100 + spawn ssh-copy-id $username@$target + expect { + \"yes/no\" { send \"yes\n\"; exp_continue; } + \"*assword\" { send \"$password\n\"; } + \"already exist on the remote system\" { exit 1; } + } + expect eof + " + } + + # auto sshkey pair + for host in $hosts; do + if ! ping -i 0.2 -c 3 -W 1 "$host" > /dev/null 2>&1; then + echo "[ERROR]: Can't connect $host" + continue + fi + + local host_entry=$(awk "/$host/"'{print $1, $2}' /etc/hosts) + if [[ $host_entry ]]; then + local hostaddr=$(echo "$host_entry" | awk '{print $1}') + local hostname=$(echo "$host_entry" | awk '{print $2}') + ssh_key_copy "$hostaddr" + ssh_key_copy "$hostname" + else + ssh_key_copy "$host" + fi + done +} + +function openim::util::sourced_variable { + # Call this function to tell shellcheck that a variable is supposed to + # be used from other calling context. This helps quiet an "unused + # variable" warning from shellcheck and also document your code. + true +} + +openim::util::sortable_date() { + date "+%Y%m%d-%H%M%S" +} + +# arguments: target, item1, item2, item3, ... +# returns 0 if target is in the given items, 1 otherwise. +openim::util::array_contains() { + local search="$1" + local element + shift + for element; do + if [[ "${element}" == "${search}" ]]; then + return 0 + fi + done + return 1 +} + +openim::util::wait_for_url() { + local url=$1 + local prefix=${2:-} + local wait=${3:-1} + local times=${4:-30} + local maxtime=${5:-1} + + command -v curl >/dev/null || { + openim::log::usage "curl must be installed" + exit 1 + } + + local i + for i in $(seq 1 "${times}"); do + local out + if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then + openim::log::status "On try ${i}, ${prefix}: ${out}" + return 0 + fi + sleep "${wait}" + done + openim::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" + return 1 +} + +# Example: openim::util::wait_for_success 120 5 "openimctl get nodes|grep localhost" +# arguments: wait time, sleep time, shell command +# returns 0 if the shell command get output, 1 otherwise. +openim::util::wait_for_success(){ + local wait_time="$1" + local sleep_time="$2" + local cmd="$3" + while [ "$wait_time" -gt 0 ]; do + if eval "$cmd"; then + return 0 + else + sleep "$sleep_time" + wait_time=$((wait_time-sleep_time)) + fi + done + return 1 +} + +# Example: openim::util::trap_add 'echo "in trap DEBUG"' DEBUG +# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal +openim::util::trap_add() { + local trap_add_cmd + trap_add_cmd=$1 + shift + + for trap_add_name in "$@"; do + local existing_cmd + local new_cmd + + # Grab the currently defined trap commands for this trap + existing_cmd=$(trap -p "${trap_add_name}" | awk -F"'" '{print $2}') + + if [[ -z "${existing_cmd}" ]]; then + new_cmd="${trap_add_cmd}" + else + new_cmd="${trap_add_cmd};${existing_cmd}" + fi + + # Assign the test. Disable the shellcheck warning telling that trap + # commands should be single quoted to avoid evaluating them at this + # point instead evaluating them at run time. The logic of adding new + # commands to a single trap requires them to be evaluated right away. + # shellcheck disable=SC2064 + trap "${new_cmd}" "${trap_add_name}" + done +} + +# Opposite of openim::util::ensure-temp-dir() +openim::util::cleanup-temp-dir() { + rm -rf "${OPENIM_TEMP}" +} + +# Create a temp dir that'll be deleted at the end of this bash session. +# +# Vars set: +# OPENIM_TEMP +openim::util::ensure-temp-dir() { + if [[ -z ${OPENIM_TEMP-} ]]; then + OPENIM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t openimrnetes.XXXXXX) + openim::util::trap_add openim::util::cleanup-temp-dir EXIT + fi +} + +openim::util::host_os() { + local host_os + case "$(uname -s)" in + Darwin) + host_os=darwin + ;; + Linux) + host_os=linux + ;; + *) + openim::log::error "Unsupported host OS. Must be Linux or Mac OS X." + exit 1 + ;; + esac + echo "${host_os}" +} + +openim::util::host_arch() { + local host_arch + case "$(uname -m)" in + x86_64*) + host_arch=amd64 + ;; + i?86_64*) + host_arch=amd64 + ;; + amd64*) + host_arch=amd64 + ;; + aarch64*) + host_arch=arm64 + ;; + arm64*) + host_arch=arm64 + ;; + arm*) + host_arch=arm + ;; + i?86*) + host_arch=x86 + ;; + s390x*) + host_arch=s390x + ;; + ppc64le*) + host_arch=ppc64le + ;; + *) + openim::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." + exit 1 + ;; + esac + echo "${host_arch}" +} + +# Define a bash function to check the versions of Docker and Docker Compose +openim::util::check_docker_and_compose_versions() { + # Define the required versions of Docker and Docker Compose + required_docker_version="20.10.0" + required_compose_version="2.0" + + # Get the currently installed Docker version + installed_docker_version=$(docker --version | awk '{print $3}' | sed 's/,//') + + # Check if the installed Docker version matches the required version + if [[ "$installed_docker_version" < "$required_docker_version" ]]; then + echo "Docker version mismatch. Installed: $installed_docker_version, Required: $required_docker_version" + return 1 + fi + + # Check if the docker compose sub-command is available + if ! docker compose version &> /dev/null; then + echo "Docker does not support the docker compose sub-command" + echo "You need to upgrade Docker to the right version" + return 1 + fi + + # Get the currently installed Docker Compose version + installed_compose_version=$(docker compose version --short) + + # Check if the installed Docker Compose version matches the required version + if [[ "$installed_compose_version" < "$required_compose_version" ]]; then + echo "Docker Compose version mismatch. Installed: $installed_compose_version, Required: $required_compose_version" + return 1 + fi + +} + + +# The `openim::util::check_ports` function analyzes the state of processes based on given ports. +# It accepts multiple ports as arguments and prints: +# 1. The state of the process (whether it's running or not). +# 2. The start time of the process if it's running. +# User: +# openim::util::check_ports 8080 8081 8082 +# The function returns a status of 1 if any of the processes is not running. +openim::util::check_ports() { + # An array to collect ports of processes that are not running. + local not_started=() + + # An array to collect information about processes that are running. + local started=() + + echo "Checking ports: $*" + # Iterate over each given port. + for port in "$@"; do + # Initialize variables + # Check the OS and use the appropriate command + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if command -v ss > /dev/null 2>&1; then + info=$(ss -ltnp | grep ":$port" || true) + else + info=$(netstat -ltnp | grep ":$port" || true) + fi + elif [[ "$OSTYPE" == "darwin"* ]]; then + # For macOS, use lsof + info=$(lsof -P -i:"$port" | grep "LISTEN" || true) + fi + + # Check if any process is using the port + if [[ -z $info ]]; then + not_started+=($port) + else + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Extract relevant details for Linux: Process Name, PID, and FD. + details=$(echo $info | sed -n 's/.*users:(("\([^"]*\)",pid=\([^,]*\),fd=\([^)]*\))).*/\1 \2 \3/p') + command=$(echo $details | awk '{print $1}') + pid=$(echo $details | awk '{print $2}') + fd=$(echo $details | awk '{print $3}') + elif [[ "$OSTYPE" == "darwin"* ]]; then + # Handle extraction for macOS + pid=$(echo $info | awk '{print $2}' | cut -d'/' -f1) + command=$(ps -p $pid -o comm= | xargs basename) + fd=$(echo $info | awk '{print $4}' | cut -d'/' -f1) + fi + + # Get the start time of the process using the PID + if [[ -z $pid ]]; then + start_time="N/A" + else + start_time=$(ps -p $pid -o lstart=) + fi + + started+=("Port $port - Command: $command, PID: $pid, FD: $fd, Started: $start_time") + fi + done + + # Print information about ports whose processes are not running. + if [[ ${#not_started[@]} -ne 0 ]]; then + echo "### Not started ports:" + for port in "${not_started[@]}"; do + openim::log::error "Port $port is not started." + done + fi + + # Print information about ports whose processes are running. + if [[ ${#started[@]} -ne 0 ]]; then + echo "### Started ports:" + for info in "${started[@]}"; do + echo "$info" + done + fi + + # If any of the processes is not running, return a status of 1. + if [[ ${#not_started[@]} -ne 0 ]]; then + #openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + #openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' + return 1 + else + #openim::log::success "All specified ports are running." + return 0 + fi +} + +# set +o errexit +# Sample call for testing: +# openim::util::check_ports 10002 1004 12345 13306 +# + +# The `openim::util::check_process_names` function analyzes the state of processes based on given names. +# It accepts multiple process names as arguments and prints: +# 1. The state of the process (whether it's running or not). +# 2. The start time of the process if it's running. +# User: +# openim::util::check_process_names nginx mysql redis +# The function returns a status of 1 if any of the processes is not running. +openim::util::check_process_names() { + # Function to get the port of a process + get_port() { + local pid=$1 + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux + ss -ltnp 2>/dev/null | grep $pid | awk '{print $4}' | cut -d ':' -f2 + elif [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + lsof -nP -iTCP -sTCP:LISTEN -a -p $pid | awk 'NR>1 {print $9}' | sed 's/.*://' + else + echo "Unsupported OS" + return 1 + fi + } + + # Arrays to collect details of processes + local not_started=() + local started=() + + # Iterate over each given process name + for process_name in "$@"; do + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o comm=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done + + + # Print information + if [[ ${#not_started[@]} -ne 0 ]]; then + echo "Not started processes:" + for process_name in "${not_started[@]}"; do + echo "Process $process_name is not started." + done + fi + + if [[ ${#started[@]} -ne 0 ]]; then + echo + echo "Started processes:" + for info in "${started[@]}"; do + echo "$info" + done + fi + + # Return status + if [[ ${#not_started[@]} -ne 0 ]]; then + #openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + #openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' + return 1 + else + echo "" + openim::log::success "All processes are running." + return 0 + fi +} + +openim::util::check_process_names_for_stop() { + # Function to get the port of a process + get_port() { + local pid=$1 + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux + ss -ltnp 2>/dev/null | grep $pid | awk '{print $4}' | cut -d ':' -f2 + elif [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + lsof -nP -iTCP -sTCP:LISTEN -a -p $pid | awk 'NR>1 {print $9}' | sed 's/.*://' + else + echo "Unsupported OS" + return 1 + fi + } + + # Arrays to collect details of processes + local not_started=() + local started=() + + + # Iterate over each given process name + for process_name in "$@"; do + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o comm=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done + + + if [[ ${#started[@]} -ne 0 ]]; then + echo + echo "The programs that have not exited are:" + for info in "${started[@]}"; do + echo "$info " + done + return 1 + fi + + return 0 + +} + +# openim::util::check_process_names docker-pr + +# The `openim::util::stop_services_on_ports` function stops services running on specified ports. +# It accepts multiple ports as arguments and performs the following: +# 1. Attempts to stop any services running on the specified ports. +# 2. Prints details of services successfully stopped and those that failed to stop. +# Usage: +# openim::util::stop_services_on_ports 8080 8081 8082 +# The function returns a status of 1 if any service couldn't be stopped. +openim::util::stop_services_on_ports() { + # An array to collect ports of processes that couldn't be stopped. + local not_stopped=() + + # An array to collect information about processes that were stopped. + local stopped=() + + echo "Stopping services on ports: $*" + # Iterate over each given port. + for port in "$@"; do + # Use the `lsof` command to find process information related to the given port. + info=$(lsof -i :$port -n -P | grep LISTEN || true) + + # If there's process information, it means the process associated with the port is running. + if [[ -n $info ]]; then + # Extract the Process ID. + while read -r line; do + local pid=$(echo $line | awk '{print $2}') + + # Try to stop the service by killing its process. + if kill -15 $pid; then + stopped+=($port) + else + not_stopped+=($port) + fi + done <<< "$info" + fi + done + + # Print information about ports whose processes couldn't be stopped. + if [[ ${#not_stopped[@]} -ne 0 ]]; then + echo "Ports that couldn't be stopped:" + for port in "${not_stopped[@]}"; do + openim::log::status "Failed to stop service on port $port." + done + fi + + # Print information about ports whose processes were successfully stopped. + if [[ ${#stopped[@]} -ne 0 ]]; then + for port in "${stopped[@]}"; do + echo "Successfully stopped service on port $port." + done + fi + + # If any of the processes couldn't be stopped, return a status of 1. + if [[ ${#not_stopped[@]} -ne 0 ]]; then + return 1 + else + openim::log::success "All specified services were stopped." + echo "" + return 0 + fi +} +# nc -l -p 12345 +# nc -l -p 123456 +# ps -ef | grep "nc -l" +# openim::util::stop_services_on_ports 1234 12345 + + +# The `openim::util::stop_services_with_name` function stops services with specified names. +# It accepts multiple service names as arguments and performs the following: +# 1. Attempts to stop any services with the specified names. +# 2. Prints details of services successfully stopped and those that failed to stop. +# Usage: +# openim::util::stop_services_with_name nginx apache +# The function returns a status of 1 if any service couldn't be stopped. +openim::util::stop_services_with_name() { + # An array to collect names of processes that couldn't be stopped. + local not_stopped=() + + # An array to collect information about processes that were stopped. + local stopped=() + + # Iterate over each given service name. + for server_name in "$@"; do + # Use the `pgrep` command to find process IDs related to the given service name. + local pids=$(pgrep -f "$server_name") + # If no process was found with the name, add it to the not_stopped list + if [[ -z $pids ]]; then + not_stopped+=("$server_name") + continue + fi + local stopped_this_time=false + for pid in $pids; do + + # Exclude the PID of the current script + if [[ "$pid" == "$$" ]]; then + continue + fi + + # If there's a Process ID, it means the service with the name is running. + if [[ -n $pid ]]; then + # Print the binary path for the PID + binary_path=$(readlink -f /proc/$pid/exe) + openim::log::colorless "stop PID $pid full path: $binary_path" + + # Try to stop the service by killing its process. + if kill -15 $pid 2>/dev/null; then + stopped_this_time=true + fi + fi + done + + + if $stopped_this_time; then + stopped+=("$server_name") + else + not_stopped+=("$server_name") + fi + done + return 0 + +} +# sleep 333333& +# sleep 444444& +# ps -ef | grep "sleep" +# openim::util::stop_services_with_name "sleep 333333" "sleep 444444" + +# This figures out the host platform without relying on golang. We need this as +# we don't want a golang install to be a prerequisite to building yet we need +# this info to figure out where the final binaries are placed. +openim::util::host_platform() { + echo "$(openim::util::host_os)/$(openim::util::host_arch)" +} + +# looks for $1 in well-known output locations for the platform ($2) +# $OPENIM_ROOT must be set +openim::util::find-binary-for-platform() { + local -r lookfor="$1" + local -r platform="$2" + local locations=( + "${OPENIM_ROOT}/_output/bin/${lookfor}" + "${OPENIM_ROOT}/_output/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/local/bin/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/bin/${platform}/${lookfor}" + ) + + # List most recently-updated location. + local -r bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) + echo -n "${bin}" +} + +# looks for $1 in well-known output locations for the host platform +# $OPENIM_ROOT must be set +openim::util::find-binary() { + openim::util::find-binary-for-platform "$1" "$(openim::util::host_platform)" +} + +# Run all known doc generators (today gendocs and genman for openimctl) +# $1 is the directory to put those generated documents +openim::util::gen-docs() { + local dest="$1" + + # Find binary + gendocs=$(openim::util::find-binary "gendocs") + genopenimdocs=$(openim::util::find-binary "genopenimdocs") + genman=$(openim::util::find-binary "genman") + genyaml=$(openim::util::find-binary "genyaml") + genfeddocs=$(openim::util::find-binary "genfeddocs") + + # TODO: If ${genfeddocs} is not used from anywhere (it isn't used at + # least from k/k tree), remove it completely. + openim::util::sourced_variable "${genfeddocs}" + + mkdir -p "${dest}/docs/guide/en-US/cmd/openimctl/" + "${gendocs}" "${dest}/docs/guide/en-US/cmd/openimctl/" + + mkdir -p "${dest}/docs/guide/en-US/cmd/" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-api" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-cmdutils" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-crontask" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msggateway" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msgtransfer" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-push" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-auth" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-conversation" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-friend" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-group" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-msg" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-third" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-user" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/openimctl" "openimctl" + + mkdir -p "${dest}/docs/man/man1/" +"${genman}" "${dest}/docs/man/man1/" "openim-api" +"${genman}" "${dest}/docs/man/man1/" "openim-cmdutils" +"${genman}" "${dest}/docs/man/man1/" "openim-crontask" +"${genman}" "${dest}/docs/man/man1/" "openim-msggateway" +"${genman}" "${dest}/docs/man/man1/" "openim-msgtransfer" +"${genman}" "${dest}/docs/man/man1/" "openim-push" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-auth" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-conversation" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-friend" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-group" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-msg" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-third" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-user" + + mkdir -p "${dest}/docs/guide/en-US/yaml/openimctl/" + "${genyaml}" "${dest}/docs/guide/en-US/yaml/openimctl/" + + # create the list of generated files + pushd "${dest}" > /dev/null || return 1 + touch docs/.generated_docs + find . -type f | cut -sd / -f 2- | LC_ALL=C sort > docs/.generated_docs + popd > /dev/null || return 1 +} + +# Removes previously generated docs-- we don't want to check them in. $OPENIM_ROOT +# must be set. +openim::util::remove-gen-docs() { + if [ -e "${OPENIM_ROOT}/docs/.generated_docs" ]; then + # remove all of the old docs; we don't want to check them in. + while read -r file; do + rm "${OPENIM_ROOT}/${file}" 2>/dev/null || true + done <"${OPENIM_ROOT}/docs/.generated_docs" + # The docs/.generated_docs file lists itself, so we don't need to explicitly + # delete it. + fi +} + +# Returns the name of the upstream remote repository name for the local git +# repo, e.g. "upstream" or "origin". +openim::util::git_upstream_remote_name() { + git remote -v | grep fetch |\ + grep -E 'github.com[/:]openimsdk/open-im-server|openim.cc/server' |\ + head -n 1 | awk '{print $1}' +} + +# Exits script if working directory is dirty. If it's run interactively in the terminal +# the user can commit changes in a second terminal. This script will wait. +openim::util::ensure_clean_working_dir() { + while ! git diff HEAD --exit-code &>/dev/null; do + echo -e "\nUnexpected dirty working directory:\n" + if tty -s; then + git status -s + else + git diff -a # be more verbose in log files without tty + exit 1 + fi | sed 's/^/ /' + echo -e "\nCommit your changes in another terminal and then continue here by pressing enter." + read -r + done 1>&2 +} + +# Find the base commit using: +# $PULL_BASE_SHA if set (from Prow) +# current ref from the remote upstream branch +openim::util::base_ref() { + local -r git_branch=$1 + + if [[ -n ${PULL_BASE_SHA:-} ]]; then + echo "${PULL_BASE_SHA}" + return + fi + + full_branch="$(openim::util::git_upstream_remote_name)/${git_branch}" + + # make sure the branch is valid, otherwise the check will pass erroneously. + if ! git describe "${full_branch}" >/dev/null; then + # abort! + exit 1 + fi + + echo "${full_branch}" +} + +# Checks whether there are any files matching pattern $2 changed between the +# current branch and upstream branch named by $1. +# Returns 1 (false) if there are no changes +# 0 (true) if there are changes detected. +openim::util::has_changes() { + local -r git_branch=$1 + local -r pattern=$2 + local -r not_pattern=${3:-totallyimpossiblepattern} + + local base_ref + base_ref=$(openim::util::base_ref "${git_branch}") + echo "Checking for '${pattern}' changes against '${base_ref}'" + + # notice this uses ... to find the first shared ancestor + if git diff --name-only "${base_ref}...HEAD" | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + return 0 + fi + # also check for pending changes + if git status --porcelain | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + echo "Detected '${pattern}' uncommitted changes." + return 0 + fi + echo "No '${pattern}' changes detected." + return 1 +} + +openim::util::download_file() { + local -r url=$1 + local -r destination_file=$2 + + rm "${destination_file}" 2&> /dev/null || true + + for i in $(seq 5) + do + if ! curl -fsSL --retry 3 --keepalive-time 2 "${url}" -o "${destination_file}"; then + echo "Downloading ${url} failed. $((5-i)) retries left." + sleep 1 + else + echo "Downloading ${url} succeed" + return 0 + fi + done + return 1 +} + +# Test whether openssl is installed. +# Sets: +# OPENSSL_BIN: The path to the openssl binary to use +function openim::util::test_openssl_installed { + if ! openssl version >& /dev/null; then + echo "Failed to run openssl. Please ensure openssl is installed" + exit 1 + fi + + OPENSSL_BIN=$(command -v openssl) +} + +# creates a client CA, args are sudo, dest-dir, ca-id, purpose +# purpose is dropped in after "key encipherment", you usually want +# '"client auth"' +# '"server auth"' +# '"client auth","server auth"' +function openim::util::create_signing_certkey { + local sudo=$1 + local dest_dir=$2 + local id=$3 + local purpose=$4 + # Create client ca + ${sudo} /usr/bin/env bash -e < "${dest_dir}/${id}-ca-config.json" +EOF +} + +# signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups... +function openim::util::create_client_certkey { + local sudo=$1 + local dest_dir=$2 + local ca=$3 + local id=$4 + local cn=${5:-$4} + local groups="" + local SEP="" + shift 5 + while [ -n "${1:-}" ]; do + groups+="${SEP}{\"O\":\"$1\"}" + SEP="," + shift 1 + done + ${sudo} /usr/bin/env bash -e < /dev/null +apiVersion: v1 +kind: Config +clusters: + - cluster: + certificate-authority: ${ca_file} + server: https://${api_host}:${api_port}/ + name: local-up-cluster +users: + - user: + token: ${token} + client-certificate: ${dest_dir}/client-${client_id}.crt + client-key: ${dest_dir}/client-${client_id}.key + name: local-up-cluster +contexts: + - context: + cluster: local-up-cluster + user: local-up-cluster + name: local-up-cluster +current-context: local-up-cluster +EOF + + # flatten the openimconfig files to make them self contained + username=$(whoami) + ${sudo} /usr/bin/env bash -e < "/tmp/${client_id}.openimconfig" + mv -f "/tmp/${client_id}.openimconfig" "${dest_dir}/${client_id}.openimconfig" + chown ${username} "${dest_dir}/${client_id}.openimconfig" +EOF +} + +# Determines if docker can be run, failures may simply require that the user be added to the docker group. +function openim::util::ensure_docker_daemon_connectivity { + IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}" + # Expand ${DOCKER[@]} only if it's not unset. This is to work around + # Bash 3 issue with unbound variable. + DOCKER=(docker ${DOCKER[@]:+"${DOCKER[@]}"}) + if ! "${DOCKER[@]}" info > /dev/null 2>&1 ; then + cat <<'EOF' >&2 +Can't connect to 'docker' daemon. please fix and retry. + +Possible causes: + - Docker Daemon not started + - Linux: confirm via your init system + - macOS w/ docker-machine: run `docker-machine ls` and `docker-machine start ` + - macOS w/ Docker for Mac: Check the menu bar and start the Docker application + - DOCKER_HOST hasn't been set or is set incorrectly + - Linux: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - macOS w/ docker-machine: run `eval "$(docker-machine env )"` + - macOS w/ Docker for Mac: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - Other things to check: + - Linux: User isn't in 'docker' group. Add and relogin. + - Something like 'sudo usermod -a -G docker ${USER}' + - RHEL7 bug and workaround: https://bugzilla.redhat.com/show_bug.cgi?id=1119282#c8 +EOF + return 1 + fi +} + +# Wait for background jobs to finish. Return with +# an error status if any of the jobs failed. +openim::util::wait-for-jobs() { + local fail=0 + local job + for job in $(jobs -p); do + wait "${job}" || fail=$((fail + 1)) + done + return ${fail} +} + +# openim::util::join +# Concatenates the list elements with the delimiter passed as first parameter +# +# Ex: openim::util::join , a b c +# -> a,b,c +function openim::util::join { + local IFS="$1" + shift + echo "$*" +} + +# Function: openim::util::list-to-string +# Description: Converts a list to a string, removing spaces, brackets, and commas. +# Example input: [1002 3 , 2 32 3 , 3 434 ,] +# Example output: 10023 2323 3434 +# Example usage: +# result=$(openim::util::list-to-string "[10023, 2323, 3434]") +# echo $result +function openim::util::list-to-string() { + # Capture all arguments into a single string + ports_list="$*" + + # Use sed for transformations: + # 1. Remove spaces + # 2. Replace commas with spaces + # 3. Remove opening and closing brackets + ports_array=$(echo "$ports_list" | sed 's/ //g; s/,/ /g; s/^\[\(.*\)\]$/\1/') + # For external use, we might want to echo the result so that it can be captured by callers + echo "$ports_array" +} +# MSG_GATEWAY_PROM_PORTS=$(openim::util::list-to-string "10023, 2323, 34 34") +# read -a MSG_GATEWAY_PROM_PORTS <<< $(openim::util::list-to-string "10023, 2323, 34 34") +# echo ${MSG_GATEWAY_PROM_PORTS} +# echo "${#MSG_GATEWAY_PROM_PORTS[@]}" +# Downloads cfssl/cfssljson/cfssl-certinfo into $1 directory if they do not already exist in PATH +# +# Assumed vars: +# $1 (cfssl directory) (optional) +# +# Sets: +# CFSSL_BIN: The path of the installed cfssl binary +# CFSSLJSON_BIN: The path of the installed cfssljson binary +# CFSSLCERTINFO_BIN: The path of the installed cfssl-certinfo binary +# +function openim::util::ensure-cfssl { + if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null && command -v cfssl-certinfo &>/dev/null; then + CFSSL_BIN=$(command -v cfssl) + CFSSLJSON_BIN=$(command -v cfssljson) + CFSSLCERTINFO_BIN=$(command -v cfssl-certinfo) + return 0 + fi + + host_arch=$(openim::util::host_arch) + + if [[ "${host_arch}" != "amd64" ]]; then + echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + + # Create a temp dir for cfssl if no directory was given + local cfssldir=${1:-} + if [[ -z "${cfssldir}" ]]; then + cfssldir="$HOME/bin" + fi + + mkdir -p "${cfssldir}" + pushd "${cfssldir}" > /dev/null || return 1 + + echo "Unable to successfully run 'cfssl' from ${PATH}; downloading instead..." + kernel=$(uname -s) + case "${kernel}" in + Linux) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 + ;; + Darwin) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_darwin-amd64 + ;; + *) + echo "Unknown, unsupported platform: ${kernel}." >&2 + echo "Supported platforms: Linux, Darwin." >&2 + exit 2 + esac + + chmod +x cfssl || true + chmod +x cfssljson || true + chmod +x cfssl-certinfo || true + + CFSSL_BIN="${cfssldir}/cfssl" + CFSSLJSON_BIN="${cfssldir}/cfssljson" + CFSSLCERTINFO_BIN="${cfssldir}/cfssl-certinfo" + if [[ ! -x ${CFSSL_BIN} || ! -x ${CFSSLJSON_BIN} || ! -x ${CFSSLCERTINFO_BIN} ]]; then + echo "Failed to download 'cfssl'." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + popd > /dev/null || return 1 +} + +function openim::util::ensure-docker-buildx { + # podman returns 0 on `docker buildx version`, docker on `docker buildx`. One of them must succeed. + if docker buildx version >/dev/null 2>&1 || docker buildx >/dev/null 2>&1; then + return 0 + else + echo "ERROR: docker buildx not available. Docker 19.03 or higher is required with experimental features enabled" + exit 1 + fi +} + +# openim::util::ensure-bash-version +# Check if we are using a supported bash version +# +function openim::util::ensure-bash-version { + # shellcheck disable=SC2004 + if ((${BASH_VERSINFO[0]}<4)) || ( ((${BASH_VERSINFO[0]}==4)) && ((${BASH_VERSINFO[1]}<2)) ); then + echo "ERROR: This script requires a minimum bash version of 4.2, but got version of ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}" + if [ "$(uname)" = 'Darwin' ]; then + echo "On macOS with homebrew 'brew install bash' is sufficient." + fi + exit 1 + fi +} + +# openim::util::ensure-install-nginx +# Check if nginx is installed +# +function openim::util::ensure-install-nginx { + if ! command -v nginx &>/dev/null; then + echo "ERROR: nginx not found. Please install nginx." + exit 1 + fi + + for port in "80" + do + if echo |telnet 127.0.0.1 $port 2>&1|grep refused &>/dev/null;then + exit 1 + fi + done +} + +# openim::util::ensure-gnu-sed +# Determines which sed binary is gnu-sed on linux/darwin +# +# Sets: +# SED: The name of the gnu-sed binary +# +function openim::util::ensure-gnu-sed { + # NOTE: the echo below is a workaround to ensure sed is executed before the grep. + # see: https://github.com/openimrnetes/openimrnetes/issues/87251 + sed_help="$(LANG=C sed --help 2>&1 || true)" + if echo "${sed_help}" | grep -q "GNU\|BusyBox"; then + SED="sed" + elif command -v gsed &>/dev/null; then + SED="gsed" + else + openim::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 + return 1 + fi + openim::util::sourced_variable "${SED}" +} + +# openim::util::ensure-gnu-date +# Determines which date binary is gnu-date on linux/darwin +# +# Sets: +# DATE: The name of the gnu-date binary +# +function openim::util::ensure-gnu-date { + # NOTE: the echo below is a workaround to ensure date is executed before the grep. + date_help="$(LANG=C date --help 2>&1 || true)" + if echo "${date_help}" | grep -q "GNU\|BusyBox"; then + DATE="date" + elif command -v gdate &>/dev/null; then + DATE="gdate" + else + openim::log::error "Failed to find GNU date as date or gdate. If you are on Mac: brew install coreutils." >&2 + return 1 + fi + openim::util::sourced_variable "${DATE}" +} + +# openim::util::check-file-in-alphabetical-order +# Check that the file is in alphabetical order +# +function openim::util::check-file-in-alphabetical-order { + local failure_file="$1" + if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then + { + echo + echo "${failure_file} is not in alphabetical order. Please sort it:" + echo + echo " LC_ALL=C sort -o ${failure_file} ${failure_file}" + echo + } >&2 + false + fi +} + +# openim::util::require-jq +# Checks whether jq is installed. +function openim::util::require-jq { + if ! command -v jq &>/dev/null; then + openim::log::errexit "jq not found. Please install." 1>&2 + fi +} + +# openim::util::require-dig +# Checks whether dig is installed and provides installation instructions if it is not. +function openim::util::require-dig { + if ! command -v dig &>/dev/null; then + openim::log::error "Please install 'dig' to use this feature. OR Set the environment variable for OPENIM_IP" + openim::log::error "Installation instructions:" + openim::log::error " For Ubuntu/Debian: sudo apt-get install dnsutils" + openim::log::error " For CentOS/RedHat: sudo yum install bind-utils" + openim::log::error " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind" + openim::log::error " For Windows: Install BIND9 tools from https://www.isc.org/download/" + openim::log::error_exit "dig command not found." + fi + return 0 +} + +# outputs md5 hash of $1, works on macOS and Linux +function openim::util::md5() { + if which md5 >/dev/null 2>&1; then + md5 -q "$1" + else + md5sum "$1" | awk '{ print $1 }' + fi +} + +# openim::util::read-array +# Reads in stdin and adds it line by line to the array provided. This can be +# used instead of "mapfile -t", and is bash 3 compatible. +# +# Assumed vars: +# $1 (name of array to create/modify) +# +# Example usage: +# openim::util::read-array files < <(ls -1) +# +function openim::util::read-array { + local i=0 + unset -v "$1" + while IFS= read -r "$1[i++]"; do :; done + eval "[[ \${$1[--i]} ]]" || unset "$1[i]" # ensures last element isn't empty +} + +# Some useful colors. +if [[ -z "${color_start-}" ]]; then + declare -r color_start="\033[" + declare -r color_red="${color_start}0;31m" + declare -r color_yellow="${color_start}0;33m" + declare -r color_green="${color_start}0;32m" + declare -r color_blue="${color_start}1;34m" + declare -r color_cyan="${color_start}1;36m" + declare -r color_norm="${color_start}0m" + + openim::util::sourced_variable "${color_start}" + openim::util::sourced_variable "${color_red}" + openim::util::sourced_variable "${color_yellow}" + openim::util::sourced_variable "${color_green}" + openim::util::sourced_variable "${color_blue}" + openim::util::sourced_variable "${color_cyan}" + openim::util::sourced_variable "${color_norm}" +fi + +# ex: ts=2 sw=2 et filetype=sh + +function openim::util::desc() { + openim::util:run::maybe_first_prompt + rate=25 + if [ -n "$DEMO_RUN_FAST" ]; then + rate=1000 + fi + echo "$blue# $@$reset" | pv -qL $rate#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# this script is used to check whether the code is formatted by gofmt or not +# +# Usage: source scripts/lib/util.sh +################################################################################ + +# TODO Debug: Just for testing, please comment out +# OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) +# source "${OPENIM_ROOT}/scripts/lib/logging.sh" + +#1、将IP写在一个文件里,比如文件名为hosts_file,一行一个IP地址。 +#2、修改ssh-mutual-trust.sh里面的用户名及密码,默认为root用户及密码123。 +# hosts_file_path="path/to/your/hosts/file" +# openim:util::setup_ssh_key_copy "$hosts_file_path" "root" "123" +function openim:util::setup_ssh_key_copy() { + local hosts_file="$1" + local username="${2:-root}" + local password="${3:-123}" + + local sshkey_file=~/.ssh/id_rsa.pub + + # check sshkey file + if [[ ! -e $sshkey_file ]]; then + expect -c " + spawn ssh-keygen -t rsa + expect \"Enter*\" { send \"\n\"; exp_continue; } + " + fi + + # get hosts list + local hosts=$(awk '/^[^#]/ {print $1}' "${hosts_file}") + + ssh_key_copy() { + local target=$1 + + # delete history + sed -i "/$target/d" ~/.ssh/known_hosts + + # copy key + expect -c " + set timeout 100 + spawn ssh-copy-id $username@$target + expect { + \"yes/no\" { send \"yes\n\"; exp_continue; } + \"*assword\" { send \"$password\n\"; } + \"already exist on the remote system\" { exit 1; } + } + expect eof + " + } + + # auto sshkey pair + for host in $hosts; do + if ! ping -i 0.2 -c 3 -W 1 "$host" > /dev/null 2>&1; then + echo "[ERROR]: Can't connect $host" + continue + fi + + local host_entry=$(awk "/$host/"'{print $1, $2}' /etc/hosts) + if [[ $host_entry ]]; then + local hostaddr=$(echo "$host_entry" | awk '{print $1}') + local hostname=$(echo "$host_entry" | awk '{print $2}') + ssh_key_copy "$hostaddr" + ssh_key_copy "$hostname" + else + ssh_key_copy "$host" + fi + done +} + +function openim::util::sourced_variable { + # Call this function to tell shellcheck that a variable is supposed to + # be used from other calling context. This helps quiet an "unused + # variable" warning from shellcheck and also document your code. + true +} + +openim::util::sortable_date() { + date "+%Y%m%d-%H%M%S" +} + +# arguments: target, item1, item2, item3, ... +# returns 0 if target is in the given items, 1 otherwise. +openim::util::array_contains() { + local search="$1" + local element + shift + for element; do + if [[ "${element}" == "${search}" ]]; then + return 0 + fi + done + return 1 +} + +openim::util::wait_for_url() { + local url=$1 + local prefix=${2:-} + local wait=${3:-1} + local times=${4:-30} + local maxtime=${5:-1} + + command -v curl >/dev/null || { + openim::log::usage "curl must be installed" + exit 1 + } + + local i + for i in $(seq 1 "${times}"); do + local out + if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then + openim::log::status "On try ${i}, ${prefix}: ${out}" + return 0 + fi + sleep "${wait}" + done + openim::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" + return 1 +} + +# Example: openim::util::wait_for_success 120 5 "openimctl get nodes|grep localhost" +# arguments: wait time, sleep time, shell command +# returns 0 if the shell command get output, 1 otherwise. +openim::util::wait_for_success(){ + local wait_time="$1" + local sleep_time="$2" + local cmd="$3" + while [ "$wait_time" -gt 0 ]; do + if eval "$cmd"; then + return 0 + else + sleep "$sleep_time" + wait_time=$((wait_time-sleep_time)) + fi + done + return 1 +} + +# Example: openim::util::trap_add 'echo "in trap DEBUG"' DEBUG +# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal +openim::util::trap_add() { + local trap_add_cmd + trap_add_cmd=$1 + shift + + for trap_add_name in "$@"; do + local existing_cmd + local new_cmd + + # Grab the currently defined trap commands for this trap + existing_cmd=$(trap -p "${trap_add_name}" | awk -F"'" '{print $2}') + + if [[ -z "${existing_cmd}" ]]; then + new_cmd="${trap_add_cmd}" + else + new_cmd="${trap_add_cmd};${existing_cmd}" + fi + + # Assign the test. Disable the shellcheck warning telling that trap + # commands should be single quoted to avoid evaluating them at this + # point instead evaluating them at run time. The logic of adding new + # commands to a single trap requires them to be evaluated right away. + # shellcheck disable=SC2064 + trap "${new_cmd}" "${trap_add_name}" + done +} + +# Opposite of openim::util::ensure-temp-dir() +openim::util::cleanup-temp-dir() { + rm -rf "${OPENIM_TEMP}" +} + +# Create a temp dir that'll be deleted at the end of this bash session. +# +# Vars set: +# OPENIM_TEMP +openim::util::ensure-temp-dir() { + if [[ -z ${OPENIM_TEMP-} ]]; then + OPENIM_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t openimrnetes.XXXXXX) + openim::util::trap_add openim::util::cleanup-temp-dir EXIT + fi +} + +openim::util::host_os() { + local host_os + case "$(uname -s)" in + Darwin) + host_os=darwin + ;; + Linux) + host_os=linux + ;; + *) + openim::log::error "Unsupported host OS. Must be Linux or Mac OS X." + exit 1 + ;; + esac + echo "${host_os}" +} + +openim::util::host_arch() { + local host_arch + case "$(uname -m)" in + x86_64*) + host_arch=amd64 + ;; + i?86_64*) + host_arch=amd64 + ;; + amd64*) + host_arch=amd64 + ;; + aarch64*) + host_arch=arm64 + ;; + arm64*) + host_arch=arm64 + ;; + arm*) + host_arch=arm + ;; + i?86*) + host_arch=x86 + ;; + s390x*) + host_arch=s390x + ;; + ppc64le*) + host_arch=ppc64le + ;; + *) + openim::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." + exit 1 + ;; + esac + echo "${host_arch}" +} + +# Define a bash function to check the versions of Docker and Docker Compose +openim::util::check_docker_and_compose_versions() { + # Define the required versions of Docker and Docker Compose + required_docker_version="20.10.0" + required_compose_version="2.0" + + # Get the currently installed Docker version + installed_docker_version=$(docker --version | awk '{print $3}' | sed 's/,//') + + # Check if the installed Docker version matches the required version + if [[ "$installed_docker_version" < "$required_docker_version" ]]; then + echo "Docker version mismatch. Installed: $installed_docker_version, Required: $required_docker_version" + return 1 + fi + + # Check if the docker compose sub-command is available + if ! docker compose version &> /dev/null; then + echo "Docker does not support the docker compose sub-command" + echo "You need to upgrade Docker to the right version" + return 1 + fi + + # Get the currently installed Docker Compose version + installed_compose_version=$(docker compose version --short) + + # Check if the installed Docker Compose version matches the required version + if [[ "$installed_compose_version" < "$required_compose_version" ]]; then + echo "Docker Compose version mismatch. Installed: $installed_compose_version, Required: $required_compose_version" + return 1 + fi + +} + + +# The `openim::util::check_ports` function analyzes the state of processes based on given ports. +# It accepts multiple ports as arguments and prints: +# 1. The state of the process (whether it's running or not). +# 2. The start time of the process if it's running. +# User: +# openim::util::check_ports 8080 8081 8082 +# The function returns a status of 1 if any of the processes is not running. +openim::util::check_ports() { + # An array to collect ports of processes that are not running. + local not_started=() + + # An array to collect information about processes that are running. + local started=() + + echo "Checking ports: $*" + # Iterate over each given port. + for port in "$@"; do + # Initialize variables + # Check the OS and use the appropriate command + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if command -v ss > /dev/null 2>&1; then + info=$(ss -ltnp | grep ":$port" || true) + else + info=$(netstat -ltnp | grep ":$port" || true) + fi + elif [[ "$OSTYPE" == "darwin"* ]]; then + # For macOS, use lsof + info=$(lsof -i:"$port" | grep "\*:$port" || true) + fi + + # Check if any process is using the port + if [[ -z $info ]]; then + not_started+=($port) + else + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Extract relevant details for Linux: Process Name, PID, and FD. + details=$(echo $info | sed -n 's/.*users:(("\([^"]*\)",pid=\([^,]*\),fd=\([^)]*\))).*/\1 \2 \3/p') + command=$(echo $details | awk '{print $1}') + pid=$(echo $details | awk '{print $2}') + fd=$(echo $details | awk '{print $3}') + elif [[ "$OSTYPE" == "darwin"* ]]; then + # Handle extraction for macOS + pid=$(echo $info | awk '{print $2}' | cut -d'/' -f1) + command=$(ps -p $pid -o comm= | xargs basename) + fd=$(echo $info | awk '{print $4}' | cut -d'/' -f1) + fi + + # Get the start time of the process using the PID + if [[ -z $pid ]]; then + start_time="N/A" + else + start_time=$(ps -p $pid -o lstart=) + fi + + started+=("Port $port - Command: $command, PID: $pid, FD: $fd, Started: $start_time") + fi + done + + # Print information about ports whose processes are not running. + if [[ ${#not_started[@]} -ne 0 ]]; then + printf "\n### Not started ports:" + for port in "${not_started[@]}"; do + openim::log::error "Port $port is not started." + done + fi + + # Print information about ports whose processes are running. + if [[ ${#started[@]} -ne 0 ]]; then + printf "\n### Started ports:" + for info in "${started[@]}"; do + echo "$info" + done + fi + + # If any of the processes is not running, return a status of 1. + if [[ ${#not_started[@]} -ne 0 ]]; then + openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + echo "" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' + return 1 + else + openim::log::success "All specified processes are running." + return 0 + fi +} + +# set +o errexit +# Sample call for testing: +# openim::util::check_ports 10002 1004 12345 13306 +# + +# The `openim::util::check_process_names` function analyzes the state of processes based on given names. +# It accepts multiple process names as arguments and prints: +# 1. The state of the process (whether it's running or not). +# 2. The start time of the process if it's running. +# User: +# openim::util::check_process_names nginx mysql redis +# The function returns a status of 1 if any of the processes is not running. +openim::util::check_process_names() { + # Function to get the port of a process + get_port() { + local pid=$1 + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux + ss -ltnp 2>/dev/null | grep $pid | awk '{print $4}' | cut -d ':' -f2 + elif [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + lsof -nP -iTCP -sTCP:LISTEN -a -p $pid | awk 'NR>1 {print $9}' | sed 's/.*://' + else + echo "Unsupported OS" + return 1 + fi + } + + # Arrays to collect details of processes + local not_started=() + local started=() + + echo "Checking processes: $*" + # Iterate over each given process name + for process_name in "$@"; do + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o cmd=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done + + # Print information + if [[ ${#not_started[@]} -ne 0 ]]; then + echo "Not started processes:" + for process_name in "${not_started[@]}"; do + openim::log::error "Process $process_name is not started." + done + fi + + if [[ ${#started[@]} -ne 0 ]]; then + echo + echo "Started processes:" + for info in "${started[@]}"; do + echo "$info" + done + fi + + # Return status + if [[ ${#not_started[@]} -ne 0 ]]; then + openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' + return 1 + else + echo "" + openim::log::success "All processes are running." + return 0 + fi +} + +# openim::util::check_process_names docker-pr + +# The `openim::util::stop_services_on_ports` function stops services running on specified ports. +# It accepts multiple ports as arguments and performs the following: +# 1. Attempts to stop any services running on the specified ports. +# 2. Prints details of services successfully stopped and those that failed to stop. +# Usage: +# openim::util::stop_services_on_ports 8080 8081 8082 +# The function returns a status of 1 if any service couldn't be stopped. +openim::util::stop_services_on_ports() { + # An array to collect ports of processes that couldn't be stopped. + local not_stopped=() + + # An array to collect information about processes that were stopped. + local stopped=() + + echo "Stopping services on ports: $*" + # Iterate over each given port. + for port in "$@"; do + # Use the `lsof` command to find process information related to the given port. + info=$(lsof -i :$port -n -P | grep LISTEN || true) + + # If there's process information, it means the process associated with the port is running. + if [[ -n $info ]]; then + # Extract the Process ID. + while read -r line; do + local pid=$(echo $line | awk '{print $2}') + + # Try to stop the service by killing its process. + if kill -10 $pid; then + stopped+=($port) + else + not_stopped+=($port) + fi + done <<< "$info" + fi + done + + # Print information about ports whose processes couldn't be stopped. + if [[ ${#not_stopped[@]} -ne 0 ]]; then + echo "Ports that couldn't be stopped:" + for port in "${not_stopped[@]}"; do + openim::log::status "Failed to stop service on port $port." + done + fi + + # Print information about ports whose processes were successfully stopped. + if [[ ${#stopped[@]} -ne 0 ]]; then + for port in "${stopped[@]}"; do + echo "Successfully stopped service on port $port." + done + fi + + # If any of the processes couldn't be stopped, return a status of 1. + if [[ ${#not_stopped[@]} -ne 0 ]]; then + return 1 + else + openim::log::success "All specified services were stopped." + echo "" + return 0 + fi +} + + +# The `openim::util::stop_services_with_name` function stops services with specified names. +# It accepts multiple service names as arguments and performs the following: +# 1. Attempts to stop any services with the specified names. +# 2. Prints details of services successfully stopped and those that failed to stop. +# Usage: +# openim::util::stop_services_with_name nginx apache +# The function returns a status of 1 if any service couldn't be stopped. + +# sleep 333333& +# sleep 444444& +# ps -ef | grep "sleep" +# openim::util::stop_services_with_name "sleep 333333" "sleep 444444" + +# This figures out the host platform without relying on golang. We need this as +# we don't want a golang install to be a prerequisite to building yet we need +# this info to figure out where the final binaries are placed. +openim::util::host_platform() { + echo "$(openim::util::host_os)/$(openim::util::host_arch)" +} + +# looks for $1 in well-known output locations for the platform ($2) +# $OPENIM_ROOT must be set +openim::util::find-binary-for-platform() { + local -r lookfor="$1" + local -r platform="$2" + local locations=( + "${OPENIM_ROOT}/_output/bin/${lookfor}" + "${OPENIM_ROOT}/_output/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/local/bin/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/${platform}/${lookfor}" + "${OPENIM_ROOT}/_output/platforms/bin/${platform}/${lookfor}" + ) + + # List most recently-updated location. + local -r bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) + echo -n "${bin}" +} + +# looks for $1 in well-known output locations for the host platform +# $OPENIM_ROOT must be set +openim::util::find-binary() { + openim::util::find-binary-for-platform "$1" "$(openim::util::host_platform)" +} + +# Run all known doc generators (today gendocs and genman for openimctl) +# $1 is the directory to put those generated documents +openim::util::gen-docs() { + local dest="$1" + + # Find binary + gendocs=$(openim::util::find-binary "gendocs") + genopenimdocs=$(openim::util::find-binary "genopenimdocs") + genman=$(openim::util::find-binary "genman") + genyaml=$(openim::util::find-binary "genyaml") + genfeddocs=$(openim::util::find-binary "genfeddocs") + + # TODO: If ${genfeddocs} is not used from anywhere (it isn't used at + # least from k/k tree), remove it completely. + openim::util::sourced_variable "${genfeddocs}" + + mkdir -p "${dest}/docs/guide/en-US/cmd/openimctl/" + "${gendocs}" "${dest}/docs/guide/en-US/cmd/openimctl/" + + mkdir -p "${dest}/docs/guide/en-US/cmd/" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-api" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-cmdutils" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-crontask" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msggateway" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-msgtransfer" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-push" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-auth" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-conversation" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-friend" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-group" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-msg" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-third" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/" "openim-rpc-user" + "${genopenimdocs}" "${dest}/docs/guide/en-US/cmd/openimctl" "openimctl" + + mkdir -p "${dest}/docs/man/man1/" +"${genman}" "${dest}/docs/man/man1/" "openim-api" +"${genman}" "${dest}/docs/man/man1/" "openim-cmdutils" +"${genman}" "${dest}/docs/man/man1/" "openim-crontask" +"${genman}" "${dest}/docs/man/man1/" "openim-msggateway" +"${genman}" "${dest}/docs/man/man1/" "openim-msgtransfer" +"${genman}" "${dest}/docs/man/man1/" "openim-push" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-auth" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-conversation" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-friend" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-group" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-msg" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-third" +"${genman}" "${dest}/docs/man/man1/" "openim-rpc-user" + + mkdir -p "${dest}/docs/guide/en-US/yaml/openimctl/" + "${genyaml}" "${dest}/docs/guide/en-US/yaml/openimctl/" + + # create the list of generated files + pushd "${dest}" > /dev/null || return 1 + touch docs/.generated_docs + find . -type f | cut -sd / -f 2- | LC_ALL=C sort > docs/.generated_docs + popd > /dev/null || return 1 +} + +# Removes previously generated docs-- we don't want to check them in. $OPENIM_ROOT +# must be set. +openim::util::remove-gen-docs() { + if [ -e "${OPENIM_ROOT}/docs/.generated_docs" ]; then + # remove all of the old docs; we don't want to check them in. + while read -r file; do + rm "${OPENIM_ROOT}/${file}" 2>/dev/null || true + done <"${OPENIM_ROOT}/docs/.generated_docs" + # The docs/.generated_docs file lists itself, so we don't need to explicitly + # delete it. + fi +} + +# Returns the name of the upstream remote repository name for the local git +# repo, e.g. "upstream" or "origin". +openim::util::git_upstream_remote_name() { + git remote -v | grep fetch |\ + grep -E 'github.com[/:]openimsdk/open-im-server|openim.cc/server' |\ + head -n 1 | awk '{print $1}' +} + +# Exits script if working directory is dirty. If it's run interactively in the terminal +# the user can commit changes in a second terminal. This script will wait. +openim::util::ensure_clean_working_dir() { + while ! git diff HEAD --exit-code &>/dev/null; do + echo -e "\nUnexpected dirty working directory:\n" + if tty -s; then + git status -s + else + git diff -a # be more verbose in log files without tty + exit 1 + fi | sed 's/^/ /' + echo -e "\nCommit your changes in another terminal and then continue here by pressing enter." + read -r + done 1>&2 +} + +# Find the base commit using: +# $PULL_BASE_SHA if set (from Prow) +# current ref from the remote upstream branch +openim::util::base_ref() { + local -r git_branch=$1 + + if [[ -n ${PULL_BASE_SHA:-} ]]; then + echo "${PULL_BASE_SHA}" + return + fi + + full_branch="$(openim::util::git_upstream_remote_name)/${git_branch}" + + # make sure the branch is valid, otherwise the check will pass erroneously. + if ! git describe "${full_branch}" >/dev/null; then + # abort! + exit 1 + fi + + echo "${full_branch}" +} + +# Checks whether there are any files matching pattern $2 changed between the +# current branch and upstream branch named by $1. +# Returns 1 (false) if there are no changes +# 0 (true) if there are changes detected. +openim::util::has_changes() { + local -r git_branch=$1 + local -r pattern=$2 + local -r not_pattern=${3:-totallyimpossiblepattern} + + local base_ref + base_ref=$(openim::util::base_ref "${git_branch}") + echo "Checking for '${pattern}' changes against '${base_ref}'" + + # notice this uses ... to find the first shared ancestor + if git diff --name-only "${base_ref}...HEAD" | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + return 0 + fi + # also check for pending changes + if git status --porcelain | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then + echo "Detected '${pattern}' uncommitted changes." + return 0 + fi + echo "No '${pattern}' changes detected." + return 1 +} + +openim::util::download_file() { + local -r url=$1 + local -r destination_file=$2 + + rm "${destination_file}" 2&> /dev/null || true + + for i in $(seq 5) + do + if ! curl -fsSL --retry 3 --keepalive-time 2 "${url}" -o "${destination_file}"; then + echo "Downloading ${url} failed. $((5-i)) retries left." + sleep 1 + else + echo "Downloading ${url} succeed" + return 0 + fi + done + return 1 +} + +# Test whether openssl is installed. +# Sets: +# OPENSSL_BIN: The path to the openssl binary to use +function openim::util::test_openssl_installed { + if ! openssl version >& /dev/null; then + echo "Failed to run openssl. Please ensure openssl is installed" + exit 1 + fi + + OPENSSL_BIN=$(command -v openssl) +} + +# creates a client CA, args are sudo, dest-dir, ca-id, purpose +# purpose is dropped in after "key encipherment", you usually want +# '"client auth"' +# '"server auth"' +# '"client auth","server auth"' +function openim::util::create_signing_certkey { + local sudo=$1 + local dest_dir=$2 + local id=$3 + local purpose=$4 + # Create client ca + ${sudo} /usr/bin/env bash -e < "${dest_dir}/${id}-ca-config.json" +EOF +} + +# signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups... +function openim::util::create_client_certkey { + local sudo=$1 + local dest_dir=$2 + local ca=$3 + local id=$4 + local cn=${5:-$4} + local groups="" + local SEP="" + shift 5 + while [ -n "${1:-}" ]; do + groups+="${SEP}{\"O\":\"$1\"}" + SEP="," + shift 1 + done + ${sudo} /usr/bin/env bash -e < /dev/null +apiVersion: v1 +kind: Config +clusters: + - cluster: + certificate-authority: ${ca_file} + server: https://${api_host}:${api_port}/ + name: local-up-cluster +users: + - user: + token: ${token} + client-certificate: ${dest_dir}/client-${client_id}.crt + client-key: ${dest_dir}/client-${client_id}.key + name: local-up-cluster +contexts: + - context: + cluster: local-up-cluster + user: local-up-cluster + name: local-up-cluster +current-context: local-up-cluster +EOF + + # flatten the openimconfig files to make them self contained + username=$(whoami) + ${sudo} /usr/bin/env bash -e < "/tmp/${client_id}.openimconfig" + mv -f "/tmp/${client_id}.openimconfig" "${dest_dir}/${client_id}.openimconfig" + chown ${username} "${dest_dir}/${client_id}.openimconfig" +EOF +} + +# Determines if docker can be run, failures may simply require that the user be added to the docker group. +function openim::util::ensure_docker_daemon_connectivity { + IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}" + # Expand ${DOCKER[@]} only if it's not unset. This is to work around + # Bash 3 issue with unbound variable. + DOCKER=(docker ${DOCKER[@]:+"${DOCKER[@]}"}) + if ! "${DOCKER[@]}" info > /dev/null 2>&1 ; then + cat <<'EOF' >&2 +Can't connect to 'docker' daemon. please fix and retry. + +Possible causes: + - Docker Daemon not started + - Linux: confirm via your init system + - macOS w/ docker-machine: run `docker-machine ls` and `docker-machine start ` + - macOS w/ Docker for Mac: Check the menu bar and start the Docker application + - DOCKER_HOST hasn't been set or is set incorrectly + - Linux: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - macOS w/ docker-machine: run `eval "$(docker-machine env )"` + - macOS w/ Docker for Mac: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` + - Other things to check: + - Linux: User isn't in 'docker' group. Add and relogin. + - Something like 'sudo usermod -a -G docker ${USER}' + - RHEL7 bug and workaround: https://bugzilla.redhat.com/show_bug.cgi?id=1119282#c8 +EOF + return 1 + fi +} + +# Wait for background jobs to finish. Return with +# an error status if any of the jobs failed. +openim::util::wait-for-jobs() { + local fail=0 + local job + for job in $(jobs -p); do + wait "${job}" || fail=$((fail + 1)) + done + return ${fail} +} + +# openim::util::join +# Concatenates the list elements with the delimiter passed as first parameter +# +# Ex: openim::util::join , a b c +# -> a,b,c +function openim::util::join { + local IFS="$1" + shift + echo "$*" +} + +# Function: openim::util::list-to-string +# Description: Converts a list to a string, removing spaces, brackets, and commas. +# Example input: [1002 3 , 2 32 3 , 3 434 ,] +# Example output: 10023 2323 3434 +# Example usage: +# result=$(openim::util::list-to-string "[10023, 2323, 3434]") +# echo $result +function openim::util::list-to-string() { + # Capture all arguments into a single string + ports_list="$*" + + # Use sed for transformations: + # 1. Remove spaces + # 2. Replace commas with spaces + # 3. Remove opening and closing brackets + ports_array=$(echo "$ports_list" | sed 's/ //g; s/,/ /g; s/^\[\(.*\)\]$/\1/') + # For external use, we might want to echo the result so that it can be captured by callers + echo "$ports_array" +} +# MSG_GATEWAY_PROM_PORTS=$(openim::util::list-to-string "10023, 2323, 34 34") +# read -a MSG_GATEWAY_PROM_PORTS <<< $(openim::util::list-to-string "10023, 2323, 34 34") +# echo ${MSG_GATEWAY_PROM_PORTS} +# echo "${#MSG_GATEWAY_PROM_PORTS[@]}" +# Downloads cfssl/cfssljson/cfssl-certinfo into $1 directory if they do not already exist in PATH +# +# Assumed vars: +# $1 (cfssl directory) (optional) +# +# Sets: +# CFSSL_BIN: The path of the installed cfssl binary +# CFSSLJSON_BIN: The path of the installed cfssljson binary +# CFSSLCERTINFO_BIN: The path of the installed cfssl-certinfo binary +# +function openim::util::ensure-cfssl { + if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null && command -v cfssl-certinfo &>/dev/null; then + CFSSL_BIN=$(command -v cfssl) + CFSSLJSON_BIN=$(command -v cfssljson) + CFSSLCERTINFO_BIN=$(command -v cfssl-certinfo) + return 0 + fi + + host_arch=$(openim::util::host_arch) + + if [[ "${host_arch}" != "amd64" ]]; then + echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + + # Create a temp dir for cfssl if no directory was given + local cfssldir=${1:-} + if [[ -z "${cfssldir}" ]]; then + cfssldir="$HOME/bin" + fi + + mkdir -p "${cfssldir}" + pushd "${cfssldir}" > /dev/null || return 1 + + echo "Unable to successfully run 'cfssl' from ${PATH}; downloading instead..." + kernel=$(uname -s) + case "${kernel}" in + Linux) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 + ;; + Darwin) + curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64 + curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64 + curl --retry 10 -L -o cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_darwin-amd64 + ;; + *) + echo "Unknown, unsupported platform: ${kernel}." >&2 + echo "Supported platforms: Linux, Darwin." >&2 + exit 2 + esac + + chmod +x cfssl || true + chmod +x cfssljson || true + chmod +x cfssl-certinfo || true + + CFSSL_BIN="${cfssldir}/cfssl" + CFSSLJSON_BIN="${cfssldir}/cfssljson" + CFSSLCERTINFO_BIN="${cfssldir}/cfssl-certinfo" + if [[ ! -x ${CFSSL_BIN} || ! -x ${CFSSLJSON_BIN} || ! -x ${CFSSLCERTINFO_BIN} ]]; then + echo "Failed to download 'cfssl'." + echo "Please install cfssl, cfssljson and cfssl-certinfo and verify they are in \$PATH." + echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." + exit 1 + fi + popd > /dev/null || return 1 +} + +function openim::util::ensure-docker-buildx { + # podman returns 0 on `docker buildx version`, docker on `docker buildx`. One of them must succeed. + if docker buildx version >/dev/null 2>&1 || docker buildx >/dev/null 2>&1; then + return 0 + else + echo "ERROR: docker buildx not available. Docker 19.03 or higher is required with experimental features enabled" + exit 1 + fi +} + +# openim::util::ensure-bash-version +# Check if we are using a supported bash version +# +function openim::util::ensure-bash-version { + # shellcheck disable=SC2004 + if ((${BASH_VERSINFO[0]}<4)) || ( ((${BASH_VERSINFO[0]}==4)) && ((${BASH_VERSINFO[1]}<2)) ); then + echo "ERROR: This script requires a minimum bash version of 4.2, but got version of ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}" + if [ "$(uname)" = 'Darwin' ]; then + echo "On macOS with homebrew 'brew install bash' is sufficient." + fi + exit 1 + fi +} + +# openim::util::ensure-install-nginx +# Check if nginx is installed +# +function openim::util::ensure-install-nginx { + if ! command -v nginx &>/dev/null; then + echo "ERROR: nginx not found. Please install nginx." + exit 1 + fi + + for port in "80" + do + if echo |telnet 127.0.0.1 $port 2>&1|grep refused &>/dev/null;then + exit 1 + fi + done +} + +# openim::util::ensure-gnu-sed +# Determines which sed binary is gnu-sed on linux/darwin +# +# Sets: +# SED: The name of the gnu-sed binary +# +function openim::util::ensure-gnu-sed { + # NOTE: the echo below is a workaround to ensure sed is executed before the grep. + # see: https://github.com/openimrnetes/openimrnetes/issues/87251 + sed_help="$(LANG=C sed --help 2>&1 || true)" + if echo "${sed_help}" | grep -q "GNU\|BusyBox"; then + SED="sed" + elif command -v gsed &>/dev/null; then + SED="gsed" + else + openim::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 + return 1 + fi + openim::util::sourced_variable "${SED}" +} + +# openim::util::ensure-gnu-date +# Determines which date binary is gnu-date on linux/darwin +# +# Sets: +# DATE: The name of the gnu-date binary +# +function openim::util::ensure-gnu-date { + # NOTE: the echo below is a workaround to ensure date is executed before the grep. + date_help="$(LANG=C date --help 2>&1 || true)" + if echo "${date_help}" | grep -q "GNU\|BusyBox"; then + DATE="date" + elif command -v gdate &>/dev/null; then + DATE="gdate" + else + openim::log::error "Failed to find GNU date as date or gdate. If you are on Mac: brew install coreutils." >&2 + return 1 + fi + openim::util::sourced_variable "${DATE}" +} + +# openim::util::check-file-in-alphabetical-order +# Check that the file is in alphabetical order +# +function openim::util::check-file-in-alphabetical-order { + local failure_file="$1" + if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then + { + echo + echo "${failure_file} is not in alphabetical order. Please sort it:" + echo + echo " LC_ALL=C sort -o ${failure_file} ${failure_file}" + echo + } >&2 + false + fi +} + +# openim::util::require-jq +# Checks whether jq is installed. +function openim::util::require-jq { + if ! command -v jq &>/dev/null; then + openim::log::errexit "jq not found. Please install." 1>&2 + fi +} + +# openim::util::require-dig +# Checks whether dig is installed and provides installation instructions if it is not. +function openim::util::require-dig { + if ! command -v dig &>/dev/null; then + openim::log::error "Please install 'dig' to use this feature. OR Set the environment variable for OPENIM_IP" + openim::log::error "Installation instructions:" + openim::log::error " For Ubuntu/Debian: sudo apt-get install dnsutils" + openim::log::error " For CentOS/RedHat: sudo yum install bind-utils" + openim::log::error " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind" + openim::log::error " For Windows: Install BIND9 tools from https://www.isc.org/download/" + openim::log::error_exit "dig command not found." + fi + return 0 +} + +# outputs md5 hash of $1, works on macOS and Linux +function openim::util::md5() { + if which md5 >/dev/null 2>&1; then + md5 -q "$1" + else + md5sum "$1" | awk '{ print $1 }' + fi +} + +# openim::util::read-array +# Reads in stdin and adds it line by line to the array provided. This can be +# used instead of "mapfile -t", and is bash 3 compatible. +# +# Assumed vars: +# $1 (name of array to create/modify) +# +# Example usage: +# openim::util::read-array files < <(ls -1) +# +function openim::util::read-array { + local i=0 + unset -v "$1" + while IFS= read -r "$1[i++]"; do :; done + eval "[[ \${$1[--i]} ]]" || unset "$1[i]" # ensures last element isn't empty +} + +# Some useful colors. +if [[ -z "${color_start-}" ]]; then + declare -r color_start="\033[" + declare -r color_red="${color_start}0;31m" + declare -r color_yellow="${color_start}0;33m" + declare -r color_green="${color_start}0;32m" + declare -r color_blue="${color_start}1;34m" + declare -r color_cyan="${color_start}1;36m" + declare -r color_norm="${color_start}0m" + + openim::util::sourced_variable "${color_start}" + openim::util::sourced_variable "${color_red}" + openim::util::sourced_variable "${color_yellow}" + openim::util::sourced_variable "${color_green}" + openim::util::sourced_variable "${color_blue}" + openim::util::sourced_variable "${color_cyan}" + openim::util::sourced_variable "${color_norm}" +fi + +# ex: ts=2 sw=2 et filetype=sh + +function openim::util::desc() { + openim::util:run::maybe_first_prompt + rate=25 + if [ -n "$DEMO_RUN_FAST" ]; then + rate=1000 + fi + echo "$blue# $@$reset" | pv -qL $rate + openim::util:run::prompt +} + +function openim::util:run::prompt() { + echo -n "${yellow}\$ ${reset}" +} + +started="" +function openim::util:run::maybe_first_prompt() { + if [ -z "$started" ]; then + openim::util:run::prompt + started=true + fi +} + +# After a `run` this variable will hold the stdout of the command that was run. +# If the command was interactive, this will likely be garbage. +DEMO_RUN_STDOUT="" + +function openim::util::run() { + openim::util:run::maybe_first_prompt + rate=25 + if [ -n "$DEMO_RUN_FAST" ]; then + rate=1000 + fi + echo "${green}$1${reset}" | pv -qL "$rate" + if [ -n "$DEMO_RUN_FAST" ]; then + sleep 0.5 + fi + OFILE="$(mktemp -t $(basename $0).XXXXXX)" + if [ "$(uname)" == "Darwin" ]; then + script -q "$OFILE" $1 + else + script -eq -c "$1" -f "$OFILE" + fi + r=$? + read -d '' -t "${timeout}" -n 10000 # clear stdin + openim::util:run::prompt + if [ -z "$DEMO_AUTO_RUN" ]; then + read -s + fi + DEMO_RUN_STDOUT="$(tail -n +2 $OFILE | sed 's/\r//g')" + return $r +} + +function openim::util::run::relative() { + for arg; do + echo "$(realpath $(dirname $(which $0)))/$arg" | sed "s|$(realpath $(pwd))|.|" + done +} + +# This function retrieves the IP address of the current server. +# It primarily uses the `curl` command to fetch the public IP address from ifconfig.me. +# If curl or the service is not available, it falls back +# to the internal IP address provided by the hostname command. +# TODO: If a delay is found, the delay needs to be addressed +function openim::util::get_server_ip() { + # Check if the 'curl' command is available + if command -v curl &> /dev/null; then + # Try to retrieve the public IP address using curl and ifconfig.me + IP=$(dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | sed 's/"//g' | tr -d '\n') + + # Check if IP retrieval was successful + if [[ -z "$IP" ]]; then + # If not, get the internal IP address + IP=$(ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d'/' -f1 | head -n 1) + fi + else + # If curl is not available, get the internal IP address + IP=$(ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d'/' -f1 | head -n 1) + fi + + # Return the fetched IP address + echo "$IP" +} + +function openim::util::onCtrlC() { + echo -e "\n${t_reset}Ctrl+C Press it. It's exiting openim make init..." + exit 1 +} + +# Function Function: Remove Spaces in the string +function openim::util::remove_space() { + value=$* # 获取传入的参数 + result=$(echo $value | sed 's/ //g') # 去除空格 +} + +function openim::util::gencpu() { + # Check the system type + system_type=$(uname) + + if [[ "$system_type" == "Darwin" ]]; then + # macOS (using sysctl) + cpu_count=$(sysctl -n hw.ncpu) + elif [[ "$system_type" == "Linux" ]]; then + # Linux (using lscpu) + cpu_count=$(lscpu --parse | grep -E '^([^#].*,){3}[^#]' | sort -u | wc -l) + else + echo "Unsupported operating system: $system_type" + exit 1 + fi + echo $cpu_count +} + +function openim::util::set_max_fd() { + local desired_fd=$1 + local max_fd_limit + + # Check if we're not on cygwin or darwin. + if [ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "cygwin" ] && [ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "darwin" ]; then + # Try to get the hard limit. + max_fd_limit=$(ulimit -H -n) + if [ $? -eq 0 ]; then + # If desired_fd is 'maximum' or 'max', set it to the hard limit. + if [ "$desired_fd" = "maximum" ] || [ "$desired_fd" = "max" ]; then + desired_fd="$max_fd_limit" + fi + + # Check if desired_fd is less than or equal to max_fd_limit. + if [ "$desired_fd" -le "$max_fd_limit" ]; then + ulimit -n "$desired_fd" + if [ $? -ne 0 ]; then + echo "Warning: Could not set maximum file descriptor limit to $desired_fd" + fi + else + echo "Warning: Desired file descriptor limit ($desired_fd) is greater than the hard limit ($max_fd_limit)" + fi + else + echo "Warning: Could not query the maximum file descriptor hard limit." + fi + else + echo "Warning: Not attempting to modify file descriptor limit on Cygwin or Darwin." + fi +} + + +function openim::util::gen_os_arch() { + # Get the current operating system and architecture + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + # Select the repository home directory based on the operating system and architecture + if [[ "$OS" == "darwin" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="darwin/amd64" + else + REPO_DIR="darwin/386" + fi + elif [[ "$OS" == "linux" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="linux/amd64" + elif [[ "$ARCH" == "arm64" ]]; then + REPO_DIR="linux/arm64" + elif [[ "$ARCH" == "mips64" ]]; then + REPO_DIR="linux/mips64" + elif [[ "$ARCH" == "mips64le" ]]; then + REPO_DIR="linux/mips64le" + elif [[ "$ARCH" == "ppc64le" ]]; then + REPO_DIR="linux/ppc64le" + elif [[ "$ARCH" == "s390x" ]]; then + REPO_DIR="linux/s390x" + else + REPO_DIR="linux/386" + fi + elif [[ "$OS" == "windows" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="windows/amd64" + else + REPO_DIR="windows/386" + fi + else + echo -e "${RED_PREFIX}Unsupported OS: $OS${COLOR_SUFFIX}" + exit 1 + fi +} + +if [[ "$*" =~ openim::util:: ]];then + eval $* +fi + + openim::util:run::prompt +} + +function openim::util:run::prompt() { + echo -n "$yellow\$ $reset" +} + +started="" +function openim::util:run::maybe_first_prompt() { + if [ -z "$started" ]; then + openim::util:run::prompt + started=true + fi +} + +# After a `run` this variable will hold the stdout of the command that was run. +# If the command was interactive, this will likely be garbage. +DEMO_RUN_STDOUT="" + +function openim::util::run() { + openim::util:run::maybe_first_prompt + rate=25 + if [ -n "$DEMO_RUN_FAST" ]; then + rate=1000 + fi + echo "$green$1$reset" | pv -qL $rate + if [ -n "$DEMO_RUN_FAST" ]; then + sleep 0.5 + fi + OFILE="$(mktemp -t $(basename $0).XXXXXX)" + if [ "$(uname)" == "Darwin" ]; then + script -q "$OFILE" $1 + else + script -eq -c "$1" -f "$OFILE" + fi + r=$? + read -d '' -t "${timeout}" -n 10000 # clear stdin + openim::util:run::prompt + if [ -z "$DEMO_AUTO_RUN" ]; then + read -s + fi + DEMO_RUN_STDOUT="$(tail -n +2 $OFILE | sed 's/\r//g')" + return $r +} + +function openim::util::run::relative() { + for arg; do + echo "$(realpath $(dirname $(which $0)))/$arg" | sed "s|$(realpath $(pwd))|.|" + done +} + +# This function retrieves the IP address of the current server. +# It primarily uses the `curl` command to fetch the public IP address from ifconfig.me. +# If curl or the service is not available, it falls back +# to the internal IP address provided by the hostname command. +# TODO: If a delay is found, the delay needs to be addressed +function openim::util::get_server_ip() { + # Check if the 'curl' command is available + if command -v curl &> /dev/null; then + # Try to retrieve the public IP address using curl and ifconfig.me + IP=$(dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | sed 's/"//g' | tr -d '\n') + + # Check if IP retrieval was successful + if [[ -z "$IP" ]]; then + # If not, get the internal IP address + IP=$(ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d'/' -f1 | head -n 1) + fi + else + # If curl is not available, get the internal IP address + IP=$(ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d'/' -f1 | head -n 1) + fi + + # Return the fetched IP address + echo "$IP" +} + +function openim::util::onCtrlC() { + echo -e "\n${t_reset}Ctrl+C Press it. It's exiting openim make init..." + exit 1 +} + +# Function Function: Remove Spaces in the string +function openim::util::remove_space() { + value=$* # 获取传入的参数 + result=$(echo $value | sed 's/ //g') # 去除空格 +} + +function openim::util::gencpu() { + # Check the system type + system_type=$(uname) + + if [[ "$system_type" == "Darwin" ]]; then + # macOS (using sysctl) + cpu_count=$(sysctl -n hw.ncpu) + elif [[ "$system_type" == "Linux" ]]; then + # Linux (using lscpu) + cpu_count=$(lscpu --parse | grep -E '^([^#].*,){3}[^#]' | sort -u | wc -l) + else + echo "Unsupported operating system: $system_type" + exit 1 + fi + echo $cpu_count +} + +function openim::util::set_max_fd() { + local desired_fd=$1 + local max_fd_limit + + # Check if we're not on cygwin or darwin. + if [ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "cygwin" ] && [ "$(uname -s | tr '[:upper:]' '[:lower:]')" != "darwin" ]; then + # Try to get the hard limit. + max_fd_limit=$(ulimit -H -n) + if [ $? -eq 0 ]; then + # If desired_fd is 'maximum' or 'max', set it to the hard limit. + if [ "$desired_fd" = "maximum" ] || [ "$desired_fd" = "max" ]; then + desired_fd="$max_fd_limit" + fi + + # Check if desired_fd is less than or equal to max_fd_limit. + if [ "$desired_fd" -le "$max_fd_limit" ]; then + ulimit -n "$desired_fd" + if [ $? -ne 0 ]; then + echo "Warning: Could not set maximum file descriptor limit to $desired_fd" + fi + else + echo "Warning: Desired file descriptor limit ($desired_fd) is greater than the hard limit ($max_fd_limit)" + fi + else + echo "Warning: Could not query the maximum file descriptor hard limit." + fi + else + echo "Warning: Not attempting to modify file descriptor limit on Cygwin or Darwin." + fi +} + + +function openim::util::gen_os_arch() { + # Get the current operating system and architecture + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + # Select the repository home directory based on the operating system and architecture + if [[ "$OS" == "darwin" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="darwin/amd64" + else + REPO_DIR="darwin/386" + fi + elif [[ "$OS" == "linux" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="linux/amd64" + elif [[ "$ARCH" == "arm64" ]]; then + REPO_DIR="linux/arm64" + elif [[ "$ARCH" == "mips64" ]]; then + REPO_DIR="linux/mips64" + elif [[ "$ARCH" == "mips64le" ]]; then + REPO_DIR="linux/mips64le" + elif [[ "$ARCH" == "ppc64le" ]]; then + REPO_DIR="linux/ppc64le" + elif [[ "$ARCH" == "s390x" ]]; then + REPO_DIR="linux/s390x" + else + REPO_DIR="linux/386" + fi + elif [[ "$OS" == "windows" ]]; then + if [[ "$ARCH" == "x86_64" ]]; then + REPO_DIR="windows/amd64" + else + REPO_DIR="windows/386" + fi + else + echo -e "${RED_PREFIX}Unsupported OS: $OS${COLOR_SUFFIX}" + exit 1 + fi +} + + + +function openim::util::is_running_in_container() { + if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then + return 0 + else + return 1 + fi +} + + +function openim::util::check_process_names_for_stop() { + local all_stopped=true + for service in "${OPENIM_ALL_SERVICE_LIBRARIES[@]}"; do + + PIDS=$(pgrep -f "${service}") || PIDS="0" + if [ "$PIDS" = "0" ]; then + continue + fi + + + NUM_PROCESSES=$(echo "$PIDS" | wc -l | xargs) + if [ "$NUM_PROCESSES" -gt 0 ]; then + all_stopped=false + echo "Found $NUM_PROCESSES processes for ${service}" + for PID in $PIDS; do + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,cmd)\033[0m" + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,comm)\033[0m" + else + openim::log::error "Unsupported OS type: $OSTYPE" + fi + done + echo "Processes for ${service} have not been stopped properly. " "$NUM_PROCESSES" + fi + done + + if [ "$all_stopped" = true ]; then + openim::log::success "All processes have been stopped properly." + return 0 + fi + + return 1 +} + + + + +function openim::util::find_process_ports() { + local process_path="$1" + if [[ -z "$process_path" ]]; then + echo "Usage: find_process_ports /path/to/process" + return 1 + fi + + local protocol_ports="" + while read -r line; do + local port_protocol=($line) + local port=${port_protocol[0]##*:} + local protocol=${port_protocol[1]} + protocol_ports="${protocol_ports}${protocol} ${port}, " + + done < <(lsof -nP -iTCP -iUDP | grep LISTEN | grep "$(pgrep -f "$process_path")" | awk '{print $9, $8}') + + protocol_ports=${protocol_ports%, } + + if [[ -z "$protocol_ports" ]]; then + openim::log::colorless "$process_path is not listening on any ports" + else + openim::log::colorless "$process_path is listening on protocol & port: $protocol_ports" + fi +} + + + + + +function openim::util::find_ports_for_all_services() { + local services=("$@") + for service in "${services[@]}"; do + openim::util::find_process_ports "$service" + done +} + + + + + +if [[ "$*" =~ openim::util:: ]];then + eval $* +fi diff --git a/scripts-new/lib/version.sh b/scripts-new/lib/version.sh new file mode 100644 index 000000000..cb47136fb --- /dev/null +++ b/scripts-new/lib/version.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Version management helpers. These functions help to set, save and load the +# following variables: +# +# OPENIM_GIT_COMMIT - The git commit id corresponding to this +# source code. +# OPENIM_GIT_TREE_STATE - "clean" indicates no changes since the git commit id +# "dirty" indicates source code changes after the git commit id +# "archive" indicates the tree was produced by 'git archive' +# OPENIM_GIT_VERSION - "vX.Y" used to indicate the last release version. +# OPENIM_GIT_MAJOR - The major part of the version +# OPENIM_GIT_MINOR - The minor component of the version + +# Grovels through git to set a set of env variables. +# +# If OPENIM_GIT_VERSION_FILE, this function will load from that file instead of +# querying git. +openim::version::get_version_vars() { + if [[ -n ${OPENIM_GIT_VERSION_FILE-} ]]; then + openim::version::load_version_vars "${OPENIM_GIT_VERSION_FILE}" + return + fi + + # If the iamrnetes source was exported through git archive, then + # we likely don't have a git tree, but these magic values may be filled in. + # shellcheck disable=SC2016,SC2050 + # Disabled as we're not expanding these at runtime, but rather expecting + # that another tool may have expanded these and rewritten the source (!) + if [[ '$Format:%%$' == "%" ]]; then + OPENIM_GIT_COMMIT='$Format:%H$' + OPENIM_GIT_TREE_STATE="archive" + # When a 'git archive' is exported, the '$Format:%D$' below will look + # something like 'HEAD -> release-1.8, tag: v1.8.3' where then 'tag: ' + # can be extracted from it. + if [[ '$Format:%D$' =~ tag:\ (v[^ ,]+) ]]; then + OPENIM_GIT_VERSION="${BASH_REMATCH[1]}" + fi + fi + + local git=(git --work-tree "${OPENIM_ROOT}") + + if [[ -n ${OPENIM_GIT_COMMIT-} ]] || OPENIM_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then + if [[ -z ${OPENIM_GIT_TREE_STATE-} ]]; then + # Check if the tree is dirty. default to dirty + if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then + OPENIM_GIT_TREE_STATE="clean" + else + OPENIM_GIT_TREE_STATE="dirty" + fi + fi + + # Use git describe to find the version based on tags. + if [[ -n ${OPENIM_GIT_VERSION-} ]] || OPENIM_GIT_VERSION=$("${git[@]}" describe --tags --always --match='v*' 2>/dev/null); then + # This translates the "git describe" to an actual semver.org + # compatible semantic version that looks something like this: + # v1.1.0-alpha.0.6+84c76d1142ea4d + # + # TODO: We continue calling this "git version" because so many + # downstream consumers are expecting it there. + # + # These regexes are painful enough in sed... + # We don't want to do them in pure shell, so disable SC2001 + # shellcheck disable=SC2001 + DASHES_IN_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/[^-]//g") + if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then + # shellcheck disable=SC2001 + # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) + OPENIM_GIT_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") + elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then + # shellcheck disable=SC2001 + # We have distance to base tag (v1.1.0-1-gCommitHash) + OPENIM_GIT_VERSION=$(echo "${OPENIM_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") + fi + if [[ "${OPENIM_GIT_TREE_STATE}" == "dirty" ]]; then + # git describe --dirty only considers changes to existing files, but + # that is problematic since new untracked .go files affect the build, + # so use our idea of "dirty" from git status instead. + # TODO? + #OPENIM_GIT_VERSION+="-dirty" + : + fi + + # Try to match the "git describe" output to a regex to try to extract + # the "major" and "minor" versions and whether this is the exact tagged + # version or whether the tree is between two tagged versions. + if [[ "${OPENIM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then + OPENIM_GIT_MAJOR=${BASH_REMATCH[1]} + OPENIM_GIT_MINOR=${BASH_REMATCH[2]} + if [[ -n "${BASH_REMATCH[4]}" ]]; then + OPENIM_GIT_MINOR+="+" + fi + fi + + # If OPENIM_GIT_VERSION is not a valid Semantic Version, then refuse to build. + if ! [[ "${OPENIM_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "OPENIM_GIT_VERSION should be a valid Semantic Version. Current value: ${OPENIM_GIT_VERSION}" + echo "Please see more details here: https://semver.org" + exit 1 + fi + fi + fi +} + +# Saves the environment flags to $1 +openim::version::save_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in openim::version::save_version_vars" + return 1 + } + + cat <"${version_file}" +OPENIM_GIT_COMMIT='${OPENIM_GIT_COMMIT-}' +OPENIM_GIT_TREE_STATE='${OPENIM_GIT_TREE_STATE-}' +OPENIM_GIT_VERSION='${OPENIM_GIT_VERSION-}' +OPENIM_GIT_MAJOR='${OPENIM_GIT_MAJOR-}' +OPENIM_GIT_MINOR='${OPENIM_GIT_MINOR-}' +EOF +} + +# Loads up the version variables from file $1 +openim::version::load_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in openim::version::load_version_vars" + return 1 + } + + source "${version_file}" +} diff --git a/scripts-new/list-feature-tests.sh b/scripts-new/list-feature-tests.sh new file mode 100644 index 000000000..f48a7a7e3 --- /dev/null +++ b/scripts-new/list-feature-tests.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This script lists all of the [Feature:.+] tests in our e2e suite. +# +# 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 \ No newline at end of file diff --git a/scripts-new/make-rules/common.mk b/scripts-new/make-rules/common.mk new file mode 100644 index 000000000..f8537b6ca --- /dev/null +++ b/scripts-new/make-rules/common.mk @@ -0,0 +1,189 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for common tasks +# + +SHELL := /bin/bash +GO:=go +DIRS=$(shell ls) +DEBUG ?= 0 +GIT_TAG := $(shell git describe --exact-match --tags --abbrev=0 2> /dev/null || echo untagged) +GIT_COMMIT ?= $(shell git rev-parse --short HEAD || echo "0.0.0") +BUILD_DATE ?=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') # Blank error: date '+%FT %T %z':"buildDate":"2023-03-31T 20:05:43 +0800" + +# include the common makefile +COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*") + +# ROOT_DIR: root directory of the code base +ifeq ($(origin ROOT_DIR),undefined) +ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/../.. && pwd -P)) +endif + +# OUTPUT_DIR: The directory where the build output is stored. +ifeq ($(origin OUTPUT_DIR),undefined) +OUTPUT_DIR := $(ROOT_DIR)/_output +$(shell mkdir -p $(OUTPUT_DIR)) +endif + +# BIN_DIR: Directory where executable files are stored. +ifeq ($(origin BIN_DIR),undefined) +BIN_DIR := $(OUTPUT_DIR)/bin +$(shell mkdir -p $(BIN_DIR)) +endif + +# BIN_TOOLS_DIR: Directory where executable files are stored. +ifeq ($(origin BIN_TOOLS_DIR),undefined) +BIN_TOOLS_DIR := $(BIN_DIR)/tools +$(shell mkdir -p $(BIN_TOOLS_DIR)) +endif + +# LOGS_DIR: Directory where log files are stored. +ifeq ($(origin LOGS_DIR),undefined) +LOGS_DIR := $(OUTPUT_DIR)/logs +$(shell mkdir -p $(LOGS_DIR)) +endif + +# TOOLS_DIR: The directory where tools are stored for build and testing. +ifeq ($(origin TOOLS_DIR),undefined) +TOOLS_DIR := $(OUTPUT_DIR)/tools +$(shell mkdir -p $(TOOLS_DIR)) +endif + +# TMP_DIR: directory where temporary files are stored. +ifeq ($(origin TMP_DIR),undefined) +TMP_DIR := $(OUTPUT_DIR)/tmp +$(shell mkdir -p $(TMP_DIR)) +endif + +ifeq ($(origin VERSION), undefined) +# VERSION := $(shell git describe --tags --always --match='v*') +# git describe --tags --always --match="v*" --dirty +# VERSION := $(shell git describe --tags --always --match="v*" --dirty | sed 's/-/./g') #v2.3.3.631.g00abdc9b.dirty +VERSION := $(shell git describe --tags --always --match='v*') +# v2.3.3: git tag +endif + +# Check if the tree is dirty. default to dirty(maybe u should commit?) +GIT_TREE_STATE:="dirty" +ifeq (, $(shell git status --porcelain 2>/dev/null)) + GIT_TREE_STATE="clean" +endif +GIT_COMMIT:=$(shell git rev-parse HEAD) + +# Minimum test coverage +# can u use make cover COVERAGE=90 +ifeq ($(origin COVERAGE),undefined) +COVERAGE := 60 +endif + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# The OS must be linux when building docker images +# PLATFORMS ?= linux_amd64 linux_arm64 +# The OS can be linux/windows/darwin when building binaries +PLATFORMS ?= linux_s390x linux_mips64 linux_mips64le darwin_amd64 darwin_arm64 windows_amd64 linux_amd64 linux_arm64 linux_ppc64le # wasip1_wasm + +# set a specific PLATFORM, defaults to the host platform +ifeq ($(origin PLATFORM), undefined) + ifeq ($(origin GOARCH), undefined) + GOARCH := $(shell go env GOARCH) + endif + # Determine the host OS + GOOS := $(shell go env GOOS) + PLATFORM := $(GOOS)_$(GOARCH) + # Use the host OS and GOARCH as the default when building images + IMAGE_PLAT := $(PLATFORM) +else + # Parse the PLATFORM variable + GOOS := $(word 1, $(subst _, ,$(PLATFORM))) + GOARCH := $(word 2, $(subst _, ,$(PLATFORM))) + IMAGE_PLAT := $(PLATFORM) +endif + + +# Protobuf file storage path +APIROOT=$(ROOT_DIR)/pkg/proto + +# Linux command settings +# TODO: Whether you need to join utils? +FIND := find . ! -path './utils/*' ! -path './vendor/*' ! -path './third_party/*' ! -path './components/*' ! -path './logs/*' +XARGS := xargs -r --no-run-if-empty + +# Linux command settings-CODE DIRS Copyright +CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/config $(ROOT_DIR)/internal $(ROOT_DIR)/scripts $(ROOT_DIR)/test $(ROOT_DIR)/.github $(ROOT_DIR)/build $(ROOT_DIR)/tools $(ROOT_DIR)/deployments +FINDS := find $(CODE_DIRS) + +# Makefile settings: Select different behaviors by determining whether V option is set +ifndef V +MAKEFLAGS += --no-print-directory +endif + +# COMMA: Concatenate multiple strings to form a list of strings +COMMA := , +# SPACE: Used to separate strings +SPACE := +# SPACE: Replace multiple consecutive Spaces with a single space +SPACE += + +# ============================================================================== +# Makefile helper functions for common tasks + +# Help information for the makefile package +define makehelp + @printf "\n\033[1mUsage: make ...\033[0m\n\n\\033[1mTargets:\\033[0m\n\n" + @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-28s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' + @printf "\n\033[1m$$USAGE_OPTIONS\033[0m\n" +endef + +# Here are some examples of builds +define MAKEFILE_EXAMPLE +# make build BINS=openim-api Only a single openim-api binary is built. +# make -j (nproc) all Run tidy gen add-copyright format lint cover build concurrently. +# make gen Generate all necessary files. +# make release Build release binaries for all platforms. +# make verify-copyright Verify the license headers for all files. +# make install-deepcopy-gen Install deepcopy-gen tools if the license is missing. +# make build BINS=openim-api V=1 DEBUG=1 Build debug binaries for only openim-api. +# make multiarch -j PLATFORMS="linux_arm64 linux_amd64" V=1 Build binaries for both platforms. +# make image +endef +export MAKEFILE_EXAMPLE + +# Define all help functions @printf "\n\033[1mCurrent openim-api version information: $(shell openim-api version):\033[0m\n\n" +define makeallhelp + @printf "\n\033[1mMake example:\033[0m\n\n" + $(call MAKEFILE_EXAMPLE) + @printf "\n\033[1mAriables:\033[0m\n\n" + @echo " DEBUG: $(DEBUG)" + @echo " BINS: $(BINS)" + @echo " PLATFORMS: $(PLATFORMS)" + @echo " V: $(V)" +endef + +# Help information for other makefile packages +CUT_OFF?="---------------------------------------------------------------------------------" +HELP_NAME:=$(shell basename $(MAKEFILE_LIST)) +define smallhelp + @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-35s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' + @echo $(CUT_OFF) +endef \ No newline at end of file diff --git a/scripts-new/make-rules/copyright.mk b/scripts-new/make-rules/copyright.mk new file mode 100644 index 000000000..18b4dd709 --- /dev/null +++ b/scripts-new/make-rules/copyright.mk @@ -0,0 +1,63 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============================================================================== +# wget https://github.com/google/addlicense/releases/download/v1.0.0/addlicense_1.0.0_Linux_x86_64.tar.gz +# Makefile helper functions for copyright +# + +LICENSE_TEMPLATE ?= $(ROOT_DIR)/scripts/template/LICENSE_TEMPLATES + +## copyright.verify: Validate boilerplate headers for assign files +.PHONY: copyright.verify +copyright.verify: tools.verify.addlicense + @echo "===========> Validate boilerplate headers for assign files starting in the $(ROOT_DIR) directory" + @$(TOOLS_DIR)/addlicense -v -check -ignore **/test/** -ignore **pb** -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End of boilerplate headers check..." + +## copyright.add: Add the boilerplate headers for all files +.PHONY: copyright.add +copyright.add: tools.verify.addlicense + @echo "===========> Adding $(LICENSE_TEMPLATE) the boilerplate headers for all files" + @$(TOOLS_DIR)/addlicense -y $(shell date +"%Y") -ignore **pb** -v -c "OpenIM." -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End the copyright is added..." + +# Addlicense Flags: +# -c string +# copyright holder (default "Google LLC") +# -check +# check only mode: verify presence of license headers and exit with non-zero code if missing +# -f string +# license file +# -ignore value +# file patterns to ignore, for example: -ignore **/*.go -ignore vendor/** +# -l string +# license type: apache, bsd, mit, mpl (default "apache") +# -s Include SPDX identifier in license header. Set -s=only to only include SPDX identifier. +# -skip value +# [deprecated: see -ignore] file extensions to skip, for example: -skip rb -skip go +# -v verbose mode: print the name of the files that are modified or were skipped +# -y string +# copyright year(s) (default "2023") + +## copyright.advertise: Advertise the license of the project +.PHONY: copyright.advertise +copyright.advertise: + @chmod +x $(ROOT_DIR)/scripts/advertise.sh + @$(ROOT_DIR)/scripts/advertise.sh + +## copyright.help: Show copyright help +.PHONY: copyright.help +copyright.help: scripts/make-rules/copyright.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/make-rules/dependencies.mk b/scripts-new/make-rules/dependencies.mk new file mode 100644 index 000000000..1a896f1ff --- /dev/null +++ b/scripts-new/make-rules/dependencies.mk @@ -0,0 +1,41 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for dependencies +# + +.PHONY: dependencies.run +dependencies.run: dependencies.packages dependencies.tools + +.PHONY: dependencies.packages +dependencies.packages: + @$(GO) mod tidy + +.PHONY: dependencies.tools +dependencies.tools: dependencies.tools.blocker dependencies.tools.critical + +.PHONY: dependencies.tools.blocker +dependencies.tools.blocker: go.build.verify $(addprefix tools.verify., $(BLOCKER_TOOLS)) + +.PHONY: dependencies.tools.critical +dependencies.tools.critical: $(addprefix tools.verify., $(CRITICAL_TOOLS)) + +.PHONY: dependencies.tools.trivial +dependencies.tools.trivial: $(addprefix tools.verify., $(TRIVIAL_TOOLS)) + +## dependencies.help: Print help for dependencies targets +.PHONY: dependencies.help +dependencies.help: scripts/make-rules/dependencies.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/make-rules/gen.mk b/scripts-new/make-rules/gen.mk new file mode 100644 index 000000000..fba0132ff --- /dev/null +++ b/scripts-new/make-rules/gen.mk @@ -0,0 +1,100 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for generate necessary files and docs +# https://cloud.redhat.com/blog/kubernetes-deep-dive-code-generation-customresources +# ! The stock of code generated by `make gen` should be idempotent +# +# Questions about go mod instead of go path: https://github.com/kubernetes/kubernetes/issues/117181 +# ============================================================================== +# Makefile helper functions for generate necessary files +# + +## gen.init: Initialize openim server project ✨ +.PHONY: gen.init +gen.init: + @echo "===========> Initializing openim server project" + @${ROOT_DIR}/scripts/init-config.sh + +## gen.init-githooks: Initialize git hooks ✨ +.PHONY: gen.init-githooks +gen.init-githooks: + @echo "===========> Initializing git hooks" + @${ROOT_DIR}/scripts/init-githooks.sh + +## gen.run: Generate necessary files and docs ✨ +.PHONY: gen.run +#gen.run: gen.errcode gen.docgo +gen.run: gen.clean gen.errcode gen.docgo.doc + +## gen.errcode: Generate necessary files and docs ✨ +.PHONY: gen.errcode +gen.errcode: gen.errcode.code gen.errcode.doc + +## gen.errcode.code: Generate openim error code go source files ✨ +.PHONY: gen.errcode.code +gen.errcode.code: tools.verify.codegen + @echo "===========> Generating openim error code go source files" + @codegen -type=int ${ROOT_DIR}/internal/pkg/code + +## gen.errcode.doc: Generate openim error code markdown documentation ✨ +.PHONY: gen.errcode.doc +gen.errcode.doc: tools.verify.codegen + @echo "===========> Generating error code markdown documentation" + @codegen -type=int -doc \ + -output ${ROOT_DIR}/docs/guide/zh-CN/api/error_code_generated.md ${ROOT_DIR}/internal/pkg/code + +## gen.docgo: Generate missing doc.go for go packages ✨ +.PHONY: gen.ca.% +gen.ca.%: + $(eval CA := $(word 1,$(subst ., ,$*))) + @echo "===========> Generating CA files for $(CA)" + @${ROOT_DIR}/scripts/gencerts.sh generate-openim-cert $(OUTPUT_DIR)/cert $(CA) + +## gen.ca: Generate CA files for all certificates ✨ +.PHONY: gen.ca +gen.ca: $(addprefix gen.ca., $(CERTIFICATES)) + +## gen.docgo: Generate missing doc.go for go packages ✨ +.PHONY: gen.docgo.doc +gen.docgo.doc: + @echo "===========> Generating missing doc.go for go packages" + @${ROOT_DIR}/scripts/gendoc.sh + +## gen.docgo.check: Check if there are untracked doc.go files ✨ +.PHONY: gen.docgo.check +gen.docgo.check: gen.docgo.doc + @n="$$(git ls-files --others '*/doc.go' | wc -l)"; \ + if test "$$n" -gt 0; then \ + git ls-files --others '*/doc.go' | sed -e 's/^/ /'; \ + echo "$@: untracked doc.go file(s) exist in working directory" >&2 ; \ + false ; \ + fi + +## gen.docgo.add: Add untracked doc.go files to git index ✨ +.PHONY: gen.docgo.add +gen.docgo.add: + @git ls-files --others '*/doc.go' | $(XARGS) -- git add + +## gen.docgo: Generate missing doc.go for go packages ✨ +.PHONY: gen.clean +gen.clean: + @rm -rf ./api/client/{clientset,informers,listers} + @$(FIND) -type f -name '*_generated.go' -delete + +## gen.help: show help for gen +.PHONY: gen.help +gen.help: scripts/make-rules/gen.m + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/make-rules/golang.mk b/scripts-new/make-rules/golang.mk new file mode 100644 index 000000000..cfc3bf649 --- /dev/null +++ b/scripts-new/make-rules/golang.mk @@ -0,0 +1,272 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Build management helpers. These functions help to set, save and load the +# + +GO := go +GO_SUPPORTED_VERSIONS ?= 1.19|1.20|1.21|1.22|1.23 + +GO_LDFLAGS += -X $(VERSION_PACKAGE).gitVersion=$(GIT_TAG) \ + -X $(VERSION_PACKAGE).gitCommit=$(GIT_COMMIT) \ + -X $(VERSION_PACKAGE).gitTreeState=$(GIT_TREE_STATE) \ + -X $(VERSION_PACKAGE).buildDate=$(BUILD_DATE) \ + -s -w # -s -w deletes debugging information and symbol tables +ifeq ($(DEBUG), 1) + GO_BUILD_FLAGS += -gcflags "all=-N -l" + GO_LDFLAGS= +endif + +GO_BUILD_FLAGS += -ldflags "$(GO_LDFLAGS)" + +ifeq ($(GOOS),windows) + GO_OUT_EXT := .exe +endif + +ifeq ($(ROOT_PACKAGE),) + $(error the variable ROOT_PACKAGE must be set prior to including golang.mk, ->/Makefile) +endif + +GOPATH ?= $(shell go env GOPATH) +ifeq ($(origin GOBIN), undefined) + GOBIN := $(GOPATH)/bin +endif + +# COMMANDS is Specify all files under ${ROOT_DIR}/cmd/ and ${ROOT_DIR}/tools/ except those ending in.md +COMMANDS ?= $(filter-out %.md, $(wildcard ${ROOT_DIR}/cmd/* ${ROOT_DIR}/tools/* ${ROOT_DIR}/tools/data-conversion/chat/cmd/* ${ROOT_DIR}/tools/data-conversion/openim/cmd/* ${ROOT_DIR}/cmd/openim-rpc/*)) +ifeq (${COMMANDS},) + $(error Could not determine COMMANDS, set ROOT_DIR or run in source dir) +endif + +# BINS is the name of each file in ${COMMANDS}, excluding the directory path +# If there are no files in ${COMMANDS}, or if all files end in.md, ${BINS} will be empty +BINS ?= $(foreach cmd,${COMMANDS},$(notdir ${cmd})) +ifeq (${BINS},) + $(error Could not determine BINS, set ROOT_DIR or run in source dir) +endif + +ifeq ($(OS),Windows_NT) + NULL := + SPACE := $(NULL) $(NULL) + ROOT_DIR := $(subst $(SPACE),\$(SPACE),$(shell cd)) +else + ROOT_DIR := $(shell pwd) +endif + +ifeq ($(strip $(COMMANDS)),) + $(error Could not determine COMMANDS, set ROOT_DIR or run in source dir) +endif +ifeq ($(strip $(BINS)),) + $(error Could not determine BINS, set ROOT_DIR or run in source dir) +endif + +# TODO: EXCLUDE_TESTS variable, which contains the name of the package to be excluded from the test +EXCLUDE_TESTS=github.com/openimsdk/open-im-server/test github.com/openimsdk/open-im-server/v3/pkg/log github.com/openimsdk/open-im-server/db github.com/openimsdk/open-im-server/scripts github.com/openimsdk/open-im-server/config + +# ============================================================================== +# ❯ tree -L 1 cmd +# cmd +# ├── openim-sdk-core/ - main.go +# ├── openim-api +# ├── openim_cms_api +# ├── openim-crontask +# ├── openim_demo +# ├── openim-rpc-msg_gateway +# ├── openim-msgtransfer +# ├── openim-push +# ├── rpc/openim_admin_cms/ - main.go +# └── test/ - main.go +# COMMAND=openim +# PLATFORM=linux_amd64 +# OS=linux +# ARCH=amd64 +# BINS=openim-api openim_cms_api openim-crontask openim_demo openim-rpc-msg_gateway openim-msgtransfer openim-push +# BIN_DIR=/root/workspaces/OpenIM/_output/bin +# ============================================================================== + +## go.build: Build binaries +.PHONY: go.build +go.build: go.build.verify $(addprefix go.build., $(addprefix $(PLATFORM)., $(BINS))) + @echo "===========> Building binary $(BINS) $(VERSION) for $(PLATFORM)" + +## go.start: Start openim +.PHONY: go.start +go.start: + @echo "=========================> Starting OpenIM <=========================" + @$(ROOT_DIR)/scripts/start-all.sh + +## go.stop: Stop openim +.PHONY: go.stop +go.stop: + @echo "=========================> Stopping OpenIM <=========================" + @$(ROOT_DIR)/scripts/stop-all.sh + +## go.check: Check openim +.PHONY: go.check +go.check: + @echo "=========================> Checking OpenIM <=========================" + @$(ROOT_DIR)/scripts/check-all.sh + +## go.check-component: Check openim component +.PHONY: go.check-component +go.check-component: + @echo "=========================> Checking OpenIM component <=========================" + @$(ROOT_DIR)/scripts/install/openim-tools.sh openim::tools::pre-start + +## go.versionchecker: Design, detect some environment variables and versions +go.versionchecker: + @$(ROOT_DIR)/scripts/install/openim-tools.sh openim::tools::post-start + +## go.build.verify: Verify that a suitable version of Go exists +.PHONY: go.build.verify +go.build.verify: +ifneq ($(shell $(GO) version | grep -q -E '\bgo($(GO_SUPPORTED_VERSIONS))\b' && echo 0 || echo 1), 0) + $(error unsupported go version. Please make install one of the following supported version: '$(GO_SUPPORTED_VERSIONS)') +endif + +## go.build.%: Build binaries for a specific platform +# CGO_ENABLED=0 https://wiki.musl-libc.org/functional-differences-from-glibc.html +.PHONY: go.build.% +go.build.%: + $(eval COMMAND := $(word 2,$(subst ., ,$*))) + $(eval PLATFORM := $(word 1,$(subst ., ,$*))) + $(eval OS := $(word 1,$(subst _, ,$(PLATFORM)))) + $(eval ARCH := $(word 2,$(subst _, ,$(PLATFORM)))) + @echo "=====> COMMAND=$(COMMAND)" + @echo "=====> PLATFORM=$(PLATFORM)" + @echo "===========> Building binary $(COMMAND) $(VERSION) for $(OS)_$(ARCH)" + @mkdir -p $(BIN_DIR)/platforms/$(OS)/$(ARCH) + @if [ "$(COMMAND)" == "openim-sdk-core" ]; then \ + echo "===========> DEBUG: OpenIM-SDK-Core It is no longer supported for openim-server $(COMMAND)"; \ + elif [ -d $(ROOT_DIR)/cmd/openim-rpc/$(COMMAND) ]; then \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/cmd/openim-rpc/$(COMMAND)/main.go; \ + else \ + if [ -f $(ROOT_DIR)/cmd/$(COMMAND)/main.go ]; then \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_DIR)/platforms/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/cmd/$(COMMAND)/main.go; \ + elif [ -f $(ROOT_DIR)/tools/$(COMMAND)/$(COMMAND).go ]; then \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/tools/$(COMMAND)/$(COMMAND).go; \ + chmod +x $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT); \ + elif [ -f $(ROOT_DIR)/tools/data-conversion/openim/cmd/$(COMMAND)/$(COMMAND).go ]; then \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/tools/data-conversion/openim/cmd/$(COMMAND)/$(COMMAND).go; \ + chmod +x $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT); \ + elif [ -f $(ROOT_DIR)/tools/data-conversion/chat/cmd/$(COMMAND)/$(COMMAND).go ]; then \ + CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \ + $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/tools/data-conversion/chat/cmd/$(COMMAND)/$(COMMAND).go; \ + chmod +x $(BIN_TOOLS_DIR)/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT); \ + fi \ + fi + +## go.install: Install deployment openim +.PHONY: go.install +go.install: + @echo "===========> Installing deployment openim" + @$(ROOT_DIR)/scripts/install-im-server.sh + +## go.multiarch: Build multi-arch binaries +.PHONY: go.build.multiarch +go.build.multiarch: go.build.verify $(foreach p,$(PLATFORMS),$(addprefix go.build., $(addprefix $(p)., $(BINS)))) + +## go.lint: Run golangci to lint source codes +.PHONY: go.lint +go.lint: tools.verify.golangci-lint + @echo "===========> Run golangci to lint source codes" + @$(TOOLS_DIR)/golangci-lint run --color always -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... + +## go.test: Run unit test +.PHONY: go.test +go.test: + @$(GO) test ./... + +## go.test.api: Run api test +.PHONY: go.test.api +go.test.api: + @echo "===========> Run api test" + @$(ROOT_DIR)/scripts/install/test.sh openim::test::test + +## go.test.e2e: Run e2e test +.PHONY: go.test.e2e +go.test.e2e: tools.verify.ginkgo + @echo "===========> Run e2e test" + @$(TOOLS_DIR)/ginkgo -v $(ROOT_DIR)/test/e2e + +## go.demo: Run demo +.PHONY: go.demo +go.demo: + @echo "===========> Run demo" + @$(ROOT_DIR)/scripts/demo.sh + +## go.test.junit-report: Run unit test +.PHONY: go.test.junit-report +go.test.junit-report: tools.verify.go-junit-report + @touch $(TMP_DIR)/coverage.out + @echo "===========> Run unit test > $(TMP_DIR)/report.xml" +# @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 $(GO_BUILD_FLAGS) ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml + @$(GO) test -v -coverprofile=$(TMP_DIR)/coverage.out 2>&1 ./... | $(TOOLS_DIR)/go-junit-report -set-exit-code > $(TMP_DIR)/report.xml + @sed -i '/mock_.*.go/d' $(TMP_DIR)/coverage.out + @echo "===========> Test coverage of Go code is reported to $(TMP_DIR)/coverage.html by generating HTML" + @$(GO) tool cover -html=$(TMP_DIR)/coverage.out -o $(TMP_DIR)/coverage.html + +## go.test.cover: Run unit test with coverage +.PHONY: go.test.cover +go.test.cover: go.test.junit-report + @$(GO) tool cover -func=$(TMP_DIR)/coverage.out | \ + awk -v target=$(COVERAGE) -f $(ROOT_DIR)/scripts/coverage.awk + +## go.format: Run unit test and format codes +.PHONY: go.format +go.format: tools.verify.golines tools.verify.goimports + @echo "===========> Formatting codes" + @$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) gofmt -s -w + @$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) $(TOOLS_DIR)/goimports -w -local $(ROOT_PACKAGE) + @$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) $(TOOLS_DIR)/golines -w --max-len=200 --reformat-tags --shorten-comments --ignore-generated . + @$(GO) mod edit -fmt + +## go.imports: task to automatically handle import packages in Go files using goimports tool +.PHONY: go.imports +go.imports: tools.verify.goimports + @$(TOOLS_DIR)/goimports -l -w $(SRC) + +## go.verify: execute all verity scripts. +.PHONY: go.verify +go.verify: tools.verify.misspell + @echo "Starting verification..." + @scripts_list=$$(find $(ROOT_DIR)/scripts -type f -name 'verify-*' | sort); \ + for script in $$scripts_list; do \ + echo "Executing $$script..."; \ + $$script || exit 1; \ + echo "$$script completed successfully"; \ + done + @echo "All verification scripts executed successfully." + +## go.updates: Check for updates to go.mod dependencies +.PHONY: go.updates +go.updates: tools.verify.go-mod-outdated + @$(GO) list -u -m -json all | go-mod-outdated -update -direct + +## go.clean: Clean all builds directories and files +.PHONY: go.clean +go.clean: + @echo "===========> Cleaning all builds tmp, bin, logs directories and files" + @-rm -vrf $(TMP_DIR) $(BIN_DIR) $(BIN_TOOLS_DIR) $(LOGS_DIR) + @echo "===========> End clean..." + +## go.help: Show go tools help +.PHONY: go.help +go.help: scripts/make-rules/golang.mk + $(call smallhelp) diff --git a/scripts-new/make-rules/image.mk b/scripts-new/make-rules/image.mk new file mode 100644 index 000000000..eaec4a127 --- /dev/null +++ b/scripts-new/make-rules/image.mk @@ -0,0 +1,183 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for docker image +# ============================================================================== +# Path: scripts/make-rules/image.mk +# docker registry: registry.example.com/namespace/image:tag as: registry.hub.docker.com/cubxxw/: +# https://docs.docker.com/build/building/multi-platform/ +# + +DOCKER := docker + +# read: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md +REGISTRY_PREFIX ?= registry.cn-hangzhou.aliyuncs.com/openimsdk +# REGISTRY_PREFIX ?= ghcr.io/openimsdk + +BASE_IMAGE ?= ghcr.io/openim-sigs/openim-bash-image + +IMAGE_PLAT ?= $(subst $(SPACE),$(COMMA),$(subst _,/,$(PLATFORMS))) + +EXTRA_ARGS ?= --no-cache +_DOCKER_BUILD_EXTRA_ARGS := + +ifdef HTTP_PROXY +_DOCKER_BUILD_EXTRA_ARGS += --build-arg HTTP_PROXY=${HTTP_PROXY} +endif + +ifneq ($(EXTRA_ARGS), ) +_DOCKER_BUILD_EXTRA_ARGS += $(EXTRA_ARGS) +endif + +# Determine image files by looking into build/images/*/Dockerfile +IMAGES_DIR ?= $(wildcard ${ROOT_DIR}/build/images/*) +# Determine images names by stripping out the dir names, and filter out the undesired directories +# IMAGES ?= $(filter-out Dockerfile,$(foreach image,${IMAGES_DIR},$(notdir ${image}))) +IMAGES ?= $(filter-out Dockerfile openim-tools openim-rpc-extend-msg openim-rpc-encryption openim-cmdutils,$(foreach image,${IMAGES_DIR},$(notdir ${image}))) +# IMAGES ?= $(filter-out Dockerfile openim-tools openim-cmdutils,$(foreach image,${IMAGES_DIR},$(notdir ${image}))) # !pro + +ifeq (${IMAGES},) + $(error Could not determine IMAGES, set ROOT_DIR or run in source dir) +endif + +# ============================================================================== +# Image targets +# ============================================================================== + +# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ +# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) +# To properly provided solutions that supports more than one platform you should use this option. +## image.docker-buildx: Build and push docker image for the manager for cross-platform support +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile +.PHONY: image.docker-buildx +image.docker-buildx: + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name project-v3-builder + $(CONTAINER_TOOL) buildx use project-v3-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMAGES} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm project-v3-builder + rm Dockerfile.cross + +## image.verify: Verify docker version +.PHONY: image.verify +image.verify: + @$(ROOT_DIR)/scripts/lib/util.sh openim::util::check_docker_and_compose_versions + +## image.daemon.verify: Verify docker daemon experimental features +.PHONY: image.daemon.verify +image.daemon.verify: + @$(ROOT_DIR)/scripts/lib/util.sh openim::util::ensure_docker_daemon_connectivity + @$(ROOT_DIR)/scripts/lib/util.sh openim::util::ensure-docker-buildx + +# If you wish built the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +## image.build: Build docker images +.PHONY: image.build +image.build: image.verify $(addprefix image.build., $(addprefix $(PLATFORM)., $(IMAGES))) + +.PHONY: image.build.multiarch +image.build.multiarch: image.verify $(foreach p,$(PLATFORMS),$(addprefix image.build., $(addprefix $(p)., $(IMAGES)))) + +## image.build.%: Build docker image for a specific platform +.PHONY: image.build.% +image.build.%: go.build.% + $(eval IMAGE := $(COMMAND)) + $(eval IMAGE_PLAT := $(subst _,/,$(PLATFORM))) + $(eval ARCH := $(word 2,$(subst _, ,$(PLATFORM)))) + @echo "===========> Building docker image $(IMAGE) $(VERSION) for $(IMAGE_PLAT)" + @mkdir -p $(TMP_DIR)/$(IMAGE)/$(PLATFORM) + @cat $(ROOT_DIR)/build/images/Dockerfile\ + | sed "s#BASE_IMAGE#$(BASE_IMAGE)#g" \ + | sed "s#BINARY_NAME#$(IMAGE)#g" >$(TMP_DIR)/$(IMAGE)/Dockerfile + @cp $(BIN_DIR)/platforms/$(IMAGE_PLAT)/$(IMAGE) $(TMP_DIR)/$(IMAGE) + $(eval BUILD_SUFFIX := $(_DOCKER_BUILD_EXTRA_ARGS) --pull -t $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) $(TMP_DIR)/$(IMAGE)) + @echo $(DOCKER) build --platform $(IMAGE_PLAT) $(BUILD_SUFFIX) + @if [ $(shell $(GO) env GOARCH) != $(ARCH) ] ; then \ + $(MAKE) image.daemon.verify ;\ + $(DOCKER) build --platform $(IMAGE_PLAT) $(BUILD_SUFFIX) ; \ + else \ + $(DOCKER) build $(BUILD_SUFFIX) ; \ + fi + @rm -rf $(TMP_DIR)/$(IMAGE) + +# https://docs.docker.com/build/building/multi-platform/ +# busybox image supports amd64, arm32v5, arm32v6, arm32v7, arm64v8, i386, ppc64le, and s390x +## image.buildx.%: Build docker images with buildx +.PHONY: image.buildx.% +image.buildx.%: + $(eval IMAGE := $(word 1,$(subst ., ,$*))) + echo "===========> Building docker image $(IMAGE) $(VERSION)" + $(DOCKER) buildx build -f $(ROOT_DIR)/Dockerfile --pull --no-cache --platform=$(PLATFORMS) --push . -t $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + +## image.push: Push docker images +.PHONY: image.push +image.push: image.verify go.build.verify $(addprefix image.push., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) + +## image.push.multiarch: Push docker images for all platforms +.PHONY: image.push.multiarch +image.push.multiarch: image.verify go.build.verify $(foreach p,$(PLATFORMS),$(addprefix image.push., $(addprefix $(p)., $(IMAGES)))) + +## image.push.%: Push docker image for a specific platform +.PHONY: image.push.% +image.push.%: image.build.% + @echo "===========> Pushing image $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX)" + $(DOCKER) push $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + +## image.manifest.push: Push manifest list for multi-arch images +.PHONY: image.manifest.push +image.manifest.push: export DOCKER_CLI_EXPERIMENTAL := enabled +image.manifest.push: image.verify go.build.verify \ +$(addprefix image.manifest.push., $(addprefix $(IMAGE_PLAT)., $(IMAGES))) + +## image.manifest.push.%: Push manifest list for multi-arch images for a specific platform +.PHONY: image.manifest.push.% +image.manifest.push.%: image.push.% image.manifest.remove.% + @echo "===========> Pushing manifest $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" + @$(DOCKER) manifest create $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ + $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) + @$(DOCKER) manifest annotate $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ + $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) \ + --os $(OS) --arch ${ARCH} + @$(DOCKER) manifest push --purge $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) + +# Docker cli has a bug: https://github.com/docker/cli/issues/954 +# If you find your manifests were not updated, +# Please manually delete them in $HOME/.docker/manifests/ +# and re-run. +## image.manifest.remove.%: Remove local manifest list +.PHONY: image.manifest.remove.% +image.manifest.remove.%: + @rm -rf ${HOME}/.docker/manifests/docker.io_$(REGISTRY_PREFIX)_$(IMAGE)-$(VERSION) + +## image.manifest.push.multiarch: Push manifest list for multi-arch images for all platforms +.PHONY: image.manifest.push.multiarch +image.manifest.push.multiarch: image.push.multiarch $(addprefix image.manifest.push.multiarch., $(IMAGES)) + +## image.manifest.push.multiarch.%: Push manifest list for multi-arch images for all platforms for a specific image +.PHONY: image.manifest.push.multiarch.% +image.manifest.push.multiarch.%: + @echo "===========> Pushing manifest $* $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" + REGISTRY_PREFIX=$(REGISTRY_PREFIX) PLATFORMS="$(PLATFORMS)" IMAGE=$* VERSION=$(VERSION) DOCKER_CLI_EXPERIMENTAL=enabled \ + $(ROOT_DIR)/build/lib/create-manifest.sh + +## image.help: Print help for image targets +.PHONY: image.help +image.help: scripts/make-rules/image.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/make-rules/release.mk b/scripts-new/make-rules/release.mk new file mode 100644 index 000000000..68bf05898 --- /dev/null +++ b/scripts-new/make-rules/release.mk @@ -0,0 +1,42 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for release +# Versions are used after merging +# + +## release.run: release the project +.PHONY: release.run +release.run: release.verify release.ensure-tag + @scripts/release.sh + +## release.verify: Check if a tool is installed and install it +.PHONY: release.verify +release.verify: tools.verify.git-chglog tools.verify.github-release tools.verify.coscmd tools.verify.coscli + +## release.tag: release the project +.PHONY: release.tag +release.tag: tools.verify.gsemver release.ensure-tag + @git push origin `git describe --tags --abbrev=0` + +## release.ensure-tag: ensure tag +.PHONY: release.ensure-tag +release.ensure-tag: tools.verify.gsemver + @scripts/ensure-tag.sh + +## release.help: Display help information about the release package +.PHONY: release.help +release.help: scripts/make-rules/release.mk + $(call smallhelp) diff --git a/scripts-new/make-rules/swagger.mk b/scripts-new/make-rules/swagger.mk new file mode 100644 index 000000000..991de43c6 --- /dev/null +++ b/scripts-new/make-rules/swagger.mk @@ -0,0 +1,19 @@ +# ============================================================================== +# Makefile helper functions for swagger +# + +## swagger.run: Generate swagger document. +.PHONY: swagger.run +swagger.run: tools.verify.swagger + @echo "===========> Generating swagger API docs" + @$(TOOLS_DIR)/swagger generate spec --scan-models -w $(ROOT_DIR)/cmd/genswaggertypedocs -o $(ROOT_DIR)/api/swagger/swagger.yaml + +## swagger.serve: Serve swagger spec and docs. +.PHONY: swagger.serve +swagger.serve: tools.verify.swagger + @$(TOOLS_DIR)/swagger serve -F=redoc --no-open --port 36666 $(ROOT_DIR)/api/swagger/swagger.yaml + +## swagger.help: Display help information about the release package +.PHONY: swagger.help +swagger.help: scripts/make-rules/swagger.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/make-rules/tools.mk b/scripts-new/make-rules/tools.mk new file mode 100644 index 000000000..917c18cfe --- /dev/null +++ b/scripts-new/make-rules/tools.mk @@ -0,0 +1,254 @@ +# Copyright © 2023 OpenIMSDK. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ============================================================================== +# Makefile helper functions for tools(https://github.com/avelino/awesome-go) -> DIR: {TOOT_DIR}/tools | (go >= 1.19) +# Why download to the tools directory, thinking we might often switch Go versions using gvm. +# + +# openim build use BUILD_TOOLS +BUILD_TOOLS ?= golangci-lint goimports addlicense deepcopy-gen conversion-gen ginkgo go-junit-report go-gitlint +# Code analysis tools +ANALYSIS_TOOLS = golangci-lint goimports golines go-callvis kube-score +# Code generation tools +GENERATION_TOOLS = deepcopy-gen conversion-gen protoc-gen-go cfssl rts codegen +# Testing tools +TEST_TOOLS = ginkgo go-junit-report gotests +# tenxun cos tools +COS_TOOLS = coscli coscmd +# Version control tools +VERSION_CONTROL_TOOLS = addlicense go-gitlint git-chglog github-release gsemver +# Utility tools +UTILITY_TOOLS = go-mod-outdated mockgen gothanks richgo kubeconform +# All tools +ALL_TOOLS ?= $(ANALYSIS_TOOLS) $(GENERATION_TOOLS) $(TEST_TOOLS) $(VERSION_CONTROL_TOOLS) $(UTILITY_TOOLS) $(COS_TOOLS) + +## tools.install: Install a must tools +.PHONY: tools.install +tools.install: $(addprefix tools.verify., $(BUILD_TOOLS)) + +## tools.install-all: Install all tools +.PHONY: tools.install-all +tools.install-all: $(addprefix tools.install-all., $(ALL_TOOLS)) + +## tools.install.%: Install a single tool in $GOBIN/ +.PHONY: tools.install.% +tools.install.%: + @echo "===========> Installing $,The default installation path is $(GOBIN)/$*" + @$(MAKE) install.$* + +## tools.install-all.%: Parallelism install a single tool in ./tools/* +.PHONY: tools.install-all.% +tools.install-all.%: + @echo "===========> Installing $,The default installation path is $(TOOLS_DIR)/$*" + @$(MAKE) -j $(nproc) install.$* + +## tools.verify.%: Check if a tool is installed and install it +.PHONY: tools.verify.% +tools.verify.%: + @echo "===========> Verifying $* is installed" + @if [ ! -f $(TOOLS_DIR)/$* ]; then GOBIN=$(TOOLS_DIR) $(MAKE) tools.install.$*; fi + @echo "===========> $* is install in $(TOOLS_DIR)/$*" + +## install.golangci-lint: Install golangci-lint +.PHONY: install.golangci-lint +install.golangci-lint: + @$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +## install.goimports: Install goimports, used to format go source files +.PHONY: install.goimports +install.goimports: + @$(GO) install golang.org/x/tools/cmd/goimports@latest + +## install.addlicense: Install addlicense, used to add license header to source files +.PHONY: install.addlicense +install.addlicense: + @$(GO) install github.com/google/addlicense@latest + +## install.deepcopy-gen: Install deepcopy-gen, used to generate deep copy functions +.PHONY: install.deepcopy-gen +install.deepcopy-gen: + @$(GO) install k8s.io/code-generator/cmd/deepcopy-gen@latest + +## install.conversion-gen: Install conversion-gen, used to generate conversion functions +.PHONY: install.conversion-gen +install.conversion-gen: + @$(GO) install k8s.io/code-generator/cmd/conversion-gen@latest + +## install.ginkgo: Install ginkgo to run a single test or set of tests +.PHONY: install.ginkgo +install.ginkgo: + @$(GO) install github.com/onsi/ginkgo/ginkgo@v1.16.2 + +## Install go-gitlint: Install go-gitlint, used to check git commit message +.PHONY: install.go-gitlint +install.go-gitlint: + @$(GO) install github.com/marmotedu/go-gitlint/cmd/go-gitlint@latest + +## install.go-junit-report: Install go-junit-report, used to convert go test output to junit xml +.PHONY: install.go-junit-report +install.go-junit-report: + @$(GO) install github.com/jstemmer/go-junit-report@latest + +## install.gotests: Install gotests, used to generate go tests +.PHONY: install.swagger +install.swagger: + @$(GO) install github.com/go-swagger/go-swagger/cmd/swagger@latest + +# ============================================================================== +# Tools that might be used include go gvm +# + +## install.kube-score: Install kube-score, used to check kubernetes yaml files +.PHONY: install.kube-score +install.kube-score: + @$(GO) install github.com/zegl/kube-score/cmd/kube-score@latest + +## install.kubeconform: Install kubeconform, used to check kubernetes yaml files +.PHONY: install.kubeconform +install.kubeconform: + @$(GO) install github.com/yannh/kubeconform/cmd/kubeconform@latest + +## install.gsemver: Install gsemver, used to generate semver +.PHONY: install.gsemver +install.gsemver: + @$(GO) install github.com/arnaud-deprez/gsemver@latest + +## install.git-chglog: Install git-chglog, used to generate changelog +.PHONY: install.git-chglog +install.git-chglog: + @$(GO) install github.com/git-chglog/git-chglog/cmd/git-chglog@latest + +## install.ko: Install ko, used to build go program into container images +.PHONY: install.ko +install.ko: + @$(GO) install github.com/google/ko@latest + +## install.github-release: Install github-release, used to create github release +.PHONY: install.github-release +install.github-release: + @$(GO) install github.com/github-release/github-release@latest + +## install.coscli: Install coscli, used to upload files to cos +# example: ./coscli cp/sync -r /home/off-line/docker-off-line/ cos://openim-1306374445/openim/image/amd/off-line/off-line/ -e cos.ap-guangzhou.myqcloud.com +# https://cloud.tencent.com/document/product/436/71763 +# amd64 +.PHONY: install.coscli +install.coscli: + @wget -q https://github.com/tencentyun/coscli/releases/download/v0.19.0-beta/coscli-linux -O ${TOOLS_DIR}/coscli + @chmod +x ${TOOLS_DIR}/coscli + +## install.coscmd: Install coscmd, used to upload files to cos +.PHONY: install.coscmd +install.coscmd: + @if which pip &>/dev/null; then pip install coscmd; else pip3 install coscmd; fi + +## install.minio: Install minio, used to upload files to minio +.PHONY: install.minio +install.minio: + @$(GO) install github.com/minio/minio@latest + +## install.delve: Install delve, used to debug go program +.PHONY: install.delve +install.delve: + @$(GO) install github.com/go-delve/delve/cmd/dlv@latest + +## install.air: Install air, used to hot reload go program +.PHONY: install.air +install.air: + @$(GO) install github.com/cosmtrek/air@latest + +## install.gvm: Install gvm, gvm is a Go version manager, built on top of the official go tool. +# github: https://github.com/moovweb/gvm +.PHONY: install.gvm +install.gvm: + @echo "===========> Installing gvm,The default installation path is ~/.gvm/scripts/gvm" + @bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) + @$(shell source /root/.gvm/scripts/gvm) + +## install.golines: Install golines, used to format long lines +.PHONY: install.golines +install.golines: + @$(GO) install github.com/segmentio/golines@latest + +## install.go-mod-outdated: Install go-mod-outdated, used to check outdated dependencies +.PHONY: install.go-mod-outdated +install.go-mod-outdated: + @$(GO) install github.com/psampaz/go-mod-outdated@latest + +## install.mockgen: Install mockgen, used to generate mock functions +.PHONY: install.mockgen +install.mockgen: + @$(GO) install github.com/golang/mock/mockgen@latest + +## install.gotests: Install gotests, used to generate test functions +.PHONY: install.gotests +install.gotests: + @$(GO) install github.com/cweill/gotests/gotests@latest + +## install.protoc-gen-go: Install protoc-gen-go, used to generate go source files from protobuf files +.PHONY: install.protoc-gen-go +install.protoc-gen-go: + @$(GO) install github.com/golang/protobuf/protoc-gen-go@latest + +## install.cfssl: Install cfssl, used to generate certificates +.PHONY: install.cfssl +install.cfssl: + @$(ROOT_DIR)/scripts/install/install.sh openim::install::install_cfssl + +## install.depth: Install depth, used to check dependency tree +.PHONY: install.depth +install.depth: + @$(GO) install github.com/KyleBanks/depth/cmd/depth@latest + +## install.go-callvis: Install go-callvis, used to visualize call graph +.PHONY: install.go-callvis +install.go-callvis: + @$(GO) install github.com/ofabry/go-callvis@latest + +## install.misspell +.PHONY: install.misspell +install.misspell: + @$(GO) install github.com/client9/misspell/cmd/misspell@latest + +## install.gothanks: Install gothanks, used to thank go dependencies +.PHONY: install.gothanks +install.gothanks: + @$(GO) install github.com/psampaz/gothanks@latest + +## install.richgo: Install richgo +.PHONY: install.richgo +install.richgo: + @$(GO) install github.com/kyoh86/richgo@latest + +## install.rts: Install rts +.PHONY: install.rts +install.rts: + @$(GO) install github.com/galeone/rts/cmd/rts@latest + +# ================= kubecub openim tools ========================================= +## install.typecheck: install kubecub typecheck check for go code +.PHONY: install.typecheck +install.typecheck: + @$(GO) install github.com/kubecub/typecheck@latest + +## install.comment-lang-detector: install kubecub comment-lang-detector check for go code comment language +.PHONY: install.comment-lang-detector +install.comment-lang-detector: + @$(GO) install github.com/kubecub/comment-lang-detector/cmd/cld@latest + +## tools.help: Display help information about the tools package +.PHONY: tools.help +tools.help: scripts/make-rules/tools.mk + $(call smallhelp) \ No newline at end of file diff --git a/scripts-new/mongo-init.sh b/scripts-new/mongo-init.sh new file mode 100644 index 000000000..41d9ca0aa --- /dev/null +++ b/scripts-new/mongo-init.sh @@ -0,0 +1,30 @@ +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +mongosh <`. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# This sets up a clean GOPATH and makes sure we are currently in it. +openim::golang::setup_env + +# Run the user-provided command. +"${@}" diff --git a/scripts-new/start-all.sh b/scripts-new/start-all.sh new file mode 100644 index 000000000..3b95dace1 --- /dev/null +++ b/scripts-new/start-all.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#FIXME This script is the startup script for multiple servers. +#FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/install/common.sh" + +# Function to execute the scripts. +function execute_start_scripts() { + for script_path in "${OPENIM_SERVER_SCRIPT_START_LIST[@]}"; do + # Extract the script name without extension for argument generation. + script_name_with_prefix=$(basename "$script_path" .sh) + + # Remove the "openim-" prefix. + script_name=${script_name_with_prefix#openim-} + + # Construct the argument based on the script name. + arg="openim::${script_name}::start" + + # Check if the script file exists and is executable. + if [[ -x "$script_path" ]]; then + openim::log::colorless "Starting script: ${script_path##*/}" # Log the script name. + # Execute the script with the constructed argument. + result=$("$script_path" "$arg") + if [[ $? -ne 0 ]]; then + openim::log::error "Start script: ${script_path##*/} failed" + openim::log::error "$result" + return 1 + fi + + else + openim::log::errexit "Script ${script_path##*/} is missing or not executable." + return 1 + fi + done +} + +if openim::util::is_running_in_container; then + exec >> ${DOCKER_LOG_FILE} 2>&1 +fi + +openim::golang::check_openim_binaries +if [[ $? -ne 0 ]]; then + openim::log::error "OpenIM binaries are not found. Please run 'make build' to build binaries." + "${OPENIM_ROOT}"/scripts/build-all-service.sh +fi + +"${OPENIM_ROOT}"/scripts/init-config.sh --skip + +#openim::log::print_blue "Execute the following script in sequence: ${OPENIM_SERVER_SCRIPTARIES[@]}" + +# TODO Prelaunch tools, simple for now, can abstract functions later +TOOLS_START_SCRIPTS_PATH=${START_SCRIPTS_PATH}/openim-tools.sh + +openim::log::status "Start the pre-start tools:" + +# if ! ${TOOLS_START_SCRIPTS_PATH} openim::tools::pre-start; then +# openim::log::error "Start the pre-start tools, aborting!" +# exit 1 +# fi + + +openim::log::colorless "pre-start has been successfully completed!" + +result=$("${OPENIM_ROOT}"/scripts/stop-all.sh) +if [[ $? -ne 0 ]]; then + openim::log::error "View the error logs from this startup. ${LOG_FILE} \n" + openim::log::error "Some programs have not exited; the start process is aborted .\n $result" + exit 1 +fi + +openim::log::status "Start the OpenIM startup scripts: " +execute_start_scripts +openim::log::status "OpenIM startup scripts have been successfully completed!" + +sleep 2 + +result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check) +if [[ $? -ne 0 ]]; then + openim::log::error "The OpenIM services may fail to start.\n $result" + exit 1 +fi + + +result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}) +if [[ $? -ne 0 ]]; then + openim::log::error "The OpenIM services may fail to start.\n $result" + exit 1 +fi + +openim::log::status "Start the post-start tools:" +# ${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start +openim::log::status "post-start has been successfully completed!" +openim::util::find_ports_for_all_services ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]} +openim::util::find_ports_for_all_services ${OPENIM_MSGTRANSFER_BINARY[@]} + +openim::log::success "All OpenIM services have been successfully started!" \ No newline at end of file diff --git a/scripts-new/start.sh b/scripts-new/start.sh new file mode 100644 index 000000000..eeb85548a --- /dev/null +++ b/scripts-new/start.sh @@ -0,0 +1,54 @@ + +#!/usr/bin/env bash + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. + + +start_binaries() { + for bin in "${!binaries[@]}"; do + local count=${binaries[$bin]} + local bin_path=$(get_bin_full_path "$OPENIM_ROOT" "$bin") + local conf_dir=$(get_conf_dir) + + for ((i=0; i>> ${LOG_FILE} " $attempt + openim::log::error "stop process failed. continue waiting\n" "${result}" + fi + sleep 1 + ((attempt++)) + else + openim::log::success " All openim processes to be stopped" + exit 0 + fi +done + +openim::log::error "openim processes stopped failed" +exit 1 diff --git a/scripts-new/template/LICENSE b/scripts-new/template/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/scripts-new/template/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/scripts-new/template/LICENSE_TEMPLATES b/scripts-new/template/LICENSE_TEMPLATES new file mode 100644 index 000000000..dbc5ce2c8 --- /dev/null +++ b/scripts-new/template/LICENSE_TEMPLATES @@ -0,0 +1,13 @@ +Copyright © {{.Year}} {{.Holder}} All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/scripts-new/template/boilerplate.txt b/scripts-new/template/boilerplate.txt new file mode 100644 index 000000000..2f12334fd --- /dev/null +++ b/scripts-new/template/boilerplate.txt @@ -0,0 +1,3 @@ +Copyright © {{.Year}} {{.Holder}} All rights reserved. +Use of this source code is governed by a MIT style +license that can be found in the LICENSE file. diff --git a/scripts-new/template/footer.md.tmpl b/scripts-new/template/footer.md.tmpl new file mode 100644 index 000000000..6bf91162c --- /dev/null +++ b/scripts-new/template/footer.md.tmpl @@ -0,0 +1,19 @@ +**Full Changelog**: https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }} + +## Get Involved with OpenIM! + +Your patronage towards OpenIM is greatly appreciated 🎉🎉. + +If you encounter any problems during its usage, please create an issue in the [GitHub repository](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/), we're committed to resolving your problem as soon as possible. + +**Here are some ways to get involved with the OpenIM community:** + +📢 **Slack Channel**: Join our Slack channels for discussions, communication, and support. Click [here](https://openimsdk.slack.com) to join the Open-IM-Server Slack team channel. + +📧 **Gmail Contact**: If you have any questions, suggestions, or feedback for our open-source projects, please feel free to [contact us via email](https://mail.google.com/mail/?view=cm&fs=1&tf=1&to=info@openim.io). + +📖 **Blog**: Stay up-to-date with OpenIM-Server projects and trends by reading our [blog](https://openim.io/). We share the latest developments, tech trends, and other interesting information related to OpenIM. + +📱 **WeChat**: Add us on WeChat (QR Code) and indicate that you are a user or developer of Open-IM-Server. We'll process your request as soon as possible. + +Remember, your contributions play a vital role in making OpenIM successful, and we look forward to your active participation in our community! 🙌 \ No newline at end of file diff --git a/scripts-new/template/head.md.tmpl b/scripts-new/template/head.md.tmpl new file mode 100644 index 000000000..e024ab4ab --- /dev/null +++ b/scripts-new/template/head.md.tmpl @@ -0,0 +1,31 @@ +## Welcome to the {{ .Tag }} release of [OpenIM](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }})!🎉🎉! + +We are excited to release {{.Tag}}, Branch: https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/tree/{{ .Tag }} , Git hash [{{ .ShortCommit }}], Install Address: [{{ .ReleaseURL }}]({{ .ReleaseURL }}) + +Learn more about versions of OpenIM: + ++ We release logs are recorded on [✨CHANGELOG](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/blob/main/CHANGELOG/CHANGELOG.md) + ++ For information on versions of OpenIM and how to maintain branches, read [📚this article](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/blob/main/docs/contrib/version.md) + ++ If you wish to use mirroring, read OpenIM's [🤲image management policy](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/blob/main/docs/contrib/images.md) + +**Want to be one of them 😘?** + +

+ + + + + + + + + +

+ +> **Note** +> @openimbot and @kubbot have made great contributions to the community as community 🤖robots(@openimsdk/bot), respectively. +> Thanks to the @openimsdk/openim team for all their hard work on this release. +> Thank you to all the [💕developers and contributors](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/graphs/contributors), people from all over the world, OpenIM brings us together +> Contributions to this project are welcome! Please see [CONTRIBUTING.md](https://github.com/{{ .Env.USERNAME }}/{{ .ProjectName }}/blob/main/CONTRIBUTING.md) for details. \ No newline at end of file diff --git a/scripts-new/template/project_README.md b/scripts-new/template/project_README.md new file mode 100644 index 000000000..96575e681 --- /dev/null +++ b/scripts-new/template/project_README.md @@ -0,0 +1,41 @@ +# Project myproject + + + +## Features + + + +## Getting Started + +### Prerequisites + + + +### Building + + + +### Running + + + +## Using + + + +## Contributing + + + +## Community(optional) + + + +## Authors + + + +## License + + diff --git a/scripts-new/update-generated-docs.sh b/scripts-new/update-generated-docs.sh new file mode 100644 index 000000000..4c1fbfccc --- /dev/null +++ b/scripts-new/update-generated-docs.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This file is not intended to be run automatically. It is meant to be run +# immediately before exporting docs. We do not want to check these documents in +# by default. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::golang::setup_env + +BINS=( + gendocs + genopenimdocs + genman + genyaml +) +make -C "${OPENIM_ROOT}" BINS="${BINS[*]}" + +openim::util::ensure-temp-dir + +openim::util::gen-docs "${OPENIM_TEMP}" + +# remove all of the old docs +openim::util::remove-gen-docs + +# Copy fresh docs into the repo. +# the shopt is so that we get docs/.generated_docs from the glob. +shopt -s dotglob +cp -af "${OPENIM_TEMP}"/* "${OPENIM_ROOT}" +shopt -u dotglob diff --git a/scripts-new/update-yamlfmt.sh b/scripts-new/update-yamlfmt.sh new file mode 100644 index 000000000..8de0cc84c --- /dev/null +++ b/scripts-new/update-yamlfmt.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::golang::setup_env + +cd "${OPENIM_ROOT}" + +find_files() { + find . -not \( \ + \( \ + -wholename './output' \ + -o -wholename './.git' \ + -o -wholename './_output' \ + -o -wholename './_gopath' \ + -o -wholename './release' \ + -o -wholename './target' \ + -o -wholename '*/vendor/*' \ + \) -prune \ + \) -name 'OWNERS*' +} + +export GO111MODULE=on +find_files | xargs go run tools/yamlfmt/yamlfmt.go \ No newline at end of file diff --git a/scripts-new/verify-pkg-names.sh b/scripts-new/verify-pkg-names.sh new file mode 100644 index 000000000..be1acd015 --- /dev/null +++ b/scripts-new/verify-pkg-names.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This script verifies whether codes follow golang convention. +# Usage: `scripts/verify-pkg-names.sh`. + + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::golang::verify_go_version + +cd "${OPENIM_ROOT}" +if git --no-pager grep -E $'^(import |\t)[a-z]+[A-Z_][a-zA-Z]* "[^"]+"$' -- '**/*.go' ':(exclude)vendor/*' ':(exclude)**/*.pb.go'; then + openim::log::error "Some package aliases break go conventions." + echo "To fix these errors, do not use capitalized or underlined characters" + echo "in pkg aliases. Refer to https://blog.golang.org/package-names for more info." + exit 1 +fi diff --git a/scripts-new/verify-shellcheck.sh b/scripts-new/verify-shellcheck.sh new file mode 100644 index 000000000..3e56038dd --- /dev/null +++ b/scripts-new/verify-shellcheck.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This script lints each shell script by `shellcheck`. +# Usage: `scripts/verify-shellcheck.sh`. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# allow overriding docker cli, which should work fine for this script +DOCKER="${DOCKER:-docker}" + +# required version for this script, if not installed on the host we will +# use the official docker image instead. keep this in sync with SHELLCHECK_IMAGE +SHELLCHECK_VERSION="0.8.0" +SHELLCHECK_IMAGE="docker.io/koalaman/shellcheck-alpine:v0.8.0@sha256:f42fde76d2d14a645a848826e54a4d650150e151d9c81057c898da89a82c8a56" + +# disabled lints +disabled=( + # this lint disallows non-constant source, which we use extensively without + # any known bugs + 1090 + # this lint warns when shellcheck cannot find a sourced file + # this wouldn't be a bad idea to warn on, but it fails on lots of path + # dependent sourcing, so just disable enforcing it + 1091 + # this lint prefers command -v to which, they are not the same + 2230 + # Error SC2155 indicates that you should separate variable declaration and assignment to avoid masking the return value of the command. + # In Bash scripts, when you declare and assign a local variable at the same time a command is executed, you only get the output of the command, but not the exit status (return value) of the command. # + 2155 + # ShellCheck issues SC2086 warnings when you refer to a variable in a script but don't put it in double quotes.This can lead to unexpected behavior when scripts encounter Spaces, + # newlines, and wildcards in file names or other data. + 2086 + 2206 + + # TODO: 需要修复,然后开启 + 2034 + 2048 + 2148 + 2059 + 2214 + 2145 + 2128 + 2550 + 2046 + 2181 + 1102 + 2045 + 2068 + 2145 + 2207 + 2231 + 2013 + 2154 + 2120 + 1083 + 2001 + 2012 + 2016 + 2164 + 2223 + 2166 + 2119 + 2162 + 2295 + 2002 + 2004 + 2202 + 2178 + 2064 + 2260 + 2261 + 2043 + 2178 + 2044 + 2153 +) +# comma separate for passing to shellcheck +join_by() { + local IFS="$1"; + shift; + echo "$*"; +} +SHELLCHECK_DISABLED="$(join_by , "${disabled[@]}")" +readonly SHELLCHECK_DISABLED + +# ensure we're linting the k8s source tree +cd "${OPENIM_ROOT}" + +# Find all shell scripts excluding: +# - Anything git-ignored - No need to lint untracked files. +# - ./_* - No need to lint output directories. +# - ./.git/* - Ignore anything in the git object store. +# - ./vendor* - Vendored code should be fixed upstream instead. +# - ./third_party/*, but re-include ./third_party/forked/* - only code we +# forked should be linted and fixed. +all_shell_scripts=() +while IFS=$'\n' read -r script; +do git check-ignore -q "$script" || all_shell_scripts+=("$script"); + done < <(find . -name "*.sh" \ + -not \( \ + -path ./_\* -o \ + -path ./.git\* -o \ + -path ./Godeps\* -o \ + -path ./_output\* -o \ + -path ./components\* -o \ + -path ./logs\* -o \ + -path ./vendor\* -o \ + \( -path ./third_party\* -a -not -path ./third_party/forked\* \) \ +\) -print 2>/dev/null) + +# detect if the host machine has the required shellcheck version installed +# if so, we will use that instead. +HAVE_SHELLCHECK=false +if which shellcheck &>/dev/null; then + detected_version="$(shellcheck --version | grep 'version: .*')" + if [[ "${detected_version}" = "version: ${SHELLCHECK_VERSION}" ]]; then + HAVE_SHELLCHECK=true + fi +fi + +# if KUBE_JUNIT_REPORT_DIR is set, disable colorized output. +# Colorized output causes malformed XML in the JUNIT report. +SHELLCHECK_COLORIZED_OUTPUT="auto" +if [[ -n "${KUBE_JUNIT_REPORT_DIR:-}" ]]; then + SHELLCHECK_COLORIZED_OUTPUT="never" +fi + +# common arguments we'll pass to shellcheck +SHELLCHECK_OPTIONS=( + # allow following sourced files that are not specified in the command, + # we need this because we specify one file at a time in order to trivially + # detect which files are failing + "--external-sources" + # include our disabled lints + "--exclude=${SHELLCHECK_DISABLED}" + # set colorized output + "--color=${SHELLCHECK_COLORIZED_OUTPUT}" +) + +# tell the user which we've selected and lint all scripts +# The shellcheck errors are printed to stdout by default, hence they need to be redirected +# to stderr in order to be well parsed for Junit representation by juLog function +res=0 +if ${HAVE_SHELLCHECK}; then + openim::log::info "Using host shellcheck ${SHELLCHECK_VERSION} binary." + shellcheck "${SHELLCHECK_OPTIONS[@]}" "${all_shell_scripts[@]}" >&2 || res=$? +else + openim::log::info "Using shellcheck ${SHELLCHECK_VERSION} docker image." + "${DOCKER}" run \ + --rm -v "${OPENIM_ROOT}:${OPENIM_ROOT}" -w "${OPENIM_ROOT}" \ + "${SHELLCHECK_IMAGE}" \ + shellcheck "${SHELLCHECK_OPTIONS[@]}" "${all_shell_scripts[@]}" >&2 || res=$? +fi + +# print a message based on the result +if [ $res -eq 0 ]; then + echo 'Congratulations! All shell files are passing lint :-)' +else + { + echo + echo 'Please review the above warnings. You can test via "./scripts/verify-shellcheck.sh"' + echo 'If the above warnings do not make sense, you can exempt this warning with a comment' + echo ' (if your reviewer is okay with it).' + echo 'In general please prefer to fix the error, we have already disabled specific lints' + echo ' that the project chooses to ignore.' + echo 'See: https://github.com/koalaman/shellcheck/wiki/Ignore#ignoring-one-specific-instance-in-a-file' + echo + } >&2 + exit 1 +fi + +# preserve the result +exit $res diff --git a/scripts-new/verify-spelling.sh b/scripts-new/verify-spelling.sh new file mode 100644 index 000000000..c718c1ad1 --- /dev/null +++ b/scripts-new/verify-spelling.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script checks commonly misspelled English words in all files in the +# working directory by client9/misspell package. +# Usage: `scripts/verify-spelling.sh`. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +export OPENIM_ROOT +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +# Spell checking +# All the skipping files are defined in scripts/.spelling_failures +skipping_file="${OPENIM_ROOT}/scripts/.spelling_failures" +failing_packages=$(sed "s| | -e |g" "${skipping_file}") +git ls-files | grep -v -e "${failing_packages}" | xargs "$OPENIM_ROOT/_output/tools/misspell" -i "Creater,creater,ect" -error -o stderr diff --git a/scripts-new/verify-typecheck.sh b/scripts-new/verify-typecheck.sh new file mode 100644 index 000000000..f6c14844f --- /dev/null +++ b/scripts-new/verify-typecheck.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script does a fast type check of script srnetes code for all platforms. +# Usage: `scripts/verify-typecheck.sh`. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::golang::verify_go_version + +cd "${OPENIM_ROOT}" +ret=0 +TYPECHECK_SERIAL="${TYPECHECK_SERIAL:-false}" +scripts/run-in-gopath.sh \ +make tools.verify.typecheck +${OPENIM_ROOT}/_output/tools/typecheck "$@" "--serial=$TYPECHECK_SERIAL" || ret=$? +if [[ $ret -ne 0 ]]; then + openim::log::error "Type Check has failed. This may cause cross platform build failures." >&2 + openim::log::error "Please see https://github.com/kubecub/typecheck for more information." >&2 + exit 1 +fi diff --git a/scripts-new/verify-yamlfmt.sh b/scripts-new/verify-yamlfmt.sh new file mode 100644 index 000000000..a0aa583a8 --- /dev/null +++ b/scripts-new/verify-yamlfmt.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This script checks whether the OWNERS files need to be formatted or not by +# `yamlfmt`. Run `scripts/update-yamlfmt.sh` to actually format sources. +# +# Usage: `scripts/verify-yamlfmt.sh`. + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::util::ensure_clean_working_dir +# This sets up the environment, like GOCACHE, which keeps the worktree cleaner. +openim::golang::setup_env + +_tmpdir="$(openim::realpath "$(mktemp -d -t "$(basename "$0").XXXXXX")")" +git worktree add -f -q "${_tmpdir}" HEAD +openim::util::trap_add "git worktree remove -f ${_tmpdir}" EXIT +cd "${_tmpdir}" + +# Format YAML files +scripts/update-yamlfmt.sh + +# Test for diffs +diffs=$(git status --porcelain | wc -l) +if [[ ${diffs} -gt 0 ]]; then + echo "YAML files need to be formatted" >&2 + git diff + echo "Please run 'scripts/update-yamlfmt.sh'" >&2 + exit 1 +fi \ No newline at end of file diff --git a/scripts-new/wait-for-it.sh b/scripts-new/wait-for-it.sh new file mode 100644 index 000000000..c05b85678 --- /dev/null +++ b/scripts-new/wait-for-it.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() { + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() { + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() { + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi