Chuyển tới nội dung chính
Version: 4.1

Quy ước CLI

hitechcloudcli đã phát triển lên khoảng 170 lệnh qua nhiều giai đoạn - script shell từ 2023, một làn sóng công cụ phụ viết bằng Go, và quá trình chuyển đổi đang diễn ra sang Rust (hitechcloudcli/cli, phát hành dưới tên hitechcloudcli). Không có gì trong sự phát triển đó lộn xộn một cách ngẫu nhiên: mỗi giai đoạn giải quyết vấn đề của riêng nó rồi đi tiếp. Nhưng một người vận hành gõ lệnh, và một panel phân tích kết quả của lệnh, đều cần một câu trả lời cho câu hỏi "một lệnh trông như thế nào", chứ không phải ba. Trang này là câu trả lời đó cho mọi thứ mới, và là bản đồ cho thấy khoảng 170 lệnh hiện có vẫn còn lệch khỏi nó ở đâu.

Cả hai panel (hitechcloudpanel, hitechcloudadmin) gọi các lệnh này qua shell và phân tích stdout/mã thoát của chúng. Điều đó có nghĩa là một thay đổi không tương thích với một lệnh hiện có sẽ làm hỏng panel. Vì vậy quy tắc bên dưới gần như luôn là "thêm quy ước còn thiếu", không bao giờ là "đổi tên cho khớp quy ước" - xem Những gì chúng tôi không đổi tên.

Cách đặt tên lệnh​

Dispatcher (hitechcloudcli, script bash cấp cao nhất) suy ra tên của lệnh từ vị trí script của nó, chứ không từ một hội đồng đặt tên:

  • Script nằm trực tiếp trong hitechcloudcli/ (ví dụ config.sh, domain.sh, waf.sh) giữ tên trần: config, domain, waf.
  • Script trong thư mục con lấy tên <directory>-<file>: ftp/list.sh → ftp-list, native/wireguard.sh → native-wireguard, user/suspend.sh → user-suspend.

Vì vậy native-*, user-*, email-*, ftp-*, domains-*, plan-*, toolkit-*, websites-* không phải các quy ước cạnh tranh nhau - chúng là cùng một quy tắc, áp dụng cho thư mục mà người sở hữu lệnh đặt nó vào. Điều đó có nguyên tắc, và các lệnh mới nên tiếp tục dùng nó: lệnh về website/tên miền nằm trong domains/, không nằm ở cấp cao nhất, trừ khi nó là loại thiết lập toàn máy chủ (như domain, hostname của chính máy chủ) thực sự không có ý nghĩa theo từng tài khoản.

Một xung đột đáng biết: domain (số ít, ở cấp gốc) đặt hostname của chính máy chủ; domains-add, domains-delete, domains-dns, ... (số nhiều, trong domains/) quản lý website của khách hàng. Chúng là các tính năng không liên quan, chỉ tình cờ chung gốc từ. Đây không phải lỗi - git mv bất kỳ bên nào cũng làm hỏng mọi nơi gọi - nhưng nó là một cái bẫy "trông không nhất quán" thực sự với người vận hành gõ hitechcloudcli domain<TAB>. Lệnh mới không được thêm một cặp thứ hai như vậy: kiểm tra hitechcloudcli --help với gốc từ của bạn trước khi chọn tên, và nếu đã có một tên gần giống, hãy nói rõ trong chính văn bản --help của lệnh (hai dòng "không phải thứ bạn đang tìm? xem domains-*" không tốn gì và tiết kiệm được một yêu cầu hỗ trợ).

Hình dạng đối số​

Mỗi lệnh là một subcommand clap ở cấp cao nhất (hitechcloudcli/cli/src/main.rs), nhưng phần lớn - 182 trong số khoảng 198 bản triển khai lệnh hiện tại - khai báo subcommand của chúng như sau:

#[command(name = "ftp-list", disable_help_flag = true)]
FtpList {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},

clap chỉ định tuyến tên lệnh; run(&args) của chính lệnh phân tích Vec<String> của nó bằng tay, và disable_help_flag giao việc xử lý --help/-h cho chính đoạn mã đó. Đây là chủ ý, không phải sơ suất: đó là cách một bản song sinh Rust giữ nguyên chính xác ngữ pháp đối số của script shell (thứ tự vị trí, cờ trần, cặp --opt value) từng byte một, đúng như yêu cầu "panel không được nhận ra đây đã thành tệp nhị phân".

Gần một chục lệnh mới hơn (wireguard peers --user, ipmanager list, packages show|has|enable|disable, rdap-scan --lines/--top, conf) thay vào đó cho clap một hình dạng có kiểu thực sự:

Wireguard { #[command(subcommand)] cmd: WgCmd },
...
enum WgCmd { Peers { #[arg(long)] user: Option<String> } }

Với một lệnh thực sự mới, không có tiền lệ shell nào cần khớp, hãy ưu tiên hình dạng có kiểu. Nó cho bạn --help và thông báo lỗi miễn phí, và chính nó ngăn sự không nhất quán tiếp theo bên dưới tái diễn. Một lệnh là bản chuyển sang Rust của ngữ pháp đối số của một script hiện có nên giữ ngữ pháp mà nó thay thế (trailing_var_arg) thay vì "sửa" nó thành một hình dạng có kiểu mà script chưa từng có - việc sửa đó là thay đổi phá vỡ với mọi nơi gọi script.

Những gì đã khác nhau bên trong các trình phân tích viết tay, vì không có gì ép buộc sự nhất quán: một số cờ là --key value (hai token: if x == "--bg" { background = true }), một số là --key=value (một token, qua split_once('=')), và vài lệnh trộn cả hai cho các cờ khác nhau trong cùng một lần gọi. Cờ mới trên một lệnh phân tích bằng tay hiện có nên khớp với phong cách sẵn có của chính lệnh đó, không đưa phong cách kia vào bên cạnh.

Đầu ra​

  • --json: hiện được khoảng 32 trong ~198 lệnh hỗ trợ (những lệnh mà một panel hoặc script khác thực sự cần phân tích). Lệnh mà panel gọi PHẢI hỗ trợ --json nếu nó chưa in JSON vô điều kiện. Lệnh thuần tương tác (người vận hành đọc, không gì phân tích nó) không cần, nhưng thêm vào thì rẻ và giúp lệnh sẵn sàng cho trang panel tiếp theo cần đến nó.
  • Bảng/văn bản thường là mặc định cho mọi thứ khác - giữ cho nó dễ đọc; đừng tạo định dạng thứ ba (CSV, kết xuất key=value) trừ khi lệnh đã có định dạng đó trước khi được chuyển đổi.
  • Im lặng khi thành công là câu trả lời hợp lệ cho lệnh thay đổi một thứ mà người vận hành đã chỉ rõ (user-suspend alice không cần lặp lại "alice suspended") nhưng lệnh nên nói rõ đã thay đổi gì khi trạng thái trước/sau không rõ ràng (lệnh dạng thêm/gán/cấp phát nên xác nhận nó đã làm gì và cho cái gì, giống như enterprise <module> license <key> giờ in ra "Licence key saved for <module>.").

Lỗi​

  • Mã thoát: 0 là thành công, khác 0 là thất bại. Hầu hết lệnh dùng 1 cho "không thể làm việc được yêu cầu" và một số (enterprise, module) dùng 2 cho "bản thân các đối số không phân tích được" - cách phân chia đó đáng giữ cho lệnh mới (một script quyết định có thử lại hay không có thể phân biệt "đầu vào sai, đừng thử lại" với "lỗi tạm thời, có thể thử lại" mà không cần đọc thông báo), nhưng hiện nó chưa được áp dụng nhất quán và nơi gọi chưa được dựa vào việc 2 mang nghĩa đó ở mọi nơi.

  • stderr cho lỗi: mọi lệnh gửi văn bản lỗi tới stderr qua eprintln!, với một ngoại lệ được ghi lại bên dưới. Con số này được đo chứ không phải ước lượng khi sửa: 75 thông báo trong 26 tệp đang bị in ra stdout, không phải "ít nhất một chục" như một lần đọc trước đây của trang này phỏng đoán, và 70 trong số đó đã được chuyển. stdout dành cho thứ đã thành công, và các panel đọc nó tại 193 vị trí gọi qua .Output(), nên lỗi nằm ở đó sẽ xuất hiện ở chỗ đáng lẽ là dữ liệu - một danh sách có Error: ... trong đó, hoặc JSON không phân tích được. Lệnh mới phải dùng eprintln!, không bao giờ dùng println!, cho bất cứ thứ gì có tiền tố Error:.

    .github/scripts/cli_conventions.sh trong kho hitechcloudcli làm thất bại bản build thêm lại một thông báo như vậy.

    Ngoại lệ, và vì sao nó không phải lỗi: api, email-setup, email-webmail, email-quotas và email-sendlimit trả lời Error: HiTechCloud Community edition does not support ... trên stdout, vì hitechcloudpanel/internal/modules/emails/webmail.go so khớp văn bản đó để quyết định liên kết webmail trỏ tới đâu:

    if strings.Contains(output, "HiTechCloud Community edition") {
    return "http://localhost", nil
    }

    Đó là câu trả lời của lệnh, không phải thất bại - và chuyển nó sang stderr sẽ làm hỏng webmail trên mọi máy chủ Community, một cách âm thầm. Mỗi dòng trong năm dòng đó có một chú thích nói rõ điều này. Đó cũng là lý do việc chuyển đổi không được làm hàng loạt: mọi thông báo đều được so khớp với mã nguồn của cả hai panel trước, trong mọi cửa sổ từ ba từ trở lên, và bốn kết quả hoá ra là chuỗi giao diện của chính các panel chứ không phải trình phân tích của chúng tôi.

  • Nói rõ bước tiếp theo. Phần đầu của chính dispatcher (hitechcloudcli, execute_command()) mang vết sẹo của điều này: một lệnh không xác định từng rơi xuống màn hình hướng dẫn sử dụng, lịch sử lệnh, và màn hình giới thiệu, nên nơi gọi đọc "dòng cuối cùng của đầu ra" (trang sao lưu của trang quản trị đã làm đúng như vậy) nhận được License: MIT và báo "Backup failed: License: MIT" cho quản trị viên. Cách sửa giờ đã thành quy tắc chung:

    Lỗi chính là câu trả lời. Gợi ý đi cùng nó, trên stderr, nơi lỗi thuộc về.

    Lệnh đã chuyển đổi mà thiếu tệp nhị phân sẽ nói rõ điều đó và cách khắc phục ("'$command' runs in hitechcloudcli, which is not installed... Install it with: hitechcloudcli update"), không phải "command not found". Lệnh mới nên tự đặt cùng tiêu chuẩn: thông báo lỗi nêu rõ cái gì sai VÀ người vận hành có thể làm gì, trên stderr, và không có gì khác được gửi tới stderr nếu không thuộc câu trả lời đó.

Trợ giúp​

  • hitechcloudcli --help (hoặc -h, hoặc help) liệt kê mọi lệnh kèm mô tả một dòng và chuỗi cách dùng, lấy từ chú thích đầu tệp # Description:/# Usage: của từng script .sh, hoặc - với lệnh đã chuyển sang Rust và không còn script - từ bảng COMMANDS trong cli/src/main.rs (name|description|usage, được đọc bởi hitechcloudcli commands). Lệnh Rust mới phải thêm dòng của nó vào bảng đó, nếu không hitechcloudcli --help sẽ âm thầm ngừng liệt kê nó, điều còn tệ hơn một mô tả kém.

  • hitechcloudcli <command> --help được trả lời tập trung, cho cả 176 lệnh, từ cùng bảng COMMANDS dùng để tạo danh sách đầy đủ: tên, mô tả và cách dùng của lệnh. Cả hai tính cách của tệp nhị phân đều làm việc này - dispatcher khi được gọi dưới tên hitechcloudcli, và main() trước clap khi được gọi dưới bất kỳ tên nào khác, chẳng hạn symlink hitechcloudcli mà mọi nơi gọi cố định sử dụng.

    Nửa sau đó từng bị thiếu, và nó quan trọng: một lệnh không phân tích --help sẽ coi nó là giá trị vị trí và chạy. hitechcloudcli domains-whoowns --help tra cứu một tên miền tên là --help; files-fix_permissions --help đọc nó như tài khoản cần chown tệp. Không có gì tồn tại dưới các tên đó, nên không có gì bị hỏng, nhưng lệnh đã chạy.

    every_command_has_help_text_to_print (kiểm thử riêng của rustcli) khẳng định rằng cả 176 dòng đều có mô tả và chuỗi cách dùng nêu tên lệnh, vì bảng đó là thứ --help giờ in ra.

  • Trợ giúp riêng của từng lệnh - một usage() bao gồm các động từ và cờ của nó - có trong khoảng 70 bản triển khai. Phần còn lại dựa vào mục tập trung ở trên cộng với nhánh chung "sai số lượng đối số" (xem user/suspend.rs, ftp/add.rs). Lệnh có các động từ đáng liệt kê nên in chúng từ usage() của chính nó khi nhận một động từ không biết; thêm vào luôn an toàn, vì --help tập trung là thứ người vận hành gặp đầu tiên.

  • Bất kỳ văn bản trợ giúp nào lệnh in ra cũng phải khớp với những gì lệnh thực sự làm bây giờ, không phải những gì script của nó làm năm 2023. Một chuỗi trợ giúp lỗi thời vẫn nhắc tới cờ mà bản Rust đã bỏ, hoặc thiếu cờ mà bản Rust đã thêm, còn tệ hơn không có trợ giúp, vì nó sai một cách âm thầm.

Những gì chúng tôi không đổi tên​

Mọi điểm lạ về tên/hình dạng/đầu ra liệt kê ở trên đều có cách sửa là đổi tên hoặc đổi hình dạng - và mỗi cách đó đều là thay đổi phá vỡ với hitechcloudadmin và hitechcloudpanel, vốn phân tích stdout của các lệnh này theo đúng hình dạng (xem rất nhiều lời gọi exec.Command ("hitechcloudcli", ...) trong internal/handlers/*.go) và, với các script shell còn tồn tại, bởi những nơi gọi shell khác trong mã nguồn này. Không cái nào bị động tới ở đây. Những gì đã được sửa thay vào đó chỉ mang tính bổ sung: câu chuyện --help/cách dùng vốn chưa tồn tại, một mã thoát, một --json mà panel cần. Các lệnh bên dưới là những lệnh có khoảng trống thực sự mà cách sửa thực chất là đổi tên hoặc đổi hình dạng đối số

  • được liệt kê để thấy rõ chi phí, không phải để làm một cách tuỳ tiện:
LệnhSự không nhất quánChi phí để sửa
domain so với domains-*Các tính năng không liên quan, cùng gốc từ, xung đột ở lần nhấn tab đầu tiênĐổi tên làm hỏng quy trình đặt hostname nằm trong install, UPDATE.sh, và thói quen hoặc script của mọi người vận hành; cách sửa trung thực là tham chiếu chéo trong --help đã được thêm, không phải đổi tên.
Khoảng 130 lệnh không có --help riêngGọi trần đôi khi in cách dùng, đôi khi không in gì, đôi khi báo một lỗi không liên quan do thiếu đối số bắt buộcRà soát và thêm xử lý --help thực sự cho từng tệp trong 130 tệp là khối lượng công việc thực sự mà câu "CLI không nhất quán" ngụ ý - lớn, máy móc, và an toàn khi làm dần (từng module, kiểm chứng theo từng module), nhưng không trong một PR.
Khoảng 166 lệnh không có --jsonPanel nào sau này muốn phân tích một trong số đó phải thêm vào lúc đó, từng lệnh mộtKhông phải chi phí bị trì hoãn - nên thêm --json khi có nơi gọi cần, không thêm theo phỏng đoán cho các lệnh chưa có gì phân tích.
Trộn --key value / --key=value trong các trình phân tích viết tayNgười vận hành phải nhớ lệnh nào dùng phong cách nàoThống nhất mọi trình phân tích viết tay về một phong cách cờ, trên thực tế, là viết lại phần phân tích đối số của khoảng 180 tệp - mục rủi ro cao nhất, giá trị thấp nhất trong danh sách này, vì mọi nơi gọi (panel, script, thói quen) đã biết phong cách cờ hiện tại của một lệnh đều cần được kiểm chứng lại. Để nguyên; cờ mới trên lệnh hiện có khớp với phong cách của chính lệnh đó (như đã nêu ở trên).
Trang này có hữu ích không?