feat(kenshell): add gRPC client for the KenShell git data plane
Copy the kenshell proto tree (plus the gitken protos it imports) into a new lib/kenshell crate, generate tonic clients, and expose KenShellClient with shared message-size limits and request-id metadata. Add git.kenshell_endpoint / kenshell_rpc_key / kenshell_timeout_secs settings.
ZhenYi committed
Modified config/default.toml config/default.toml +3 −0 View config/default.toml
@@ -194,6 +194,9 @@
194194ssh_clone_user = "git"
195195graphql_complexity_limit = 1000
196196graphql_introspection = true
197kenshell_endpoint = "http://127.0.0.1:50091"
198kenshell_rpc_key = ""
199kenshell_timeout_secs = 3600
197200
198201[git.sync]
199202lock_ttl_secs = 120
Modified lib/config/src/git.rs lib/config/src/git.rs +6 −0 View lib/config/src/git.rs
@@ -71,6 +71,9 @@
7171 pub ssh_clone_user: String,
7272 pub graphql_complexity_limit: u64,
7373 pub graphql_introspection: bool,
74 pub kenshell_endpoint: String,
75 pub kenshell_rpc_key: String,
76 pub kenshell_timeout_secs: u64,
7477}
7578
7679impl Default for GitConfig {
@@ -90,6 +93,9 @@
9093 ssh_clone_user: "git".to_string(),
9194 graphql_complexity_limit: 1000,
9295 graphql_introspection: false,
96 kenshell_endpoint: "http://127.0.0.1:50091".to_string(),
97 kenshell_rpc_key: String::new(),
98 kenshell_timeout_secs: 3600,
9399 }
94100 }
95101}
Added lib/kenshell/proto/gitken/v1/cleanup.proto lib/kenshell/proto/gitken/v1/cleanup.proto +87 −0 View lib/kenshell/proto/gitken/v1/cleanup.proto
@@ -0,0 +1,87 @@
1syntax = "proto3";
2
3package gitken;
4
5import "gitken/v1/lint.proto";
6import "gitken/v1/shared.proto";
7
8
9// CleanupService provides RPCs to clean up a repository's contents.
10service CleanupService {
11
12 // ApplyBfgObjectMapStream ...
13 rpc ApplyBfgObjectMapStream(stream ApplyBfgObjectMapStreamRequest) returns (stream ApplyBfgObjectMapStreamResponse) {
14 option (op_type) = {
15 op: MUTATOR
16 };
17 }
18
19 // RewriteHistory redacts targeted strings and deletes requested blobs in a
20 // repository and updates all references to point to the rewritten commit
21 // history. This is useful for removing inadvertently pushed secrets from your
22 // repository and purging large blobs. This is a dangerous operation.
23 //
24 // The following known error conditions may happen:
25 //
26 // - `InvalidArgument` in the following situations:
27 // - The provided repository can't be validated.
28 // - The repository field is set on any request other than the initial one.
29 // - All of the client requests do not contain either blobs to remove or
30 // redaction patterns to redact.
31 // - A blob object ID is invalid.
32 // - A redaction pattern contains a newline character.
33 // - `Aborted` if the repository is mutated while this RPC is executing.
34 rpc RewriteHistory(stream RewriteHistoryRequest) returns (RewriteHistoryResponse) {
35 option (op_type) = {
36 op : MUTATOR
37 };
38 }
39
40}
41
42// ApplyBfgObjectMapStreamRequest ...
43message ApplyBfgObjectMapStreamRequest {
44 // repository ...
45 // Only available on the first message
46 Repository repository = 1 [(target_repository)=true];
47
48 // object_map is a raw object-map file as generated by BFG: https://rtyley.github.io/bfg-repo-cleaner
49 // Each line in the file has two object SHAs, space-separated - the original
50 // SHA of the object, and the SHA after BFG has rewritten the object.
51 bytes object_map = 2;
52}
53
54// ApplyBfgObjectMapStreamResponse ...
55message ApplyBfgObjectMapStreamResponse {
56 // Entry refers to each parsed entry in the request's object map so the client
57 // can take action.
58 message Entry {
59 // type ...
60 ObjectType type = 1;
61 // old_oid ...
62 string old_oid = 2;
63 // new_oid ...
64 string new_oid = 3;
65 }
66
67 // entries ...
68 repeated Entry entries = 1;
69}
70
71// RewriteHistoryRequest is a request for the RewriteHistory RPC.
72message RewriteHistoryRequest {
73 // repository is the repository that shall be rewritten.
74 // Must be sent on the first request only.
75 Repository repository = 1 [(target_repository)=true];
76 // blobs is the list of blob object ids that will be removed from history.
77 repeated string blobs = 2;
78 // redactions is the list of literals or patterns that will be replaced
79 // with "***REMOVED***". Items cannot contain newline characters.
80 // See https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html
81 // for a full explanation of what patterns are supported.
82 repeated bytes redactions = 3;
83}
84
85// RewriteHistoryResponse a response for the RewriteHistory RPC.
86message RewriteHistoryResponse {
87}
Added lib/kenshell/proto/gitken/v1/conflicts.proto lib/kenshell/proto/gitken/v1/conflicts.proto +136 −0 View lib/kenshell/proto/gitken/v1/conflicts.proto
@@ -0,0 +1,136 @@
1syntax = "proto3";
2
3package gitken;
4
5import "google/protobuf/timestamp.proto";
6import "gitken/v1/lint.proto";
7import "gitken/v1/shared.proto";
8
9
10// ConflictsService is a service which provides RPCs to interact with conflicts
11// resulting from a merge.
12service ConflictsService {
13
14 // ListConflictFiles returns all conflicting files which result from a merge
15 // of two specified commit objects.
16 rpc ListConflictFiles(ListConflictFilesRequest) returns (stream ListConflictFilesResponse) {
17 option (op_type) = {
18 op: ACCESSOR
19 };
20 }
21
22 // ResolveConflicts tries to resolve a conflicting merge with a set of
23 // user-provided merge resolutions. If resolving the conflict succeeds, the
24 // result will be a new merge commit.
25 rpc ResolveConflicts(stream ResolveConflictsRequest) returns (ResolveConflictsResponse) {
26 option (op_type) = {
27 op: MUTATOR
28 };
29 }
30
31}
32
33// ListConflictFilesRequest is the request for the ListConflictFilesRequest rpc.
34message ListConflictFilesRequest {
35 // repository is the repository for which we want to list the conflicted files.
36 Repository repository = 1 [(target_repository)=true];
37 // our_commit_oid is the tree-ish OID to merge into.
38 string our_commit_oid = 2;
39 // their_commit_oid is the tree-ish OID to merge from.
40 string their_commit_oid = 3;
41 // allow_tree_conflicts will not cause the request to fail in case there are
42 // tree conflicts. If set to true, then responses may contain conflict files
43 // where some of the paths are unset.
44 bool allow_tree_conflicts = 4;
45 // skip_content will skip the parsing and streaming of conflicted file's content.
46 // This can be useful when we only want to know if there is a conflict and which files
47 // are conflicted but don't care about the contents of the conflicted files.
48 bool skip_content = 5;
49}
50
51// ConflictFileHeader contains parsed conflicted file information from the `git-merge-tree(1)`
52// output for an individual file.
53message ConflictFileHeader {
54 reserved 1;
55 // commit_oid is the tree-ish revision being merged into.
56 string commit_oid = 2;
57 // their_path is the path of the conflicted file being merged from.
58 bytes their_path = 3;
59 // our_path is the path of the conflicted file being merged into.
60 bytes our_path = 4;
61 // our_mode is the mode of the conflicted file being merged into.
62 int32 our_mode = 5;
63 // ancestor_path is path of the conflicted file for the merge base.
64 bytes ancestor_path = 6;
65}
66
67// ConflictFile contains conflicted file information.
68message ConflictFile {
69 oneof conflict_file_payload {
70 // header contains the header information about a conflicted file.
71 ConflictFileHeader header = 1;
72 // content is the content data from the conflicted file. The file data may be broken into chunks
73 // and sent in the stream following a file header. if skip_content is set in the request,
74 // conflicted file content is not streamed.
75 bytes content = 2;
76 }
77}
78
79// ListConflictFilesResponse is the response for the ListConflicts RPC.
80message ListConflictFilesResponse {
81 // files is a list of conflicted file information being sent in the stream. This information
82 // contains conflicted file headers and optionally the conflicted file content.
83 repeated ConflictFile files = 1;
84}
85
86// ResolveConflictsRequestHeader is the first message that must be sent for
87// each ResolveConflicts call.
88message ResolveConflictsRequestHeader {
89 // repository is the repository in which conflicts shall be resolved and
90 // where SourceBranch shall be updated with the resolved conflict.
91 Repository repository = 1 [(gitken.target_repository)=true];
92 // our_commit_oid is the OID of the commit representing the local commit.
93 string our_commit_oid = 2;
94 // target_repository is the repository from which TheirCommitOid shall be
95 // retrieved.
96 Repository target_repository = 3;
97 // their_commit_oid is the OID of the commit representing the remote commit
98 // which is to be merged into the local commit.
99 string their_commit_oid = 4;
100 // source_branch is the branch on which the new commit shall be created.
101 bytes source_branch = 5;
102 // target_branch identifies the branch which will be fetched from
103 // target_repository in case TheirCommitOid does not exist in Repository.
104 bytes target_branch = 6;
105 // commit_message is the message of the newly created merge commit.
106 bytes commit_message = 7;
107 // user is the user used as author and committer of the newly created merge
108 // commit.
109 User user = 8;
110 // timestamp is the optional timestamp to use for the commit as committer
111 // date. If it's not set, the current time will be used.
112 google.protobuf.Timestamp timestamp = 9;
113}
114
115// ResolveConflictsRequest is a request for the ResolveConflicts RPC.
116message ResolveConflictsRequest {
117 // RequestPayload is the payload part of the request. The first message sent
118 // must always be a ResolveConflictsRequestHeader, whereas all remaining
119 // requests must be FilesJson requests.
120 oneof resolve_conflicts_request_payload {
121 // header is the initial message specifying parameters of the RPC call.
122 ResolveConflictsRequestHeader header = 1;
123 // files_json is a JSON-encoded list of conflicts resolutions.
124 bytes files_json = 2;
125 }
126}
127
128// ResolveConflictsResponse is a response of the ResolveConflicts RPC. Conflict
129// resolution may have failed even if the RPC has returned OK. The user must
130// check ResolutionError to verify whether the merge commit was correctly
131// computed or not.
132message ResolveConflictsResponse {
133 // resolution_error contains a description of why conflict resolution has
134 // failed.
135 string resolution_error = 1;
136}
Added lib/kenshell/proto/gitken/v1/lint.proto lib/kenshell/proto/gitken/v1/lint.proto +74 −0 View lib/kenshell/proto/gitken/v1/lint.proto
@@ -0,0 +1,74 @@
1syntax = "proto3";
2
3package gitken;
4
5import "google/protobuf/descriptor.proto";
6
7
8// OperationMsg ...
9message OperationMsg {
10 // Operation ...
11 enum Operation {
12 // UNKNOWN ...
13 UNKNOWN = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
14 // MUTATOR ...
15 MUTATOR = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
16 // ACCESSOR ...
17 ACCESSOR = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
18 // MAINTENANCE ...
19 MAINTENANCE = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
20 }
21
22 // Scope ...
23 enum Scope {
24 // REPOSITORY ...
25 REPOSITORY = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
26 // STORAGE ...
27 STORAGE = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
28 // PARTITION ...
29 PARTITION = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
30
31 reserved 1;
32 reserved "SERVER";
33 }
34
35 // op ...
36 Operation op = 1;
37
38 // scope_level indicates what level an RPC interacts with a server:
39 // - REPOSITORY: scoped to only a single repo
40 // - SERVER: affects the entire server and potentially all repos
41 // - STORAGE: scoped to a specific storage location and all repos within
42 // - PARTITION: scoped to a specific partition and all repos within
43 Scope scope_level = 2;
44}
45
46extend google.protobuf.ServiceOptions {
47 // intercepted indicates whether the proxy intercepts and handles the call
48 // instead of proxying. Intercepted services do not require scope or operation
49 // annotations.
50 bool intercepted = 82302;
51}
52
53extend google.protobuf.MethodOptions {
54 // op_type ...
55 OperationMsg op_type = 82303; // Random high number..
56}
57
58extend google.protobuf.FieldOptions {
59 // storage is used to mark field containing name of affected storage.
60 bool storage = 91233; // Random high number..
61
62 // repository annotation is used mark field used as repository
63 // when parent message is marked as target or additional repository
64 // If this operation modifies a repository, this annotations
65 // will specify the location of the Repository field within
66 // the request message.
67 bool repository = 91234;
68 // target_repository is used to mark target repository.
69 bool target_repository = 91235;
70 // additional_repository is used to mark additional repository.
71 bool additional_repository = 91236;
72 // partition_id is used to mark field containing id of affected partition.
73 bool partition_id = 91237; // Random high number..
74}
Added lib/kenshell/proto/gitken/v1/shared.proto lib/kenshell/proto/gitken/v1/shared.proto +262 −0 View lib/kenshell/proto/gitken/v1/shared.proto
@@ -0,0 +1,262 @@
1syntax = "proto3";
2
3package gitken;
4
5import "google/protobuf/timestamp.proto";
6import "gitken/v1/lint.proto";
7
8
9// ObjectType ...
10enum ObjectType {
11 // UNKNOWN ...
12 UNKNOWN = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
13 // COMMIT ...
14 COMMIT = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
15 // BLOB ...
16 BLOB = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
17 // TREE ...
18 TREE = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
19 // TAG ...
20 TAG = 4; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
21}
22
23// ObjectFormat is the object format that a Git repository can use.
24enum ObjectFormat {
25 // OBJECT_FORMAT_UNSPECIFIED is the default object type when it has not been explicitly requested
26 // by the client. Defaults to OBJECT_FORMAT_SHA1.
27 OBJECT_FORMAT_UNSPECIFIED = 0;
28 // OBJECT_FORMAT_SHA1 is the object format based on the SHA1 hash.
29 OBJECT_FORMAT_SHA1 = 1;
30 // OBJECT_FORMAT_SHA256 is the object format based on the SHA256 hash. This is experimental.
31 OBJECT_FORMAT_SHA256 = 2;
32}
33
34// ReferenceBackend is the backend that a Git repository uses to store its references.
35enum ReferenceBackend {
36 // REFERENCE_BACKEND_UNSPECIFIED is the default value for a reference backend if there
37 // is an error during detection. This state should ideally never occur, and if set
38 // it indicates that something has gone wrong.
39 REFERENCE_BACKEND_UNSPECIFIED = 0;
40 // REFERENCE_BACKEND_FILES is the traditional backend built using files/directories.
41 REFERENCE_BACKEND_FILES = 1;
42 // REFERENCE_BACKEND_REFTABLE is the new backend using customized binary file format.
43 REFERENCE_BACKEND_REFTABLE = 2;
44 // REFERENCE_BACKEND_MVCC is the MVCC-enabled reference backend, served by the dedicated
45 // MVCC Git binary.
46 REFERENCE_BACKEND_MVCC = 3;
47}
48
49// SignatureType ...
50enum SignatureType {
51 // NONE ...
52 NONE = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
53 // PGP ...
54 PGP = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
55 // X509 ...
56 X509 = 2; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
57 // SSH ...
58 SSH = 3; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
59 // maybe add X509+TSA or other combinations at a later step
60}
61
62// Repository ...
63message Repository {
64 // DEPRECATED: upstream issue 151
65 reserved 1;
66 reserved "path";
67
68 // storage_name ...
69 string storage_name = 2;
70 // relative_path ...
71 string relative_path = 3;
72 // git_object_directory sets the GIT_OBJECT_DIRECTORY envvar on git commands to the value of this field.
73 // It influences the object storage directory the SHA1 directories are created underneath.
74 string git_object_directory = 4;
75 // git_alternate_object_directories sets the GIT_ALTERNATE_OBJECT_DIRECTORIES envvar on git commands to
76 // the values of this field. It influences the list of Git object directories which can be used to search
77 // for Git objects.
78 repeated string git_alternate_object_directories = 5;
79 // gl_repository is used in callbacks to GitLab so that it knows what repository the event is
80 // associated with. May be left empty on RPC's that do not perform callbacks.
81 // During project creation, `gl_repository` may not be known.
82 string gl_repository = 6;
83 reserved 7;
84 // gl_project_path is the human-readable GitLab project path (e.g. gitlab-org/gitlab-ce).
85 // When hashed storage is use, this associates a project path with its
86 // path on disk. The name can change over time (e.g. when a project is
87 // renamed). This is primarily used for logging/debugging at the
88 // moment.
89 string gl_project_path = 8;
90}
91
92// CommitTrailer is a single Git trailer (https://git-scm.com/docs/git-interpret-trailers)
93// key-value pair.
94message CommitTrailer {
95 // key is the key of the trailer, such as `Signed-off-by`.
96 bytes key = 1;
97 // value is the value of the trailer, such as `Alice <alice@gmail.com>`.
98 bytes value = 2;
99}
100
101// CommitStatInfo includes the number of changed lines and files in the commit.
102message CommitStatInfo {
103 // additions is the number of line additions in the commit.
104 int32 additions = 1;
105 // deletions is the number of lines deleted in the commit.
106 int32 deletions = 2;
107 // changed_files is the number of files changed in the commit.
108 int32 changed_files = 3;
109}
110
111// GitCommit corresponds to Gitlab::Git::Commit
112message GitCommit {
113 // id ...
114 string id = 1;
115 // subject ...
116 bytes subject = 2;
117 // body ...
118 bytes body = 3;
119 // author ...
120 CommitAuthor author = 4;
121 // committer ...
122 CommitAuthor committer = 5;
123 // parent_ids ...
124 repeated string parent_ids = 6;
125 // body_size is the size of the commit body. If body exceeds a certain threshold,
126 // it will be nullified, but its size will be set in body_size so we can know if
127 // a commit had a body in the first place.
128 int64 body_size = 7;
129 // signature_type ...
130 SignatureType signature_type = 8;
131 // tree_id is the object ID of the tree. The tree ID will always be filled, even
132 // if the tree is empty. In that case the value will be `4b825dc642cb6eb9a060e54bf8d69288fbee4904`.
133 // That value is equivalent to `git hash-object -t tree /dev/null`.
134 string tree_id = 9;
135 // trailers is the list of Git trailers (https://git-scm.com/docs/git-interpret-trailers)
136 // found in this commit's message. The number of trailers and their key/value
137 // sizes are limited. If a trailer exceeds these size limits, it and any
138 // trailers that follow it are not included.
139 repeated CommitTrailer trailers = 10;
140 // short_stats are the git stats including additions, deletions and changed_files,
141 // they are only set when `include_shortstat == true`.
142 CommitStatInfo short_stats = 11;
143 // referenced_by contains fully-qualified reference names (e.g refs/heads/main)
144 // that point to the commit.
145 repeated bytes referenced_by = 12; // protolint:disable:this REPEATED_FIELD_NAMES_PLURALIZED
146 // encoding is the encoding of the commit message. This field will only be present if
147 // `i18n.commitEncoding` was set to a value other than "UTF-8" at the time
148 // this commit was made.
149 // See: https://git-scm.com/docs/git-commit#_discussion
150 string encoding = 13;
151}
152
153// CommitAuthor ...
154message CommitAuthor {
155 // name ...
156 bytes name = 1;
157 // email ...
158 bytes email = 2;
159 // date ...
160 google.protobuf.Timestamp date = 3;
161 // timezone ...
162 bytes timezone = 4;
163}
164
165// ExitStatus ...
166message ExitStatus {
167 // value ...
168 int32 value = 1;
169}
170
171// Branch corresponds to Gitlab::Git::Branch
172message Branch {
173 // name ...
174 bytes name = 1;
175 // target_commit ...
176 GitCommit target_commit = 2;
177}
178
179// Tag ...
180message Tag {
181 // name ...
182 bytes name = 1;
183 // id ...
184 string id = 2;
185 // target_commit ...
186 GitCommit target_commit = 3;
187 // message is the message of the tag. If message exceeds a certain
188 // threshold, it will be nullified, but its size will be set in
189 // message_size so we can know if a tag had a message in the first place.
190 bytes message = 4;
191 // message_size ...
192 int64 message_size = 5;
193 // tagger ...
194 CommitAuthor tagger = 6;
195 // signature_type ...
196 SignatureType signature_type = 7;
197}
198
199// User ...
200message User {
201 // gl_id ...
202 string gl_id = 1;
203 // name ...
204 bytes name = 2;
205 // email ...
206 bytes email = 3;
207 // gl_username ...
208 string gl_username = 4;
209 // timezone is the timezone as configured by the user in the web interface. This
210 // timezone may be used when new commits are created via RPC calls.
211 string timezone = 5;
212}
213
214// ObjectPool ...
215message ObjectPool {
216 // repository ...
217 Repository repository = 1 [(gitken.repository)=true];
218}
219
220// PaginationParameter controls pagination within RPCs.
221message PaginationParameter {
222 // page_token instructs pagination to start sending results after the provided page
223 // token appears. A page token allows for a generic pattern to uniquely
224 // identify a result or 'page'. Each paginated RPC may interpret a page
225 // token differently.
226 string page_token = 1;
227 // limit is the maximum number of objects the client will receive. When fully consuming
228 // the response the client will receive _at most_ `limit` number of resulting objects.
229 // Note that the number of response messages might be much lower, as some response
230 // messages already send multiple objects per message.
231 // When the limit is smaller than 0, it will be normalized to 2147483647
232 // on the server side. When limit is not set, it defaults to 0, and no
233 // results are send in the response.
234 int32 limit = 2;
235}
236
237// PaginationCursor defines the page token clients should use to fetch the next page.
238message PaginationCursor {
239 // next_cursor is an opaque token provided to the caller to indicate what the caller
240 // should present as a page_token to get subsequent results.
241 string next_cursor = 1;
242}
243
244// GlobalOptions are additional git options as defined at https://git-scm.com/docs/git/#_options.
245message GlobalOptions {
246 // literal_pathspecs should be set to treat pathspecs literally (i.e. no globbing, no pathspec magic).
247 bool literal_pathspecs = 1;
248}
249
250// SortDirection defines the sort direction.
251enum SortDirection {
252 // ASCENDING sorts by the sort key in ascending order.
253 ASCENDING = 0; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
254 // DESCENDING sorts by the sort key in descending order.
255 DESCENDING = 1; // protolint:disable:this ENUM_FIELD_NAMES_PREFIX
256}
257
258// Partition represents storage partition
259message Partition {
260 // id is the identifier of a partition within the storage
261 string id = 1;
262}
Added lib/kenshell/proto/kenshell/common/v1/errors.proto lib/kenshell/proto/kenshell/common/v1/errors.proto +34 −0 View lib/kenshell/proto/kenshell/common/v1/errors.proto
@@ -0,0 +1,34 @@
1syntax = "proto3";
2
3package kenshell.common.v1;
4
5enum ErrorCode {
6 ERROR_CODE_UNSPECIFIED = 0;
7 INVALID_ARGUMENT = 1;
8 NOT_FOUND = 2;
9 ALREADY_EXISTS = 3;
10 PERMISSION_DENIED = 4;
11 FAILED_PRECONDITION = 5;
12 RESOURCE_EXHAUSTED = 6;
13 UNAVAILABLE = 7;
14 INTERNAL = 8;
15 INVALID_REPOSITORY_REF = 20;
16 REPOSITORY_NOT_READY = 21;
17 NO_READY_REPLICA = 22;
18 WRITE_LEASE_BUSY = 23;
19 WRITE_LEASE_LOST = 24;
20 PRIMARY_WRITE_FAILED = 25;
21 REPLICA_SYNC_FAILED = 26;
22 WRITE_NOT_DURABLE = 27;
23 STALE_GENERATION = 28;
24}
25
26message ErrorDetail {
27 ErrorCode code = 1;
28 string message = 2;
29 string request_id = 3;
30 string repository_id = 4;
31 string node_id = 5;
32 int64 current_generation = 6;
33 bool retryable = 7;
34}
Added lib/kenshell/proto/kenshell/common/v1/types.proto lib/kenshell/proto/kenshell/common/v1/types.proto +243 −0 View lib/kenshell/proto/kenshell/common/v1/types.proto
@@ -0,0 +1,243 @@
1syntax = "proto3";
2
3package kenshell.common.v1;
4
5import "google/protobuf/duration.proto";
6import "google/protobuf/timestamp.proto";
7
8// 所有 RPC 都可携带 gRPC metadata `x-request-id`;服务端原样使用合法值,
9// 缺失或非法时生成新值,并在响应 metadata 中回写同一个 ID。
10
11// 对外仓库地址。所有公开数据 RPC 都以组织名 + 仓库名定位仓库。
12message RepositoryRef {
13 string org_name = 1;
14 string repo_name = 2;
15}
16
17message PageRequest {
18 uint32 page_size = 1;
19 string page_token = 2;
20}
21
22message PageResponse {
23 string next_page_token = 1;
24 // omitted when the backing backend cannot provide an exact total
25 optional uint64 total_size = 2;
26}
27
28// 所有展示读响应携带的路由与缓存元数据。
29message ReadMeta {
30 string repository_id = 1;
31 int64 generation = 2;
32 string etag = 3;
33 string served_by_node_id = 4;
34}
35
36message GitActor {
37 int64 user_id = 1;
38 string username = 2;
39}
40
41message WriteOptions {
42 string holder = 1;
43 google.protobuf.Duration lock_ttl = 2;
44 google.protobuf.Duration lock_wait = 3;
45 bool require_all_replicas = 4;
46}
47
48message StreamingWriteHeader {
49 RepositoryRef repository = 1;
50 WriteOptions options = 2;
51 GitActor actor = 3;
52}
53
54message ReplicaReceipt {
55 string node_id = 1;
56 string node_name = 2;
57 string placement_id = 3;
58 string volume_id = 4;
59 bool consistent = 5;
60 int64 generation = 6;
61 string error = 7;
62}
63
64message WriteReceipt {
65 string repository_id = 1;
66 int64 previous_generation = 2;
67 int64 generation = 3;
68 string primary_node_id = 4;
69 string primary_node_name = 5;
70 repeated ReplicaReceipt replicas = 6;
71 bool all_replicas_consistent = 7;
72 google.protobuf.Timestamp committed_at = 8;
73}
74
75enum NodeScheme {
76 NODE_SCHEME_UNSPECIFIED = 0;
77 NODE_SCHEME_TCP = 1;
78 NODE_SCHEME_TLS = 2;
79 NODE_SCHEME_UNIX = 3;
80}
81
82enum NodeStatus {
83 NODE_STATUS_UNSPECIFIED = 0;
84 NODE_STATUS_UNKNOWN = 1;
85 NODE_STATUS_ONLINE = 2;
86 NODE_STATUS_OFFLINE = 3;
87 NODE_STATUS_DRAINING = 4;
88}
89
90enum NodeVolumeStatus {
91 NODE_VOLUME_STATUS_UNSPECIFIED = 0;
92 NODE_VOLUME_STATUS_ACTIVE = 1;
93 NODE_VOLUME_STATUS_DEGRADED = 2;
94 NODE_VOLUME_STATUS_UNAVAILABLE = 3;
95}
96
97enum VolumeStatus {
98 VOLUME_STATUS_UNSPECIFIED = 0;
99 VOLUME_STATUS_UNKNOWN = 1;
100 VOLUME_STATUS_READY = 2;
101 VOLUME_STATUS_DEGRADED = 3;
102 VOLUME_STATUS_OFFLINE = 4;
103 VOLUME_STATUS_MAINTENANCE = 5;
104}
105
106enum RepositoryState {
107 REPOSITORY_STATE_UNSPECIFIED = 0;
108 REPOSITORY_STATE_CREATING = 1;
109 REPOSITORY_STATE_READY = 2;
110 REPOSITORY_STATE_DELETING = 3;
111 REPOSITORY_STATE_DELETED = 4;
112}
113
114enum PlacementRole {
115 PLACEMENT_ROLE_UNSPECIFIED = 0;
116 PLACEMENT_ROLE_PRIMARY = 1;
117 PLACEMENT_ROLE_REPLICA = 2;
118}
119
120enum PlacementState {
121 PLACEMENT_STATE_UNSPECIFIED = 0;
122 PLACEMENT_STATE_PENDING = 1;
123 PLACEMENT_STATE_READY = 2;
124 PLACEMENT_STATE_SYNCING = 3;
125 PLACEMENT_STATE_FAILED = 4;
126}
127
128enum LfsObjectState {
129 LFS_OBJECT_STATE_UNSPECIFIED = 0;
130 LFS_OBJECT_STATE_PENDING = 1;
131 LFS_OBJECT_STATE_READY = 2;
132 LFS_OBJECT_STATE_DELETING = 3;
133 LFS_OBJECT_STATE_DELETED = 4;
134}
135
136message TcpAddress {
137 string host = 1;
138 uint32 port = 2;
139}
140
141message UnixAddress {
142 string path = 1;
143}
144
145message NodeEndpoint {
146 NodeScheme scheme = 1;
147 oneof address {
148 TcpAddress tcp = 2;
149 UnixAddress unix = 3;
150 }
151}
152
153// auth_token 是 write-only 字段,不出现在任何 Node 响应中。
154message Node {
155 string id = 1;
156 string name = 2;
157 string description = 3;
158 NodeEndpoint endpoint = 4;
159 NodeStatus status = 5;
160 string version = 6;
161 google.protobuf.Timestamp last_health_at = 7;
162 string last_error = 8;
163 google.protobuf.Timestamp created_at = 9;
164 google.protobuf.Timestamp updated_at = 10;
165}
166
167message Volume {
168 string id = 1;
169 string name = 2;
170 string description = 3;
171 string device_id = 4;
172 string volume_key = 5;
173 string relative_root = 6;
174 VolumeStatus status = 7;
175 uint64 capacity_bytes = 8;
176 uint64 used_bytes = 9;
177 google.protobuf.Timestamp last_checked_at = 10;
178 google.protobuf.Timestamp created_at = 11;
179 google.protobuf.Timestamp updated_at = 12;
180}
181
182message NodeMount {
183 string id = 1;
184 string node_id = 2;
185 string volume_id = 3;
186 string storage_name = 4;
187 string mount_path = 5;
188 string storage_path = 6;
189 NodeVolumeStatus status = 7;
190 google.protobuf.Timestamp last_health_at = 8;
191 google.protobuf.Timestamp created_at = 9;
192 google.protobuf.Timestamp updated_at = 10;
193}
194
195message Repository {
196 string id = 1;
197 string owner_org_id = 2;
198 string name = 3;
199 RepositoryState state = 4;
200 google.protobuf.Timestamp created_at = 5;
201 google.protobuf.Timestamp updated_at = 6;
202 google.protobuf.Timestamp last_receive_at = 7;
203 google.protobuf.Timestamp deleted_at = 8;
204}
205
206message Placement {
207 string id = 1;
208 string repo_id = 2;
209 string volume_id = 3;
210 string relative_path = 4;
211 PlacementRole role = 5;
212 PlacementState state = 6;
213 int64 generation = 7;
214 string error = 8;
215 google.protobuf.Timestamp last_verified_at = 9;
216 google.protobuf.Timestamp last_replication_at = 10;
217 google.protobuf.Timestamp created_at = 11;
218 google.protobuf.Timestamp updated_at = 12;
219}
220
221message ServeTarget {
222 Node node = 1;
223 NodeMount mount = 2;
224 Placement placement = 3;
225 int64 generation = 4;
226}
227
228message PushLockInfo {
229 string repo_id = 1;
230 string holder = 2;
231 int64 acquired_at_ms = 3;
232 string node_id = 4;
233}
234
235message LfsObject {
236 string id = 1;
237 string oid = 2;
238 string storage_key = 3;
239 int64 size_bytes = 4;
240 LfsObjectState state = 5;
241 google.protobuf.Timestamp created_at = 6;
242 google.protobuf.Timestamp last_verified_at = 7;
243}
Added lib/kenshell/proto/kenshell/internal/v1/lfs.proto lib/kenshell/proto/kenshell/internal/v1/lfs.proto +64 −0 View lib/kenshell/proto/kenshell/internal/v1/lfs.proto
@@ -0,0 +1,64 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "google/protobuf/duration.proto";
6import "kenshell/common/v1/types.proto";
7
8// LFS 元数据与引用管理。对象内容上传下载继续走 HTTP LFS 链路。
9service LfsAdminService {
10 rpc StatObject(StatObjectRequest) returns (StatObjectResponse);
11 rpc ListExistingObjects(ListExistingObjectsRequest)
12 returns (ListExistingObjectsResponse);
13 rpc ListRepositoryObjects(ListRepositoryObjectsRequest)
14 returns (ListRepositoryObjectsResponse);
15 rpc UnreferenceObject(UnreferenceObjectRequest)
16 returns (UnreferenceObjectResponse);
17 rpc CreateDownloadUrl(CreateDownloadUrlRequest)
18 returns (CreateDownloadUrlResponse);
19}
20
21message StatObjectRequest {
22 string oid = 1;
23}
24
25message StatObjectResponse {
26 bool found = 1;
27 kenshell.common.v1.LfsObject object = 2;
28}
29
30message ListExistingObjectsRequest {
31 repeated string oids = 1;
32}
33
34message ListExistingObjectsResponse {
35 repeated kenshell.common.v1.LfsObject objects = 1;
36}
37
38message ListRepositoryObjectsRequest {
39 string repository_id = 1;
40 repeated string oids = 2;
41}
42
43message ListRepositoryObjectsResponse {
44 repeated kenshell.common.v1.LfsObject objects = 1;
45}
46
47message UnreferenceObjectRequest {
48 string repository_id = 1;
49 string oid = 2;
50}
51
52message UnreferenceObjectResponse {
53 bool removed = 1;
54}
55
56message CreateDownloadUrlRequest {
57 string oid = 1;
58 google.protobuf.Duration ttl = 2;
59}
60
61message CreateDownloadUrlResponse {
62 string url = 1;
63 uint32 expires_in_secs = 2;
64}
Added lib/kenshell/proto/kenshell/internal/v1/maintenance.proto lib/kenshell/proto/kenshell/internal/v1/maintenance.proto +68 −0 View lib/kenshell/proto/kenshell/internal/v1/maintenance.proto
@@ -0,0 +1,68 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "kenshell/common/v1/types.proto";
6import "gitken/v1/cleanup.proto";
7import "gitken/v1/conflicts.proto";
8
9// 管理端维护操作。params.repository 必须为空,KenShell 使用 repository_id 覆盖。
10service MaintenanceAdminService {
11 rpc ListConflictFiles(ListConflictFilesRequest)
12 returns (stream gitken.ListConflictFilesResponse);
13 rpc ResolveConflicts(stream ResolveConflictsRequest)
14 returns (ResolveConflictsResponse);
15 rpc RewriteHistory(stream RewriteHistoryRequest)
16 returns (RewriteHistoryResponse);
17 rpc ApplyBfgObjectMapStream(stream ApplyBfgObjectMapStreamRequest)
18 returns (stream ApplyBfgObjectMapStreamResponse);
19}
20
21message ListConflictFilesRequest {
22 string repository_id = 1;
23 gitken.ListConflictFilesRequest params = 2;
24}
25
26message ResolveConflictsRequest {
27 oneof payload {
28 MaintenanceWriteHeader header = 1;
29 gitken.ResolveConflictsRequest params = 2;
30 }
31}
32
33message ResolveConflictsResponse {
34 gitken.ResolveConflictsResponse result = 1;
35 kenshell.common.v1.WriteReceipt receipt = 2;
36}
37
38message RewriteHistoryRequest {
39 oneof payload {
40 MaintenanceWriteHeader header = 1;
41 gitken.RewriteHistoryRequest params = 2;
42 }
43}
44
45message MaintenanceWriteHeader {
46 string repository_id = 1;
47 kenshell.common.v1.WriteOptions options = 2;
48 kenshell.common.v1.GitActor actor = 3;
49}
50
51message RewriteHistoryResponse {
52 gitken.RewriteHistoryResponse result = 1;
53 kenshell.common.v1.WriteReceipt receipt = 2;
54}
55
56message ApplyBfgObjectMapStreamRequest {
57 oneof payload {
58 MaintenanceWriteHeader header = 1;
59 gitken.ApplyBfgObjectMapStreamRequest params = 2;
60 }
61}
62
63message ApplyBfgObjectMapStreamResponse {
64 oneof payload {
65 gitken.ApplyBfgObjectMapStreamResponse result = 1;
66 kenshell.common.v1.WriteReceipt receipt = 2;
67 }
68}
Added lib/kenshell/proto/kenshell/internal/v1/node.proto lib/kenshell/proto/kenshell/internal/v1/node.proto +99 −0 View lib/kenshell/proto/kenshell/internal/v1/node.proto
@@ -0,0 +1,99 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "google/protobuf/field_mask.proto";
6import "kenshell/common/v1/types.proto";
7
8// GitKen 节点管理。auth_token 只在 Create/Update 请求中出现。
9service NodeAdminService {
10 rpc CreateNode(CreateNodeRequest) returns (CreateNodeResponse);
11 rpc UpdateNode(UpdateNodeRequest) returns (UpdateNodeResponse);
12 rpc GetNode(GetNodeRequest) returns (GetNodeResponse);
13 rpc ListNodes(ListNodesRequest) returns (ListNodesResponse);
14 rpc DeleteNode(DeleteNodeRequest) returns (DeleteNodeResponse);
15 rpc ProbeNode(ProbeNodeRequest) returns (ProbeNodeResponse);
16}
17
18message NodeView {
19 kenshell.common.v1.Node node = 1;
20 repeated NodeMountView mounts = 2;
21}
22
23message NodeMountView {
24 kenshell.common.v1.NodeMount mount = 1;
25 kenshell.common.v1.Volume volume = 2;
26}
27
28message CreateNodeRequest {
29 string name = 1;
30 string description = 2;
31 kenshell.common.v1.NodeScheme scheme = 3;
32 string host = 4;
33 int32 port = 5;
34 string socket_path = 6;
35 // write-only;不会出现在任何响应中。
36 string auth_token = 7;
37}
38
39message CreateNodeResponse {
40 NodeView node = 1;
41}
42
43message NodePatch {
44 optional string name = 1;
45 optional string description = 2;
46 optional kenshell.common.v1.NodeScheme scheme = 3;
47 optional string host = 4;
48 optional int32 port = 5;
49 optional string socket_path = 6;
50 // write-only;只在 update_mask 包含 auth_token 时生效。
51 optional string auth_token = 7;
52}
53
54message UpdateNodeRequest {
55 string node_id = 1;
56 NodePatch patch = 2;
57 google.protobuf.FieldMask update_mask = 3;
58}
59
60message UpdateNodeResponse {
61 NodeView node = 1;
62}
63
64message GetNodeRequest {
65 oneof key {
66 string node_id = 1;
67 string name = 2;
68 }
69}
70
71message GetNodeResponse {
72 NodeView node = 1;
73}
74
75message ListNodesRequest {
76 kenshell.common.v1.NodeStatus status = 1;
77 kenshell.common.v1.NodeScheme scheme = 2;
78 string name_contains = 3;
79 kenshell.common.v1.PageRequest page = 4;
80}
81
82message ListNodesResponse {
83 repeated kenshell.common.v1.Node nodes = 1;
84 kenshell.common.v1.PageResponse page = 2;
85}
86
87message DeleteNodeRequest {
88 string node_id = 1;
89}
90
91message DeleteNodeResponse {}
92
93message ProbeNodeRequest {
94 string node_id = 1;
95}
96
97message ProbeNodeResponse {
98 NodeView node = 1;
99}
Added lib/kenshell/proto/kenshell/internal/v1/push_lock.proto lib/kenshell/proto/kenshell/internal/v1/push_lock.proto +20 −0 View lib/kenshell/proto/kenshell/internal/v1/push_lock.proto
@@ -0,0 +1,20 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "kenshell/common/v1/types.proto";
6
7// 推送锁只开放诊断读取;锁的生命周期由数据写 RPC 持有。
8service PushLockAdminService {
9 rpc GetPushLockStatus(GetPushLockStatusRequest)
10 returns (GetPushLockStatusResponse);
11}
12
13message GetPushLockStatusRequest {
14 string repository_id = 1;
15}
16
17message GetPushLockStatusResponse {
18 bool locked = 1;
19 kenshell.common.v1.PushLockInfo lock = 2;
20}
Added lib/kenshell/proto/kenshell/internal/v1/repository.proto lib/kenshell/proto/kenshell/internal/v1/repository.proto +87 −0 View lib/kenshell/proto/kenshell/internal/v1/repository.proto
@@ -0,0 +1,87 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "kenshell/common/v1/types.proto";
6
7// 仓库生命周期与 placement 诊断。普通数据 CRUD 使用 kenshell.v1。
8service RepositoryAdminService {
9 rpc CreateRepository(CreateRepositoryRequest)
10 returns (CreateRepositoryResponse);
11 rpc GetRepository(GetRepositoryRequest)
12 returns (GetRepositoryResponse);
13 rpc ListRepositories(ListRepositoriesRequest)
14 returns (ListRepositoriesResponse);
15 rpc DeleteRepository(DeleteRepositoryRequest)
16 returns (DeleteRepositoryResponse);
17 rpc ListRepositoryNodes(ListRepositoryNodesRequest)
18 returns (ListRepositoryNodesResponse);
19 rpc ListPlacements(ListPlacementsRequest)
20 returns (ListPlacementsResponse);
21}
22
23message RepositoryView {
24 kenshell.common.v1.Repository repository = 1;
25 repeated PlacementView placements = 2;
26}
27
28message PlacementView {
29 kenshell.common.v1.Placement placement = 1;
30 repeated kenshell.common.v1.Node nodes = 2;
31}
32
33message CreateRepositoryRequest {
34 string owner_org_id = 1;
35 string name = 2;
36 string volume_id = 3;
37 string default_branch = 4;
38}
39
40message CreateRepositoryResponse {
41 RepositoryView repository = 1;
42}
43
44message GetRepositoryRequest {
45 oneof key {
46 string repository_id = 1;
47 kenshell.common.v1.RepositoryRef path = 2;
48 }
49}
50
51message GetRepositoryResponse {
52 RepositoryView repository = 1;
53}
54
55message ListRepositoriesRequest {
56 string owner_org_id = 1;
57 kenshell.common.v1.RepositoryState state = 2;
58 string name_contains = 3;
59 kenshell.common.v1.PageRequest page = 4;
60}
61
62message ListRepositoriesResponse {
63 repeated kenshell.common.v1.Repository repositories = 1;
64 kenshell.common.v1.PageResponse page = 2;
65}
66
67message DeleteRepositoryRequest {
68 string repository_id = 1;
69}
70
71message DeleteRepositoryResponse {}
72
73message ListRepositoryNodesRequest {
74 string repository_id = 1;
75}
76
77message ListRepositoryNodesResponse {
78 repeated kenshell.common.v1.Node nodes = 1;
79}
80
81message ListPlacementsRequest {
82 string repository_id = 1;
83}
84
85message ListPlacementsResponse {
86 repeated kenshell.common.v1.Placement placements = 1;
87}
Added lib/kenshell/proto/kenshell/internal/v1/volume.proto lib/kenshell/proto/kenshell/internal/v1/volume.proto +134 −0 View lib/kenshell/proto/kenshell/internal/v1/volume.proto
@@ -0,0 +1,134 @@
1syntax = "proto3";
2
3package kenshell.internal.v1;
4
5import "google/protobuf/field_mask.proto";
6import "kenshell/common/v1/types.proto";
7
8// 虚拟卷声明与节点挂载管理。
9service VolumeAdminService {
10 rpc CreateVolume(CreateVolumeRequest) returns (CreateVolumeResponse);
11 rpc UpdateVolume(UpdateVolumeRequest) returns (UpdateVolumeResponse);
12 rpc GetVolume(GetVolumeRequest) returns (GetVolumeResponse);
13 rpc ListVolumes(ListVolumesRequest) returns (ListVolumesResponse);
14 rpc DeleteVolume(DeleteVolumeRequest) returns (DeleteVolumeResponse);
15 rpc MountVolume(MountVolumeRequest) returns (MountVolumeResponse);
16 rpc UnmountVolume(UnmountVolumeRequest) returns (UnmountVolumeResponse);
17 rpc DiscoverStorage(DiscoverStorageRequest)
18 returns (DiscoverStorageResponse);
19 rpc ProbeVolume(ProbeVolumeRequest) returns (ProbeVolumeResponse);
20}
21
22message VolumeView {
23 kenshell.common.v1.Volume volume = 1;
24 repeated VolumeMountView mounts = 2;
25}
26
27message VolumeMountView {
28 kenshell.common.v1.NodeMount mount = 1;
29 kenshell.common.v1.Node node = 2;
30}
31
32message CreateVolumeRequest {
33 string name = 1;
34 string description = 2;
35 string device_id = 3;
36 string relative_root = 4;
37}
38
39message CreateVolumeResponse {
40 VolumeView volume = 1;
41}
42
43message VolumePatch {
44 optional string name = 1;
45 optional string description = 2;
46 optional string device_id = 3;
47 optional string relative_root = 4;
48 optional kenshell.common.v1.VolumeStatus status = 5;
49}
50
51message UpdateVolumeRequest {
52 string volume_id = 1;
53 VolumePatch patch = 2;
54 google.protobuf.FieldMask update_mask = 3;
55}
56
57message UpdateVolumeResponse {
58 VolumeView volume = 1;
59}
60
61message GetVolumeRequest {
62 oneof key {
63 string volume_id = 1;
64 string name = 2;
65 }
66}
67
68message GetVolumeResponse {
69 VolumeView volume = 1;
70}
71
72message ListVolumesRequest {
73 kenshell.common.v1.VolumeStatus status = 1;
74 string device_id = 2;
75 string name_contains = 3;
76 kenshell.common.v1.PageRequest page = 4;
77}
78
79message ListVolumesResponse {
80 repeated kenshell.common.v1.Volume volumes = 1;
81 kenshell.common.v1.PageResponse page = 2;
82}
83
84message DeleteVolumeRequest {
85 string volume_id = 1;
86}
87
88message DeleteVolumeResponse {}
89
90message MountVolumeRequest {
91 string volume_id = 1;
92 string node_id = 2;
93 string storage_name = 3;
94 string mount_path = 4;
95 string storage_path = 5;
96}
97
98message MountVolumeResponse {
99 VolumeView volume = 1;
100}
101
102message UnmountVolumeRequest {
103 string volume_id = 1;
104 string node_id = 2;
105}
106
107message UnmountVolumeResponse {
108 VolumeView volume = 1;
109}
110
111message DiscoverStorageRequest {
112 string node_id = 1;
113}
114
115message DiscoveredStorage {
116 string storage_name = 1;
117 string path = 2;
118 string volume_key = 3;
119 string fs_type = 4;
120 optional bool network = 5;
121 kenshell.common.v1.Volume matched_volume = 6;
122}
123
124message DiscoverStorageResponse {
125 repeated DiscoveredStorage storages = 1;
126}
127
128message ProbeVolumeRequest {
129 string volume_id = 1;
130}
131
132message ProbeVolumeResponse {
133 VolumeView volume = 1;
134}
Added lib/kenshell/proto/kenshell/v1/analysis.proto lib/kenshell/proto/kenshell/v1/analysis.proto +69 −0 View lib/kenshell/proto/kenshell/v1/analysis.proto
@@ -0,0 +1,69 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6import "kenshell/v1/job.proto";
7
8service AnalysisService {
9 rpc GetLanguages(GetLanguagesRequest) returns (GetLanguagesResponse);
10 rpc StartLanguagesAnalysis(StartLanguagesAnalysisRequest) returns (StartJobResponse);
11 rpc ListContributors(ListContributorsRequest) returns (ListContributorsResponse);
12 rpc GetCodeActivity(GetCodeActivityRequest) returns (GetCodeActivityResponse);
13}
14
15message LanguageStatView {
16 string language = 1;
17 int32 percentage_bp = 2;
18 int64 size = 3;
19 bool is_primary = 4;
20}
21
22message ContributorStatView {
23 string name = 1;
24 string email = 2;
25 int64 commits = 3;
26 int64 additions = 4;
27 int64 deletions = 5;
28}
29
30message CodeActivityView {
31 repeated ContributorStatView authors = 1;
32 int64 total_commits = 2;
33 int64 total_additions = 3;
34 int64 total_deletions = 4;
35}
36
37message GetLanguagesRequest {
38 kenshell.common.v1.RepositoryRef repository = 1;
39}
40
41message GetLanguagesResponse {
42 repeated LanguageStatView languages = 1;
43 kenshell.common.v1.ReadMeta meta = 2;
44}
45
46message StartLanguagesAnalysisRequest {
47 kenshell.common.v1.RepositoryRef repository = 1;
48 kenshell.common.v1.GitActor actor = 2;
49 string idempotency_key = 3;
50}
51
52message ListContributorsRequest {
53 kenshell.common.v1.RepositoryRef repository = 1;
54}
55
56message ListContributorsResponse {
57 repeated ContributorStatView contributors = 1;
58 kenshell.common.v1.ReadMeta meta = 2;
59}
60
61message GetCodeActivityRequest {
62 kenshell.common.v1.RepositoryRef repository = 1;
63 string branch = 2;
64}
65
66message GetCodeActivityResponse {
67 CodeActivityView activity = 1;
68 kenshell.common.v1.ReadMeta meta = 2;
69}
Added lib/kenshell/proto/kenshell/v1/archive.proto lib/kenshell/proto/kenshell/v1/archive.proto +84 −0 View lib/kenshell/proto/kenshell/v1/archive.proto
@@ -0,0 +1,84 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6import "kenshell/v1/job.proto";
7import "google/protobuf/timestamp.proto";
8
9// Next.js 下载仓库 archive;服务端按 revision 路由到单个读者副本。
10service ArchiveService {
11 rpc DownloadArchive(DownloadArchiveRequest)
12 returns (stream DownloadArchiveResponse);
13 rpc GetArchiveArtifact(GetArchiveArtifactRequest)
14 returns (GetArchiveArtifactResponse);
15 rpc StartArchive(StartArchiveRequest) returns (StartJobResponse);
16 rpc StartArchiveBundle(StartArchiveBundleRequest) returns (StartJobResponse);
17}
18
19enum ArchiveFormat {
20 ARCHIVE_FORMAT_UNSPECIFIED = 0;
21 ARCHIVE_FORMAT_ZIP = 1;
22 ARCHIVE_FORMAT_TAR = 2;
23 ARCHIVE_FORMAT_TAR_GZ = 3;
24 ARCHIVE_FORMAT_TAR_BZ2 = 4;
25}
26
27message ArchiveInfo {
28 string filename = 1;
29 string revision = 2;
30 ArchiveFormat format = 3;
31 // omitted when the archive is streamed without buffering
32 optional uint64 content_length = 4;
33 kenshell.common.v1.ReadMeta meta = 5;
34}
35
36message DownloadArchiveRequest {
37 kenshell.common.v1.RepositoryRef repository = 1;
38 string revision = 2;
39 string path = 3;
40 ArchiveFormat format = 4;
41 repeated string path_filters = 5;
42}
43
44message DownloadArchiveResponse {
45 oneof payload {
46 ArchiveInfo info = 1;
47 bytes data = 2;
48 }
49}
50
51message ArchiveArtifactView {
52 ArchiveFormat format = 1;
53 uint64 size_bytes = 2;
54 string storage_key = 3;
55 string download_url = 4;
56 google.protobuf.Timestamp expires_at = 5;
57}
58
59message GetArchiveArtifactRequest {
60 kenshell.common.v1.RepositoryRef repository = 1;
61 string revision = 2;
62 ArchiveFormat format = 3;
63 repeated string path_filters = 4;
64}
65
66message GetArchiveArtifactResponse {
67 ArchiveArtifactView artifact = 1;
68 kenshell.common.v1.ReadMeta meta = 2;
69}
70
71message StartArchiveRequest {
72 kenshell.common.v1.RepositoryRef repository = 1;
73 string revision = 2;
74 ArchiveFormat format = 3;
75 repeated string path_filters = 4;
76 kenshell.common.v1.GitActor actor = 5;
77 string idempotency_key = 6;
78}
79
80message StartArchiveBundleRequest {
81 kenshell.common.v1.RepositoryRef repository = 1;
82 kenshell.common.v1.GitActor actor = 2;
83 string idempotency_key = 3;
84}
Added lib/kenshell/proto/kenshell/v1/blob.proto lib/kenshell/proto/kenshell/v1/blob.proto +65 −0 View lib/kenshell/proto/kenshell/v1/blob.proto
@@ -0,0 +1,65 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6
7service BlobService {
8 rpc GetBlobMetadata(GetBlobMetadataRequest) returns (GetBlobMetadataResponse);
9 rpc GetBlobContent(GetBlobContentRequest) returns (GetBlobContentResponse);
10 rpc ListLfsPointers(ListLfsPointersRequest) returns (ListLfsPointersResponse);
11}
12
13message LfsPointerView {
14 string version = 1;
15 string oid = 2;
16 int64 size = 3;
17}
18
19message BlobMetadataView {
20 string oid = 1;
21 int64 size = 2;
22 bool is_binary = 3;
23 bool is_lfs_pointer = 4;
24 LfsPointerView lfs = 5;
25}
26
27message BlobContentView {
28 string oid = 1;
29 bytes data = 2;
30 int64 size = 3;
31 bool is_binary = 4;
32 bool truncated = 5;
33}
34
35message GetBlobMetadataRequest {
36 kenshell.common.v1.RepositoryRef repository = 1;
37 string oid = 2;
38}
39
40message GetBlobMetadataResponse {
41 BlobMetadataView metadata = 1;
42 kenshell.common.v1.ReadMeta meta = 2;
43}
44
45message GetBlobContentRequest {
46 kenshell.common.v1.RepositoryRef repository = 1;
47 string oid = 2;
48 uint64 max_bytes = 3;
49}
50
51message GetBlobContentResponse {
52 BlobContentView content = 1;
53 kenshell.common.v1.ReadMeta meta = 2;
54}
55
56message ListLfsPointersRequest {
57 kenshell.common.v1.RepositoryRef repository = 1;
58 string revision = 2;
59 uint32 limit = 3;
60}
61
62message ListLfsPointersResponse {
63 repeated LfsPointerView pointers = 1;
64 kenshell.common.v1.ReadMeta meta = 2;
65}
Added lib/kenshell/proto/kenshell/v1/commands.proto lib/kenshell/proto/kenshell/v1/commands.proto +142 −0 View lib/kenshell/proto/kenshell/v1/commands.proto
@@ -0,0 +1,142 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6
7// Next.js 表单操作触发的仓库命令。
8// 所有命令必须等待所有副本确认后才返回 WriteReceipt。
9service CommandService {
10 rpc CreateRepository(CreateRepositoryRequest)
11 returns (CreateRepositoryResponse);
12 rpc DeleteRepository(DeleteRepositoryRequest)
13 returns (DeleteRepositoryResponse);
14 rpc CreateBranch(CreateBranchRequest) returns (CreateBranchResponse);
15 rpc UpdateBranch(UpdateBranchRequest) returns (UpdateBranchResponse);
16 rpc DeleteBranch(DeleteBranchRequest) returns (DeleteBranchResponse);
17 rpc RenameBranch(RenameBranchRequest) returns (RenameBranchResponse);
18 rpc SetDefaultBranch(SetDefaultBranchRequest)
19 returns (SetDefaultBranchResponse);
20 rpc CreateTag(CreateTagRequest) returns (CreateTagResponse);
21 rpc DeleteTag(DeleteTagRequest) returns (DeleteTagResponse);
22}
23
24message CreateRepositoryRequest {
25 kenshell.common.v1.RepositoryRef repository = 1;
26 string default_branch = 2;
27 kenshell.common.v1.GitActor actor = 3;
28 kenshell.common.v1.WriteOptions options = 4;
29}
30
31message CreateRepositoryResponse {
32 kenshell.common.v1.Repository repository = 1;
33 kenshell.common.v1.WriteReceipt receipt = 2;
34}
35
36message DeleteRepositoryRequest {
37 kenshell.common.v1.RepositoryRef repository = 1;
38 kenshell.common.v1.GitActor actor = 2;
39 kenshell.common.v1.WriteOptions options = 3;
40}
41
42message DeleteRepositoryResponse {
43 kenshell.common.v1.WriteReceipt receipt = 1;
44}
45
46message CreateBranchRequest {
47 kenshell.common.v1.RepositoryRef repository = 1;
48 string name = 2;
49 string start_point = 3;
50 kenshell.common.v1.GitActor actor = 4;
51 kenshell.common.v1.WriteOptions options = 5;
52}
53
54message CreateBranchResponse {
55 string name = 1;
56 string sha = 2;
57 kenshell.common.v1.WriteReceipt receipt = 3;
58}
59
60message UpdateBranchRequest {
61 kenshell.common.v1.RepositoryRef repository = 1;
62 string name = 2;
63 string new_sha = 3;
64 string expected_old_sha = 4;
65 kenshell.common.v1.GitActor actor = 5;
66 kenshell.common.v1.WriteOptions options = 6;
67}
68
69message UpdateBranchResponse {
70 string name = 1;
71 string previous_sha = 2;
72 string sha = 3;
73 kenshell.common.v1.WriteReceipt receipt = 4;
74}
75
76message DeleteBranchRequest {
77 kenshell.common.v1.RepositoryRef repository = 1;
78 string name = 2;
79 string expected_sha = 3;
80 kenshell.common.v1.GitActor actor = 4;
81 kenshell.common.v1.WriteOptions options = 5;
82 bool force = 6;
83}
84
85message DeleteBranchResponse {
86 kenshell.common.v1.WriteReceipt receipt = 1;
87}
88
89message RenameBranchRequest {
90 kenshell.common.v1.RepositoryRef repository = 1;
91 string name = 2;
92 string new_name = 3;
93 kenshell.common.v1.GitActor actor = 4;
94 kenshell.common.v1.WriteOptions options = 5;
95}
96
97message RenameBranchResponse {
98 string previous_name = 1;
99 string name = 2;
100 kenshell.common.v1.WriteReceipt receipt = 3;
101}
102
103message SetDefaultBranchRequest {
104 kenshell.common.v1.RepositoryRef repository = 1;
105 string name = 2;
106 kenshell.common.v1.GitActor actor = 3;
107 kenshell.common.v1.WriteOptions options = 4;
108}
109
110message SetDefaultBranchResponse {
111 string name = 1;
112 kenshell.common.v1.WriteReceipt receipt = 2;
113}
114
115message CreateTagRequest {
116 kenshell.common.v1.RepositoryRef repository = 1;
117 string name = 2;
118 string target_sha = 3;
119 string message = 4;
120 kenshell.common.v1.GitActor actor = 5;
121 kenshell.common.v1.WriteOptions options = 6;
122 bool annotated = 7;
123}
124
125message CreateTagResponse {
126 string name = 1;
127 string target_sha = 2;
128 string tag_sha = 3;
129 kenshell.common.v1.WriteReceipt receipt = 4;
130}
131
132message DeleteTagRequest {
133 kenshell.common.v1.RepositoryRef repository = 1;
134 string name = 2;
135 string expected_sha = 3;
136 kenshell.common.v1.GitActor actor = 4;
137 kenshell.common.v1.WriteOptions options = 5;
138}
139
140message DeleteTagResponse {
141 kenshell.common.v1.WriteReceipt receipt = 1;
142}
Added lib/kenshell/proto/kenshell/v1/comment.proto lib/kenshell/proto/kenshell/v1/comment.proto +81 −0 View lib/kenshell/proto/kenshell/v1/comment.proto
@@ -0,0 +1,81 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/timestamp.proto";
6import "kenshell/common/v1/types.proto";
7
8// 代码行评论。用户身份由调用方通过 user_id 传入,KenShell 不维护用户体系。
9service CodeCommentService {
10 rpc ListCodeComments(ListCodeCommentsRequest)
11 returns (ListCodeCommentsResponse);
12 rpc CreateCodeComment(CreateCodeCommentRequest)
13 returns (CreateCodeCommentResponse);
14 rpc UpdateCodeComment(UpdateCodeCommentRequest)
15 returns (UpdateCodeCommentResponse);
16 rpc DeleteCodeComment(DeleteCodeCommentRequest)
17 returns (DeleteCodeCommentResponse);
18}
19
20message CodeCommentView {
21 string id = 1;
22 string commit_sha = 2;
23 // path 是 UTF-8 展示值;path_bytes 是 Git 路径的原始字节。
24 string path = 3;
25 bytes path_bytes = 4;
26 uint32 line_number = 5;
27 string body = 6;
28 int64 user_id = 7;
29 google.protobuf.Timestamp created_at = 8;
30 google.protobuf.Timestamp updated_at = 9;
31}
32
33message ListCodeCommentsRequest {
34 kenshell.common.v1.RepositoryRef repository = 1;
35 string commit_sha = 2;
36 string path = 3;
37 bytes path_bytes = 4;
38 optional uint32 line_number = 5;
39}
40
41message ListCodeCommentsResponse {
42 repeated CodeCommentView comments = 1;
43 kenshell.common.v1.ReadMeta meta = 2;
44}
45
46message CreateCodeCommentRequest {
47 kenshell.common.v1.RepositoryRef repository = 1;
48 // revision 可以是分支、标签或提交 SHA,入库存解析后的不可变 SHA。
49 string revision = 2;
50 string path = 3;
51 uint32 line_number = 4;
52 string body = 5;
53 int64 user_id = 6;
54 bytes path_bytes = 7;
55}
56
57message CreateCodeCommentResponse {
58 CodeCommentView comment = 1;
59 kenshell.common.v1.ReadMeta meta = 2;
60}
61
62message UpdateCodeCommentRequest {
63 kenshell.common.v1.RepositoryRef repository = 1;
64 string id = 2;
65 int64 user_id = 3;
66 string body = 4;
67}
68
69message UpdateCodeCommentResponse {
70 CodeCommentView comment = 1;
71}
72
73message DeleteCodeCommentRequest {
74 kenshell.common.v1.RepositoryRef repository = 1;
75 string id = 2;
76 int64 user_id = 3;
77}
78
79message DeleteCodeCommentResponse {
80 bool removed = 1;
81}
Added lib/kenshell/proto/kenshell/v1/commit.proto lib/kenshell/proto/kenshell/v1/commit.proto +195 −0 View lib/kenshell/proto/kenshell/v1/commit.proto
@@ -0,0 +1,195 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/timestamp.proto";
6import "kenshell/common/v1/types.proto";
7
8// Next.js commit 历史、详情、统计与 blame 展示。
9service CommitService {
10 rpc ListCommits(ListCommitsRequest) returns (ListCommitsResponse);
11 rpc GetCommit(GetCommitRequest) returns (GetCommitResponse);
12 rpc GetCommitStats(GetCommitStatsRequest)
13 returns (GetCommitStatsResponse);
14 rpc ListBlame(ListBlameRequest) returns (ListBlameResponse);
15 rpc RevParse(RevParseRequest) returns (RevParseResponse);
16 rpc IsAncestor(IsAncestorRequest) returns (IsAncestorResponse);
17 rpc SetCommitStatus(SetCommitStatusRequest) returns (SetCommitStatusResponse);
18 rpc ListCommitStatuses(ListCommitStatusesRequest)
19 returns (ListCommitStatusesResponse);
20 rpc SyncCommit(SyncCommitRequest) returns (SyncCommitResponse);
21}
22
23message CommitPerson {
24 string name = 1;
25 string email = 2;
26 google.protobuf.Timestamp at = 3;
27}
28
29message CommitSignature {
30 bool present = 1;
31 bool verified = 2;
32 string key_id = 3;
33 string signer = 4;
34}
35
36message CommitView {
37 string id = 1;
38 repeated string parent_ids = 2;
39 string tree_id = 3;
40 string title = 4;
41 string message = 5;
42 CommitPerson author = 6;
43 CommitPerson committer = 7;
44 CommitSignature signature = 8;
45}
46
47message CommitStatsView {
48 string commit_id = 1;
49 uint32 additions = 2;
50 uint32 deletions = 3;
51 uint32 changed_files = 4;
52}
53
54message BlameLineView {
55 uint32 line_number = 1;
56 string commit_id = 2;
57 string author_name = 3;
58 string author_email = 4;
59 google.protobuf.Timestamp authored_at = 5;
60 string content = 6;
61}
62
63message ListCommitsRequest {
64 kenshell.common.v1.RepositoryRef repository = 1;
65 string revision = 2;
66 string path = 3;
67 string query = 4;
68 bool first_parent = 5;
69 kenshell.common.v1.PageRequest page = 6;
70 string since = 7;
71 string until = 8;
72}
73
74message ListCommitsResponse {
75 repeated CommitView commits = 1;
76 kenshell.common.v1.PageResponse page = 2;
77 kenshell.common.v1.ReadMeta meta = 3;
78 // omitted when the backing backend cannot provide an exact total
79 optional uint64 total = 4;
80 bool has_more = 5;
81 string diff_view_style = 6;
82}
83
84message GetCommitRequest {
85 kenshell.common.v1.RepositoryRef repository = 1;
86 string commit_id = 2;
87}
88
89message GetCommitResponse {
90 CommitView commit = 1;
91 kenshell.common.v1.ReadMeta meta = 2;
92}
93
94message GetCommitStatsRequest {
95 kenshell.common.v1.RepositoryRef repository = 1;
96 string commit_id = 2;
97 string path = 3;
98}
99
100message GetCommitStatsResponse {
101 CommitStatsView stats = 1;
102 kenshell.common.v1.ReadMeta meta = 2;
103}
104
105message ListBlameRequest {
106 kenshell.common.v1.RepositoryRef repository = 1;
107 string revision = 2;
108 string path = 3;
109 kenshell.common.v1.PageRequest page = 4;
110}
111
112message ListBlameResponse {
113 repeated BlameLineView lines = 1;
114 kenshell.common.v1.PageResponse page = 2;
115 kenshell.common.v1.ReadMeta meta = 3;
116}
117
118message RevParseRequest {
119 kenshell.common.v1.RepositoryRef repository = 1;
120 string revision = 2;
121}
122
123message RevParseResponse {
124 string oid = 1;
125 kenshell.common.v1.ReadMeta meta = 2;
126}
127
128message IsAncestorRequest {
129 kenshell.common.v1.RepositoryRef repository = 1;
130 string ancestor = 2;
131 string descendant = 3;
132}
133
134message IsAncestorResponse {
135 bool is_ancestor = 1;
136 kenshell.common.v1.ReadMeta meta = 2;
137}
138
139enum CommitStatusState {
140 COMMIT_STATUS_STATE_UNSPECIFIED = 0;
141 COMMIT_STATUS_STATE_PENDING = 1;
142 COMMIT_STATUS_STATE_SUCCESS = 2;
143 COMMIT_STATUS_STATE_FAILURE = 3;
144 COMMIT_STATUS_STATE_ERROR = 4;
145}
146
147message CommitStatusCreator {
148 int64 user_id = 1;
149 string username = 2;
150}
151
152message CommitStatusView {
153 string sha = 1;
154 CommitStatusState state = 2;
155 string context = 3;
156 string description = 4;
157 string target_url = 5;
158 CommitStatusCreator creator = 6;
159 google.protobuf.Timestamp created_at = 7;
160 google.protobuf.Timestamp updated_at = 8;
161}
162
163message SetCommitStatusRequest {
164 kenshell.common.v1.RepositoryRef repository = 1;
165 string sha = 2;
166 CommitStatusState state = 3;
167 string context = 4;
168 string description = 5;
169 string target_url = 6;
170 kenshell.common.v1.GitActor actor = 7;
171}
172
173message SetCommitStatusResponse {
174 CommitStatusView status = 1;
175}
176
177message ListCommitStatusesRequest {
178 kenshell.common.v1.RepositoryRef repository = 1;
179 string sha = 2;
180}
181
182message ListCommitStatusesResponse {
183 repeated CommitStatusView statuses = 1;
184 kenshell.common.v1.ReadMeta meta = 2;
185}
186
187message SyncCommitRequest {
188 kenshell.common.v1.RepositoryRef repository = 1;
189 string revision = 2;
190}
191
192message SyncCommitResponse {
193 string repository_id = 1;
194 string sha = 2;
195}
Added lib/kenshell/proto/kenshell/v1/conflict.proto lib/kenshell/proto/kenshell/v1/conflict.proto +41 −0 View lib/kenshell/proto/kenshell/v1/conflict.proto
@@ -0,0 +1,41 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6
7service ConflictService {
8 rpc CheckConflicts(CheckConflictsRequest) returns (CheckConflictsResponse);
9 rpc GetConflictDetail(GetConflictDetailRequest) returns (GetConflictDetailResponse);
10}
11
12message ConflictStatusView {
13 bool mergeable = 1;
14 bool fast_forwardable = 2;
15 repeated string paths = 3;
16}
17
18message CheckConflictsRequest {
19 kenshell.common.v1.RepositoryRef repository = 1;
20 string base_revision = 2;
21 string head_revision = 3;
22 string ancestor_revision = 4;
23}
24
25message CheckConflictsResponse {
26 ConflictStatusView status = 1;
27 kenshell.common.v1.ReadMeta meta = 2;
28}
29
30message GetConflictDetailRequest {
31 kenshell.common.v1.RepositoryRef repository = 1;
32 string base_revision = 2;
33 string head_revision = 3;
34 string path = 4;
35}
36
37message GetConflictDetailResponse {
38 string path = 1;
39 string content = 2;
40 kenshell.common.v1.ReadMeta meta = 3;
41}
Added lib/kenshell/proto/kenshell/v1/diff.proto lib/kenshell/proto/kenshell/v1/diff.proto +197 −0 View lib/kenshell/proto/kenshell/v1/diff.proto
@@ -0,0 +1,197 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6
7// Next.js commit diff 与 revision 对比展示。
8service DiffService {
9 rpc GetCommitDiff(GetCommitDiffRequest) returns (GetCommitDiffResponse);
10 rpc CompareCommits(CompareCommitsRequest) returns (CompareCommitsResponse);
11 rpc GetRawDiff(GetRawDiffRequest) returns (stream RawDiffResponse);
12 rpc GetDiffShortStat(GetDiffShortStatRequest) returns (GetDiffShortStatResponse);
13 rpc ListDiffNumStats(ListDiffNumStatsRequest) returns (ListDiffNumStatsResponse);
14 rpc ListChangedPaths(ListChangedPathsRequest) returns (ListChangedPathsResponse);
15 rpc ListDiffTree(ListDiffTreeRequest) returns (ListDiffTreeResponse);
16 rpc GetRangeDiff(GetRangeDiffRequest) returns (stream RangeDiffResponse);
17}
18
19enum DiffFileStatus {
20 DIFF_FILE_STATUS_UNSPECIFIED = 0;
21 DIFF_FILE_STATUS_ADDED = 1;
22 DIFF_FILE_STATUS_MODIFIED = 2;
23 DIFF_FILE_STATUS_DELETED = 3;
24 DIFF_FILE_STATUS_RENAMED = 4;
25 DIFF_FILE_STATUS_COPIED = 5;
26 DIFF_FILE_STATUS_TYPE_CHANGED = 6;
27}
28
29enum DiffLineKind {
30 DIFF_LINE_KIND_UNSPECIFIED = 0;
31 DIFF_LINE_KIND_CONTEXT = 1;
32 DIFF_LINE_KIND_ADDITION = 2;
33 DIFF_LINE_KIND_DELETION = 3;
34 DIFF_LINE_KIND_NO_NEWLINE = 4;
35}
36
37message DiffLineView {
38 DiffLineKind kind = 1;
39 string content = 2;
40 uint32 old_line = 3;
41 uint32 new_line = 4;
42}
43
44message DiffHunkView {
45 string header = 1;
46 uint32 old_start = 2;
47 uint32 old_lines = 3;
48 uint32 new_start = 4;
49 uint32 new_lines = 5;
50 repeated DiffLineView lines = 6;
51}
52
53message DiffFileView {
54 string old_path = 1;
55 string new_path = 2;
56 DiffFileStatus status = 3;
57 string old_oid = 4;
58 string new_oid = 5;
59 bool binary = 6;
60 bool truncated = 7;
61 uint32 additions = 8;
62 uint32 deletions = 9;
63 repeated DiffHunkView hunks = 10;
64}
65
66message GetCommitDiffRequest {
67 kenshell.common.v1.RepositoryRef repository = 1;
68 string commit_id = 2;
69 string path = 3;
70 kenshell.common.v1.PageRequest page = 4;
71}
72
73message GetCommitDiffResponse {
74 repeated DiffFileView files = 1;
75 kenshell.common.v1.PageResponse page = 2;
76 kenshell.common.v1.ReadMeta meta = 3;
77}
78
79message CompareCommitsRequest {
80 kenshell.common.v1.RepositoryRef repository = 1;
81 string base_revision = 2;
82 string head_revision = 3;
83 bool merge_base = 4;
84 string path = 5;
85 kenshell.common.v1.PageRequest page = 6;
86}
87
88message CompareCommitsResponse {
89 string base_commit = 1;
90 string head_commit = 2;
91 repeated DiffFileView files = 3;
92 kenshell.common.v1.PageResponse page = 4;
93 kenshell.common.v1.ReadMeta meta = 5;
94}
95
96message GetRawDiffRequest {
97 kenshell.common.v1.RepositoryRef repository = 1;
98 string base_revision = 2;
99 string head_revision = 3;
100}
101
102message RawDiffResponse {
103 bytes data = 1;
104}
105
106message DiffShortStatView {
107 uint64 files_changed = 1;
108 uint64 additions = 2;
109 uint64 deletions = 3;
110}
111
112message GetDiffShortStatRequest {
113 kenshell.common.v1.RepositoryRef repository = 1;
114 string base_revision = 2;
115 string head_revision = 3;
116}
117
118message GetDiffShortStatResponse {
119 DiffShortStatView stats = 1;
120 kenshell.common.v1.ReadMeta meta = 2;
121}
122
123message DiffNumStatView {
124 string path = 1;
125 uint64 additions = 2;
126 uint64 deletions = 3;
127 bool binary = 4;
128}
129
130message ListDiffNumStatsRequest {
131 kenshell.common.v1.RepositoryRef repository = 1;
132 string base_revision = 2;
133 string head_revision = 3;
134 repeated string paths = 4;
135}
136
137message ListDiffNumStatsResponse {
138 repeated DiffNumStatView stats = 1;
139 kenshell.common.v1.ReadMeta meta = 2;
140}
141
142message ChangedPathView {
143 string old_path = 1;
144 string new_path = 2;
145 string status = 3;
146 uint32 old_mode = 4;
147 uint32 new_mode = 5;
148 string old_oid = 6;
149 string new_oid = 7;
150}
151
152message ListChangedPathsRequest {
153 kenshell.common.v1.RepositoryRef repository = 1;
154 string base_revision = 2;
155 string head_revision = 3;
156 string commit_id = 4;
157}
158
159message ListChangedPathsResponse {
160 repeated ChangedPathView paths = 1;
161 kenshell.common.v1.ReadMeta meta = 2;
162}
163
164message DiffTreeRecordView {
165 string status = 1;
166 uint32 score = 2;
167 string head_path = 3;
168 string base_path = 4;
169 uint32 head_mode = 5;
170 uint32 base_mode = 6;
171 string head_blob_id = 7;
172 string base_blob_id = 8;
173}
174
175message ListDiffTreeRequest {
176 kenshell.common.v1.RepositoryRef repository = 1;
177 string base_revision = 2;
178 string head_revision = 3;
179 bool use_merge_base = 4;
180 repeated string paths = 5;
181}
182
183message ListDiffTreeResponse {
184 repeated DiffTreeRecordView records = 1;
185 kenshell.common.v1.ReadMeta meta = 2;
186}
187
188message GetRangeDiffRequest {
189 kenshell.common.v1.RepositoryRef repository = 1;
190 string from = 2;
191 string to = 3;
192 string base = 4;
193}
194
195message RangeDiffResponse {
196 bytes data = 1;
197}
Added lib/kenshell/proto/kenshell/v1/job.proto lib/kenshell/proto/kenshell/v1/job.proto +127 −0 View lib/kenshell/proto/kenshell/v1/job.proto
@@ -0,0 +1,127 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/timestamp.proto";
6import "kenshell/common/v1/types.proto";
7
8service JobService {
9 rpc GetJob(GetJobRequest) returns (GetJobResponse);
10 rpc ListJobs(ListJobsRequest) returns (ListJobsResponse);
11 rpc CancelJob(CancelJobRequest) returns (CancelJobResponse);
12 rpc RetryJob(RetryJobRequest) returns (RetryJobResponse);
13}
14
15enum JobKind {
16 JOB_KIND_UNSPECIFIED = 0;
17 JOB_KIND_REMOTE_IMPORT = 1;
18 JOB_KIND_REMOTE_FETCH = 2;
19 JOB_KIND_REMOTE_PUSH = 3;
20 JOB_KIND_ARCHIVE_CREATE = 4;
21 JOB_KIND_ARCHIVE_BUNDLE = 5;
22 JOB_KIND_ANALYSIS_LANGUAGES = 6;
23}
24
25enum JobState {
26 JOB_STATE_UNSPECIFIED = 0;
27 JOB_STATE_QUEUED = 1;
28 JOB_STATE_RUNNING = 2;
29 JOB_STATE_SUCCEEDED = 3;
30 JOB_STATE_FAILED = 4;
31 JOB_STATE_CANCELED = 5;
32}
33
34message JobRef {
35 string id = 1;
36 JobKind kind = 2;
37 JobState state = 3;
38}
39
40message StartJobResponse {
41 JobRef job = 1;
42}
43
44message ArchiveJobResult {
45 string format = 1;
46 uint64 size_bytes = 2;
47 string storage_key = 3;
48 string download_url = 4;
49 google.protobuf.Timestamp expires_at = 5;
50}
51
52message RemoteJobResult {
53 string default_branch = 1;
54 uint64 commit_count = 2;
55 repeated string updated = 3;
56 repeated string new_refs = 4;
57 repeated string pushed = 5;
58 repeated string rejected = 6;
59}
60
61message LanguageJobItem {
62 string language = 1;
63 int32 percentage_bp = 2;
64 int64 size = 3;
65 bool is_primary = 4;
66}
67
68message LanguageJobResult {
69 repeated LanguageJobItem languages = 1;
70}
71
72message Job {
73 string id = 1;
74 JobKind kind = 2;
75 JobState state = 3;
76 kenshell.common.v1.RepositoryRef repository = 4;
77 int64 requested_by = 5;
78 uint32 progress_percent = 6;
79 string stage = 7;
80 uint32 attempt = 8;
81 string error_code = 9;
82 string error_message = 10;
83 google.protobuf.Timestamp created_at = 11;
84 google.protobuf.Timestamp started_at = 12;
85 google.protobuf.Timestamp finished_at = 13;
86 oneof result {
87 ArchiveJobResult archive = 14;
88 RemoteJobResult remote = 15;
89 LanguageJobResult languages = 16;
90 }
91}
92
93message GetJobRequest {
94 string id = 1;
95}
96
97message GetJobResponse {
98 Job job = 1;
99}
100
101message ListJobsRequest {
102 kenshell.common.v1.RepositoryRef repository = 1;
103 repeated JobKind kinds = 2;
104 repeated JobState states = 3;
105 kenshell.common.v1.PageRequest page = 4;
106}
107
108message ListJobsResponse {
109 repeated Job jobs = 1;
110 kenshell.common.v1.PageResponse page = 2;
111}
112
113message CancelJobRequest {
114 string id = 1;
115}
116
117message CancelJobResponse {
118 Job job = 1;
119}
120
121message RetryJobRequest {
122 string id = 1;
123}
124
125message RetryJobResponse {
126 Job job = 1;
127}
Added lib/kenshell/proto/kenshell/v1/lfs.proto lib/kenshell/proto/kenshell/v1/lfs.proto +89 −0 View lib/kenshell/proto/kenshell/v1/lfs.proto
@@ -0,0 +1,89 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/duration.proto";
6import "google/protobuf/timestamp.proto";
7import "kenshell/common/v1/types.proto";
8
9// Next.js LFS 对象展示。对象上传下载继续使用现有 HTTP LFS 协议。
10service LfsService {
11 rpc GetLfsObject(GetLfsObjectRequest) returns (GetLfsObjectResponse);
12 rpc BatchGetLfsObjects(BatchGetLfsObjectsRequest)
13 returns (BatchGetLfsObjectsResponse);
14 rpc ListRepositoryLfsObjects(ListRepositoryLfsObjectsRequest)
15 returns (ListRepositoryLfsObjectsResponse);
16 rpc ResolveLfsPointers(ResolveLfsPointersRequest)
17 returns (ResolveLfsPointersResponse);
18 rpc CreateDownloadUrl(CreateDownloadUrlRequest)
19 returns (CreateDownloadUrlResponse);
20}
21
22message LfsObjectView {
23 string oid = 1;
24 int64 size_bytes = 2;
25 string media_type = 3;
26 kenshell.common.v1.LfsObjectState state = 4;
27 bool ready = 5;
28 google.protobuf.Timestamp created_at = 6;
29 google.protobuf.Timestamp last_verified_at = 7;
30}
31
32message LfsPointerInput {
33 string oid = 1;
34 int64 size_bytes = 2;
35}
36
37message GetLfsObjectRequest {
38 kenshell.common.v1.RepositoryRef repository = 1;
39 string oid = 2;
40}
41
42message GetLfsObjectResponse {
43 LfsObjectView object = 1;
44 kenshell.common.v1.ReadMeta meta = 2;
45}
46
47message BatchGetLfsObjectsRequest {
48 kenshell.common.v1.RepositoryRef repository = 1;
49 repeated string oids = 2;
50}
51
52message BatchGetLfsObjectsResponse {
53 repeated LfsObjectView objects = 1;
54 kenshell.common.v1.ReadMeta meta = 2;
55}
56
57message ListRepositoryLfsObjectsRequest {
58 kenshell.common.v1.RepositoryRef repository = 1;
59 string query = 2;
60 kenshell.common.v1.PageRequest page = 3;
61}
62
63message ListRepositoryLfsObjectsResponse {
64 repeated LfsObjectView objects = 1;
65 kenshell.common.v1.PageResponse page = 2;
66 kenshell.common.v1.ReadMeta meta = 3;
67}
68
69message ResolveLfsPointersRequest {
70 kenshell.common.v1.RepositoryRef repository = 1;
71 repeated LfsPointerInput pointers = 2;
72}
73
74message ResolveLfsPointersResponse {
75 repeated LfsObjectView objects = 1;
76 kenshell.common.v1.ReadMeta meta = 2;
77}
78
79message CreateDownloadUrlRequest {
80 kenshell.common.v1.RepositoryRef repository = 1;
81 string oid = 2;
82 google.protobuf.Duration ttl = 3;
83}
84
85message CreateDownloadUrlResponse {
86 string url = 1;
87 google.protobuf.Timestamp expires_at = 2;
88 kenshell.common.v1.ReadMeta meta = 3;
89}
Added lib/kenshell/proto/kenshell/v1/ref.proto lib/kenshell/proto/kenshell/v1/ref.proto +185 −0 View lib/kenshell/proto/kenshell/v1/ref.proto
@@ -0,0 +1,185 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/timestamp.proto";
6import "kenshell/common/v1/types.proto";
7
8// Next.js branches、tags 与 refs 展示。
9service RefService {
10 rpc GetDefaultBranch(GetDefaultBranchRequest)
11 returns (GetDefaultBranchResponse);
12 rpc GetBranch(GetBranchRequest) returns (GetBranchResponse);
13 rpc GetTag(GetTagRequest) returns (GetTagResponse);
14 rpc ListBranches(ListBranchesRequest) returns (ListBranchesResponse);
15 rpc ListTags(ListTagsRequest) returns (ListTagsResponse);
16 rpc ListRefs(ListRefsRequest) returns (ListRefsResponse);
17 rpc GetRef(GetRefRequest) returns (GetRefResponse);
18 rpc ListBranchProtections(ListBranchProtectionsRequest)
19 returns (ListBranchProtectionsResponse);
20 rpc CreateBranchProtection(CreateBranchProtectionRequest)
21 returns (CreateBranchProtectionResponse);
22 rpc DeleteBranchProtection(DeleteBranchProtectionRequest)
23 returns (DeleteBranchProtectionResponse);
24}
25
26enum BranchSort {
27 BRANCH_SORT_UNSPECIFIED = 0;
28 BRANCH_SORT_NAME_ASC = 1;
29 BRANCH_SORT_UPDATED_DESC = 2;
30 BRANCH_SORT_UPDATED_ASC = 3;
31}
32
33enum TagSort {
34 TAG_SORT_UNSPECIFIED = 0;
35 TAG_SORT_NAME_ASC = 1;
36 TAG_SORT_CREATED_DESC = 2;
37 TAG_SORT_CREATED_ASC = 3;
38}
39
40enum RefType {
41 REF_TYPE_UNSPECIFIED = 0;
42 REF_TYPE_BRANCH = 1;
43 REF_TYPE_TAG = 2;
44 REF_TYPE_REMOTE_BRANCH = 3;
45 REF_TYPE_OTHER = 4;
46}
47
48message BranchView {
49 string name = 1;
50 string sha = 2;
51 google.protobuf.Timestamp updated_at = 3;
52 bool is_protected = 4;
53 bool is_deleted = 5;
54 google.protobuf.Timestamp deleted_at = 6;
55 google.protobuf.Timestamp created_at = 7;
56 google.protobuf.Timestamp repository_updated_at = 8;
57}
58
59message BranchProtectionView {
60 string id = 1;
61 string branch = 2;
62 string ref_name = 3;
63 google.protobuf.Timestamp created_at = 4;
64 google.protobuf.Timestamp updated_at = 5;
65}
66
67message TagView {
68 string name = 1;
69 string target_sha = 2;
70 bool annotated = 3;
71 string tagger_name = 4;
72 string message = 5;
73 google.protobuf.Timestamp created_at = 6;
74 string tagger_email = 7;
75}
76
77message RefView {
78 string name = 1;
79 string sha = 2;
80 RefType type = 3;
81}
82
83message GetDefaultBranchRequest {
84 kenshell.common.v1.RepositoryRef repository = 1;
85}
86
87message GetDefaultBranchResponse {
88 string name = 1;
89 kenshell.common.v1.ReadMeta meta = 2;
90}
91
92message GetBranchRequest {
93 kenshell.common.v1.RepositoryRef repository = 1;
94 string name = 2;
95}
96
97message GetBranchResponse {
98 BranchView branch = 1;
99 kenshell.common.v1.ReadMeta meta = 2;
100}
101
102message GetTagRequest {
103 kenshell.common.v1.RepositoryRef repository = 1;
104 string name = 2;
105}
106
107message GetTagResponse {
108 TagView tag = 1;
109 kenshell.common.v1.ReadMeta meta = 2;
110}
111
112message ListBranchesRequest {
113 kenshell.common.v1.RepositoryRef repository = 1;
114 string query = 2;
115 BranchSort sort = 3;
116 kenshell.common.v1.PageRequest page = 4;
117}
118
119message ListBranchesResponse {
120 repeated BranchView branches = 1;
121 kenshell.common.v1.PageResponse page = 2;
122 kenshell.common.v1.ReadMeta meta = 3;
123}
124
125message ListTagsRequest {
126 kenshell.common.v1.RepositoryRef repository = 1;
127 string query = 2;
128 TagSort sort = 3;
129 kenshell.common.v1.PageRequest page = 4;
130}
131
132message ListTagsResponse {
133 repeated TagView tags = 1;
134 kenshell.common.v1.PageResponse page = 2;
135 kenshell.common.v1.ReadMeta meta = 3;
136}
137
138message ListRefsRequest {
139 kenshell.common.v1.RepositoryRef repository = 1;
140 string prefix = 2;
141 kenshell.common.v1.PageRequest page = 3;
142}
143
144message ListRefsResponse {
145 repeated RefView refs = 1;
146 kenshell.common.v1.PageResponse page = 2;
147 kenshell.common.v1.ReadMeta meta = 3;
148}
149
150message GetRefRequest {
151 kenshell.common.v1.RepositoryRef repository = 1;
152 string name = 2;
153}
154
155message GetRefResponse {
156 RefView ref = 1;
157 kenshell.common.v1.ReadMeta meta = 2;
158}
159
160message ListBranchProtectionsRequest {
161 kenshell.common.v1.RepositoryRef repository = 1;
162}
163
164message ListBranchProtectionsResponse {
165 repeated BranchProtectionView protections = 1;
166}
167
168message CreateBranchProtectionRequest {
169 kenshell.common.v1.RepositoryRef repository = 1;
170 // Accepts "main" or "refs/heads/main"; stored as the normalized full ref.
171 string branch = 2;
172}
173
174message CreateBranchProtectionResponse {
175 BranchProtectionView protection = 1;
176}
177
178message DeleteBranchProtectionRequest {
179 kenshell.common.v1.RepositoryRef repository = 1;
180 string id = 2;
181}
182
183message DeleteBranchProtectionResponse {
184 bool removed = 1;
185}
Added lib/kenshell/proto/kenshell/v1/remote.proto lib/kenshell/proto/kenshell/v1/remote.proto +91 −0 View lib/kenshell/proto/kenshell/v1/remote.proto
@@ -0,0 +1,91 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6import "kenshell/v1/job.proto";
7
8service RemoteService {
9 rpc ListRemotes(ListRemotesRequest) returns (ListRemotesResponse);
10 rpc UpsertRemote(UpsertRemoteRequest) returns (UpsertRemoteResponse);
11 rpc RemoveRemote(RemoveRemoteRequest) returns (RemoveRemoteResponse);
12 rpc StartRemoteImport(StartRemoteImportRequest) returns (StartJobResponse);
13 rpc StartRemoteFetch(StartRemoteFetchRequest) returns (StartJobResponse);
14 rpc StartRemotePush(StartRemotePushRequest) returns (StartJobResponse);
15}
16
17message RemoteView {
18 string name = 1;
19 string fetch_url = 2;
20 string push_url = 3;
21}
22
23message RemoteAuth {
24 string username = 1;
25 // Request-only. Never serialized in a response or Job view.
26 string token = 2;
27 string authorization_header = 3;
28 string ssh_key = 4;
29 string known_hosts = 5;
30 string resolved_address = 6;
31}
32
33message ListRemotesRequest {
34 kenshell.common.v1.RepositoryRef repository = 1;
35}
36
37message ListRemotesResponse {
38 repeated RemoteView remotes = 1;
39 kenshell.common.v1.ReadMeta meta = 2;
40}
41
42message UpsertRemoteRequest {
43 kenshell.common.v1.RepositoryRef repository = 1;
44 string name = 2;
45 string url = 3;
46 kenshell.common.v1.GitActor actor = 4;
47 kenshell.common.v1.WriteOptions options = 5;
48}
49
50message UpsertRemoteResponse {
51 RemoteView remote = 1;
52 kenshell.common.v1.WriteReceipt receipt = 2;
53}
54
55message RemoveRemoteRequest {
56 kenshell.common.v1.RepositoryRef repository = 1;
57 string name = 2;
58 kenshell.common.v1.GitActor actor = 3;
59 kenshell.common.v1.WriteOptions options = 4;
60}
61
62message RemoveRemoteResponse {
63 kenshell.common.v1.WriteReceipt receipt = 1;
64}
65
66message StartRemoteImportRequest {
67 kenshell.common.v1.RepositoryRef repository = 1;
68 string url = 2;
69 RemoteAuth auth = 3;
70 kenshell.common.v1.GitActor actor = 4;
71 string idempotency_key = 5;
72}
73
74message StartRemoteFetchRequest {
75 kenshell.common.v1.RepositoryRef repository = 1;
76 string remote = 2;
77 RemoteAuth auth = 3;
78 kenshell.common.v1.GitActor actor = 4;
79 string idempotency_key = 5;
80}
81
82message StartRemotePushRequest {
83 kenshell.common.v1.RepositoryRef repository = 1;
84 string remote = 2;
85 string refspec = 3;
86 bool force = 4;
87 RemoteAuth auth = 5;
88 kenshell.common.v1.GitActor actor = 6;
89 string idempotency_key = 7;
90}
91
Added lib/kenshell/proto/kenshell/v1/repository.proto lib/kenshell/proto/kenshell/v1/repository.proto +77 −0 View lib/kenshell/proto/kenshell/v1/repository.proto
@@ -0,0 +1,77 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "google/protobuf/timestamp.proto";
6import "kenshell/common/v1/types.proto";
7
8// Next.js 仓库总览与仓库列表。
9service RepositoryService {
10 rpc GetRepository(GetRepositoryRequest)
11 returns (GetRepositoryResponse);
12 rpc RepositoryExists(RepositoryExistsRequest)
13 returns (RepositoryExistsResponse);
14 rpc ListRepositories(ListRepositoriesRequest)
15 returns (ListRepositoriesResponse);
16 rpc GetRepositorySize(GetRepositorySizeRequest)
17 returns (GetRepositorySizeResponse);
18}
19
20message RepositorySummary {
21 string id = 1;
22 string org_name = 2;
23 string repo_name = 3;
24 kenshell.common.v1.RepositoryState state = 4;
25 string default_branch = 5;
26 google.protobuf.Timestamp created_at = 6;
27 google.protobuf.Timestamp updated_at = 7;
28 google.protobuf.Timestamp last_receive_at = 8;
29 string primary_language = 9;
30}
31
32message RepositoryOverview {
33 RepositorySummary repository = 1;
34 uint64 size_bytes = 2;
35 string object_format = 3;
36 uint32 branch_count = 4;
37 uint32 tag_count = 5;
38 kenshell.common.v1.ReadMeta meta = 6;
39}
40
41message GetRepositoryRequest {
42 kenshell.common.v1.RepositoryRef repository = 1;
43}
44
45message GetRepositoryResponse {
46 RepositoryOverview repository = 1;
47}
48
49message RepositoryExistsRequest {
50 kenshell.common.v1.RepositoryRef repository = 1;
51}
52
53message RepositoryExistsResponse {
54 bool exists = 1;
55 kenshell.common.v1.ReadMeta meta = 2;
56}
57
58message ListRepositoriesRequest {
59 string org_name = 1;
60 string query = 2;
61 kenshell.common.v1.RepositoryState state = 3;
62 kenshell.common.v1.PageRequest page = 4;
63}
64
65message ListRepositoriesResponse {
66 repeated RepositorySummary repositories = 1;
67 kenshell.common.v1.PageResponse page = 2;
68}
69
70message GetRepositorySizeRequest {
71 kenshell.common.v1.RepositoryRef repository = 1;
72}
73
74message GetRepositorySizeResponse {
75 uint64 size_bytes = 1;
76 kenshell.common.v1.ReadMeta meta = 2;
77}
Added lib/kenshell/proto/kenshell/v1/search.proto lib/kenshell/proto/kenshell/v1/search.proto +74 −0 View lib/kenshell/proto/kenshell/v1/search.proto
@@ -0,0 +1,74 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6
7// 仓库内搜索,供 Next.js 搜索页和文件查找使用。
8service SearchService {
9 rpc SearchFilesByName(SearchFilesByNameRequest)
10 returns (SearchFilesByNameResponse);
11 rpc SearchFilesByContent(SearchFilesByContentRequest)
12 returns (SearchFilesByContentResponse);
13 rpc SearchCommits(SearchCommitsRequest) returns (SearchCommitsResponse);
14}
15
16message FileSearchHit {
17 string path = 1;
18 string oid = 2;
19 uint64 size_bytes = 3;
20 bool is_lfs = 4;
21 string lfs_oid = 5;
22 repeated string fragments = 6;
23}
24
25message CommitSearchHit {
26 string commit_id = 1;
27 string title = 2;
28 string message = 3;
29 string author_name = 4;
30 string author_email = 5;
31}
32
33message SearchFilesByNameRequest {
34 kenshell.common.v1.RepositoryRef repository = 1;
35 string revision = 2;
36 string query = 3;
37 bool case_sensitive = 4;
38 kenshell.common.v1.PageRequest page = 5;
39}
40
41message SearchFilesByNameResponse {
42 repeated FileSearchHit hits = 1;
43 kenshell.common.v1.PageResponse page = 2;
44 kenshell.common.v1.ReadMeta meta = 3;
45}
46
47message SearchFilesByContentRequest {
48 kenshell.common.v1.RepositoryRef repository = 1;
49 string revision = 2;
50 string query = 3;
51 bool case_sensitive = 4;
52 bool regex = 5;
53 kenshell.common.v1.PageRequest page = 6;
54}
55
56message SearchFilesByContentResponse {
57 repeated FileSearchHit hits = 1;
58 kenshell.common.v1.PageResponse page = 2;
59 kenshell.common.v1.ReadMeta meta = 3;
60}
61
62message SearchCommitsRequest {
63 kenshell.common.v1.RepositoryRef repository = 1;
64 string revision = 2;
65 string query = 3;
66 bool case_sensitive = 4;
67 kenshell.common.v1.PageRequest page = 5;
68}
69
70message SearchCommitsResponse {
71 repeated CommitSearchHit hits = 1;
72 kenshell.common.v1.PageResponse page = 2;
73 kenshell.common.v1.ReadMeta meta = 3;
74}
Added lib/kenshell/proto/kenshell/v1/tree.proto lib/kenshell/proto/kenshell/v1/tree.proto +147 −0 View lib/kenshell/proto/kenshell/v1/tree.proto
@@ -0,0 +1,147 @@
1syntax = "proto3";
2
3package kenshell.v1;
4
5import "kenshell/common/v1/types.proto";
6import "kenshell/v1/commit.proto";
7
8// Next.js 目录树与文件内容展示。
9service TreeService {
10 rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
11 rpc GetFile(GetFileRequest) returns (GetFileResponse);
12 rpc ListFiles(ListFilesRequest) returns (ListFilesResponse);
13 rpc ResolveTreePath(ResolveTreePathRequest) returns (ResolveTreePathResponse);
14 rpc ListTreeLastUpdated(ListTreeLastUpdatedRequest)
15 returns (ListTreeLastUpdatedResponse);
16}
17
18enum TreeEntryType {
19 TREE_ENTRY_TYPE_UNSPECIFIED = 0;
20 TREE_ENTRY_TYPE_FILE = 1;
21 TREE_ENTRY_TYPE_DIRECTORY = 2;
22 TREE_ENTRY_TYPE_SYMLINK = 3;
23 TREE_ENTRY_TYPE_SUBMODULE = 4;
24}
25
26enum FileEncoding {
27 FILE_ENCODING_UNSPECIFIED = 0;
28 FILE_ENCODING_UTF8 = 1;
29 FILE_ENCODING_BINARY = 2;
30}
31
32message TreeEntryView {
33 string name = 1;
34 string path = 2;
35 TreeEntryType type = 3;
36 string mode = 4;
37 string oid = 5;
38 uint64 size_bytes = 6;
39 bool is_lfs = 7;
40 string lfs_oid = 8;
41 CommitView latest_commit = 9;
42 // name_bytes/path_bytes 是 Git 路径的原始字节,path/name 仅作展示。
43 bytes name_bytes = 10;
44 bytes path_bytes = 11;
45}
46
47message FileView {
48 string path = 1;
49 string oid = 2;
50 uint64 size_bytes = 3;
51 string media_type = 4;
52 FileEncoding encoding = 5;
53 bytes content = 6;
54 bool truncated = 7;
55 bool is_lfs = 8;
56 string lfs_oid = 9;
57 bytes path_bytes = 10;
58}
59
60message GetTreeRequest {
61 kenshell.common.v1.RepositoryRef repository = 1;
62 string revision = 2;
63 string path = 3;
64 kenshell.common.v1.PageRequest page = 4;
65 bytes path_bytes = 5;
66}
67
68message GetTreeResponse {
69 string revision = 1;
70 string path = 2;
71 repeated TreeEntryView entries = 3;
72 kenshell.common.v1.PageResponse page = 4;
73 kenshell.common.v1.ReadMeta meta = 5;
74 CommitView latest_commit = 6;
75 // revision 解析后的不可变 commit SHA。
76 string resolved_revision = 7;
77 bytes path_bytes = 8;
78}
79
80message GetFileRequest {
81 kenshell.common.v1.RepositoryRef repository = 1;
82 string revision = 2;
83 string path = 3;
84 // 0 表示由服务端选择安全展示上限。
85 uint64 max_bytes = 4;
86 bytes path_bytes = 5;
87}
88
89message GetFileResponse {
90 FileView file = 1;
91 kenshell.common.v1.ReadMeta meta = 2;
92 CommitView latest_commit = 3;
93 string resolved_revision = 4;
94}
95
96message ListFilesRequest {
97 kenshell.common.v1.RepositoryRef repository = 1;
98 string revision = 2;
99 string path = 3;
100 bool recursive = 4;
101 kenshell.common.v1.PageRequest page = 5;
102 bool include_directories = 6;
103 bytes path_bytes = 7;
104}
105
106message ListFilesResponse {
107 repeated TreeEntryView files = 1;
108 kenshell.common.v1.PageResponse page = 2;
109 kenshell.common.v1.ReadMeta meta = 3;
110 string resolved_revision = 4;
111}
112
113message ResolveTreePathRequest {
114 kenshell.common.v1.RepositoryRef repository = 1;
115 string revision = 2;
116 string path = 3;
117 bytes path_bytes = 4;
118}
119
120message ResolveTreePathResponse {
121 TreeEntryView entry = 1;
122 string symlink_target = 2;
123 TreeEntryView resolved_entry = 3;
124 kenshell.common.v1.ReadMeta meta = 4;
125 string resolved_revision = 5;
126}
127
128message TreePathLastUpdatedView {
129 string path = 1;
130 int32 object_type = 2;
131 int64 last_updated = 3;
132 string commit_id = 4;
133 bytes path_bytes = 5;
134}
135
136message ListTreeLastUpdatedRequest {
137 kenshell.common.v1.RepositoryRef repository = 1;
138 string revision = 2;
139 string path = 3;
140 bytes path_bytes = 4;
141}
142
143message ListTreeLastUpdatedResponse {
144 repeated TreePathLastUpdatedView entries = 1;
145 kenshell.common.v1.ReadMeta meta = 2;
146 string resolved_revision = 3;
147}
Added lib/kenshell/src/client.rs lib/kenshell/src/client.rs +208 −0 View lib/kenshell/src/client.rs
@@ -0,0 +1,208 @@
1use std::time::Duration;
2
3use tonic::metadata::{Ascii, MetadataValue};
4use tonic::service::Interceptor;
5use tonic::service::interceptor::InterceptedService;
6use tonic::transport::{Channel, Endpoint};
7use tonic::{Request, Status};
8use uuid::Uuid;
9
10use crate::error::{KenShellError, KenShellResult};
11use crate::proto;
12
13/// 64 MiB:KenShell 的 blob/archive 流与 pack 分片都按这个量级返回。
14const DEFAULT_MESSAGE_SIZE: usize = 64 * 1024 * 1024;
15
16#[derive(Clone, Default)]
17pub struct RpcInterceptor {
18 key: Option<MetadataValue<Ascii>>,
19}
20
21impl RpcInterceptor {
22 pub fn new(rpc_key: &str) -> KenShellResult<Self> {
23 let key =
24 if rpc_key.is_empty() {
25 None
26 } else {
27 Some(MetadataValue::try_from(rpc_key).map_err(|_| {
28 KenShellError::Config("rpc_key is not valid header value".into())
29 })?)
30 };
31 Ok(Self { key })
32 }
33}
34
35impl Interceptor for RpcInterceptor {
36 fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
37 if let Some(key) = &self.key {
38 request
39 .metadata_mut()
40 .insert(crate::RPC_KEY_HEADER, key.clone());
41 }
42 if let Ok(value) = MetadataValue::try_from(Uuid::now_v7().to_string()) {
43 request
44 .metadata_mut()
45 .insert(crate::REQUEST_ID_HEADER, value);
46 }
47 Ok(request)
48 }
49}
50
51pub type Intercepted = InterceptedService<Channel, RpcInterceptor>;
52
53#[derive(Clone)]
54pub struct KenShellClient {
55 channel: Channel,
56 interceptor: RpcInterceptor,
57 request_timeout: Duration,
58 connect_timeout: Duration,
59}
60
61macro_rules! accessor {
62 ($name:ident, $client:ty) => {
63 pub fn $name(&self) -> $client {
64 <$client>::new(self.intercepted())
65 .max_decoding_message_size(DEFAULT_MESSAGE_SIZE)
66 .max_encoding_message_size(DEFAULT_MESSAGE_SIZE)
67 }
68 };
69}
70
71impl KenShellClient {
72 pub fn connect(endpoint: &str, request_timeout: Duration) -> KenShellResult<Self> {
73 Self::connect_with_key(endpoint, "", request_timeout)
74 }
75
76 pub fn connect_with_key(
77 endpoint: &str,
78 rpc_key: &str,
79 request_timeout: Duration,
80 ) -> KenShellResult<Self> {
81 if endpoint.is_empty() {
82 return Err(KenShellError::Config("kenshell endpoint is empty".into()));
83 }
84 let connect_timeout = Duration::from_secs(3);
85 let channel = Endpoint::from_shared(endpoint.to_string())
86 .map_err(|error| KenShellError::Config(error.to_string()))?
87 .connect_timeout(connect_timeout)
88 .timeout(request_timeout)
89 .connect_lazy();
90 Ok(Self {
91 channel,
92 interceptor: RpcInterceptor::new(rpc_key)?,
93 request_timeout,
94 connect_timeout,
95 })
96 }
97
98 pub fn channel(&self) -> Channel {
99 self.channel.clone()
100 }
101
102 pub fn connect_timeout(&self) -> Duration {
103 self.connect_timeout
104 }
105
106 pub fn request_timeout(&self) -> Duration {
107 self.request_timeout
108 }
109
110 pub fn set_timeout<T>(&self, request: &mut Request<T>) {
111 request.set_timeout(self.request_timeout);
112 }
113
114 fn intercepted(&self) -> Intercepted {
115 InterceptedService::new(self.channel.clone(), self.interceptor.clone())
116 }
117
118 accessor!(
119 repository,
120 proto::v1::repository_service_client::RepositoryServiceClient<Intercepted>
121 );
122 accessor!(
123 blob,
124 proto::v1::blob_service_client::BlobServiceClient<Intercepted>
125 );
126 accessor!(
127 tree,
128 proto::v1::tree_service_client::TreeServiceClient<Intercepted>
129 );
130 accessor!(
131 commit,
132 proto::v1::commit_service_client::CommitServiceClient<Intercepted>
133 );
134 accessor!(
135 diff,
136 proto::v1::diff_service_client::DiffServiceClient<Intercepted>
137 );
138 accessor!(
139 refs,
140 proto::v1::ref_service_client::RefServiceClient<Intercepted>
141 );
142 accessor!(
143 command,
144 proto::v1::command_service_client::CommandServiceClient<Intercepted>
145 );
146 accessor!(
147 remote,
148 proto::v1::remote_service_client::RemoteServiceClient<Intercepted>
149 );
150 accessor!(
151 job,
152 proto::v1::job_service_client::JobServiceClient<Intercepted>
153 );
154 accessor!(
155 lfs,
156 proto::v1::lfs_service_client::LfsServiceClient<Intercepted>
157 );
158 accessor!(
159 archive,
160 proto::v1::archive_service_client::ArchiveServiceClient<Intercepted>
161 );
162 accessor!(
163 analysis,
164 proto::v1::analysis_service_client::AnalysisServiceClient<Intercepted>
165 );
166 accessor!(
167 search,
168 proto::v1::search_service_client::SearchServiceClient<Intercepted>
169 );
170 accessor!(
171 conflict,
172 proto::v1::conflict_service_client::ConflictServiceClient<Intercepted>
173 );
174 accessor!(
175 comment,
176 proto::v1::code_comment_service_client::CodeCommentServiceClient<Intercepted>
177 );
178 accessor!(
179 repository_admin,
180 proto::internal::v1::repository_admin_service_client::RepositoryAdminServiceClient<
181 Intercepted,
182 >
183 );
184 accessor!(
185 node_admin,
186 proto::internal::v1::node_admin_service_client::NodeAdminServiceClient<Intercepted>
187 );
188 accessor!(
189 volume_admin,
190 proto::internal::v1::volume_admin_service_client::VolumeAdminServiceClient<Intercepted>
191 );
192 accessor!(
193 lfs_admin,
194 proto::internal::v1::lfs_admin_service_client::LfsAdminServiceClient<Intercepted>
195 );
196 accessor!(
197 push_lock_admin,
198 proto::internal::v1::push_lock_admin_service_client::PushLockAdminServiceClient<
199 Intercepted,
200 >
201 );
202 accessor!(
203 maintenance_admin,
204 proto::internal::v1::maintenance_admin_service_client::MaintenanceAdminServiceClient<
205 Intercepted,
206 >
207 );
208}
Added lib/kenshell/src/error.rs lib/kenshell/src/error.rs +80 −0 View lib/kenshell/src/error.rs
@@ -0,0 +1,80 @@
1use tonic::{Code, Status};
2
3#[derive(Debug, thiserror::Error)]
4pub enum KenShellError {
5 #[error("config: {0}")]
6 Config(String),
7 #[error("transport: {0}")]
8 Transport(#[from] tonic::transport::Error),
9 #[error("rpc {code:?}: {message}")]
10 Rpc { code: Code, message: String },
11 #[error("timeout")]
12 Timeout,
13}
14
15impl KenShellError {
16 pub fn from_status(status: Status) -> Self {
17 match status.code() {
18 Code::DeadlineExceeded => Self::Timeout,
19 code => Self::Rpc {
20 code,
21 message: status.message().to_string(),
22 },
23 }
24 }
25
26 pub fn code(&self) -> Option<Code> {
27 match self {
28 Self::Rpc { code, .. } => Some(*code),
29 Self::Timeout => Some(Code::DeadlineExceeded),
30 _ => None,
31 }
32 }
33
34 pub fn is_not_found(&self) -> bool {
35 self.code() == Some(Code::NotFound)
36 }
37
38 pub fn is_permission_denied(&self) -> bool {
39 self.code() == Some(Code::PermissionDenied)
40 }
41
42 pub fn is_invalid_argument(&self) -> bool {
43 self.code() == Some(Code::InvalidArgument)
44 }
45
46 pub fn is_unauthenticated(&self) -> bool {
47 self.code() == Some(Code::Unauthenticated)
48 }
49
50 pub fn is_conflict(&self) -> bool {
51 matches!(
52 self.code(),
53 Some(Code::Aborted) | Some(Code::AlreadyExists) | Some(Code::FailedPrecondition)
54 )
55 }
56
57 pub fn is_unavailable(&self) -> bool {
58 matches!(
59 self.code(),
60 Some(Code::Unavailable) | Some(Code::DeadlineExceeded)
61 )
62 }
63
64 pub fn message(&self) -> String {
65 match self {
66 Self::Config(message) => message.clone(),
67 Self::Transport(error) => error.to_string(),
68 Self::Rpc { message, .. } => message.clone(),
69 Self::Timeout => "deadline exceeded".to_string(),
70 }
71 }
72}
73
74impl From<Status> for KenShellError {
75 fn from(status: Status) -> Self {
76 Self::from_status(status)
77 }
78}
79
80pub type KenShellResult<T> = Result<T, KenShellError>;
Added lib/kenshell/src/lib.rs lib/kenshell/src/lib.rs +38 −0 View lib/kenshell/src/lib.rs
@@ -0,0 +1,38 @@
1pub mod client;
2pub mod error;
3
4// 生成代码里的跨包引用是相对路径(`super::super::common::v1`),因此模块层级必须
5// 与 proto package 完全一致:kenshell.{common,internal,}.v1 与顶层 gitken。
6pub mod kenshell {
7 pub mod common {
8 pub mod v1 {
9 tonic::include_proto!("kenshell.common.v1");
10 }
11 }
12
13 pub mod v1 {
14 tonic::include_proto!("kenshell.v1");
15 }
16
17 pub mod internal {
18 pub mod v1 {
19 tonic::include_proto!("kenshell.internal.v1");
20 }
21 }
22}
23
24#[allow(clippy::all)]
25pub mod gitken {
26 tonic::include_proto!("gitken");
27}
28
29pub mod proto {
30 pub use crate::gitken;
31 pub use crate::kenshell::{common, internal, v1};
32}
33
34pub use client::KenShellClient;
35pub use error::KenShellError;
36
37pub const REQUEST_ID_HEADER: &str = "x-request-id";
38pub const RPC_KEY_HEADER: &str = "x-rpc-key";
Added lib/kenshell/build.rs lib/kenshell/build.rs +72 −0 View lib/kenshell/build.rs
@@ -0,0 +1,72 @@
1use std::env;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
7 let proto_root = manifest_dir.join("proto");
8
9 println!("cargo:rerun-if-changed=build.rs");
10 if !proto_root.is_dir() {
11 println!("cargo:warning=proto 目录不存在,跳过协议生成");
12 return Ok(());
13 }
14 println!("cargo:rerun-if-changed={}", proto_root.display());
15
16 let sources = collect_protos(&proto_root)?;
17 if sources.is_empty() {
18 println!("cargo:warning=proto 下没有 .proto 文件,跳过协议生成");
19 return Ok(());
20 }
21
22 let cwd = env::current_dir()?;
23 let include_root = relative_path(&cwd, &proto_root);
24 let protos: Vec<PathBuf> = sources
25 .iter()
26 .map(|path| relative_path(&cwd, path))
27 .collect();
28
29 tonic_prost_build::configure()
30 .build_server(false)
31 .build_client(true)
32 .compile_protos(&protos, &[include_root])?;
33
34 Ok(())
35}
36
37fn collect_protos(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
38 let mut found = Vec::new();
39 let mut stack = vec![dir.to_path_buf()];
40 while let Some(dir) = stack.pop() {
41 for entry in fs::read_dir(&dir)? {
42 let path = entry?.path();
43 if path.is_dir() {
44 stack.push(path);
45 } else if path.extension().is_some_and(|ext| ext == "proto") {
46 found.push(path);
47 }
48 }
49 }
50 found.sort();
51 Ok(found)
52}
53
54fn relative_path(base: &Path, target: &Path) -> PathBuf {
55 let base: Vec<_> = base.components().collect();
56 let target: Vec<_> = target.components().collect();
57 let common = base
58 .iter()
59 .zip(target.iter())
60 .take_while(|(a, b)| a == b)
61 .count();
62 if common == 0 {
63 return target.iter().collect();
64 }
65
66 let mut rel = PathBuf::new();
67 for _ in common..base.len() {
68 rel.push("..");
69 }
70 rel.extend(target[common..].iter().map(|c| c.as_os_str()));
71 rel
72}
Added lib/kenshell/Cargo.toml lib/kenshell/Cargo.toml +17 −0 View lib/kenshell/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2name = "kenshell"
3version.workspace = true
4edition.workspace = true
5
6[dependencies]
7prost = { workspace = true }
8prost-types = { workspace = true }
9thiserror = { workspace = true }
10tonic = { workspace = true }
11tonic-prost = { workspace = true }
12tracing = { workspace = true }
13uuid = { workspace = true }
14tokio = { workspace = true, features = ["time"] }
15
16[build-dependencies]
17tonic-prost-build = { workspace = true }
No newline at end of file