Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Update `radicle-git` and `radicle-surf`
xphoniex committed 3 years ago
commit bdc0ab9881e7489bcb207b050fd28bb877e53ce5
parent 93ba7f593cd0443a7ff38dff0f12797806115a8d
13 files changed +95 -75
modified Cargo.lock
@@ -913,9 +913,9 @@ dependencies = [

[[package]]
name = "git-commit"
-
version = "0.2.0"
+
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3e5120c3ada84aa4bc54602ab0c02c223a5a569b5cf28ae59b2ac423d5b3f0e9"
+
checksum = "42480682cb6d05a34304eaea858ff0008fe3ac8167e31eda360931c0b51a35c1"
dependencies = [
 "git-trailers",
 "git2",
@@ -924,8 +924,9 @@ dependencies = [

[[package]]
name = "git-ref-format"
-
version = "0.1.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9c0eb2e3e4837359b843ab8cc5ad482bb84d4aa528153dc882dbbba44a2f4f47"
dependencies = [
 "git-ref-format-core",
 "git-ref-format-macro",
@@ -933,8 +934,9 @@ dependencies = [

[[package]]
name = "git-ref-format-core"
-
version = "0.1.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1cbf4ccf1d4cd0ac87a1461d6e59be34aec975bc2097850d7c3e164e3ff66436"
dependencies = [
 "serde",
 "thiserror",
@@ -942,8 +944,9 @@ dependencies = [

[[package]]
name = "git-ref-format-macro"
-
version = "0.1.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3058716dac23a95ef68bd44c855c8e8368ee36feefc875bd8bdcb42ae582d98e"
dependencies = [
 "git-ref-format-core",
 "proc-macro-error",
@@ -963,9 +966,9 @@ dependencies = [

[[package]]
name = "git2"
-
version = "0.15.0"
+
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
+
checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc"
dependencies = [
 "bitflags",
 "libc",
@@ -1934,8 +1937,9 @@ dependencies = [

[[package]]
name = "radicle-git-ext"
-
version = "0.2.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
version = "0.2.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c68edd5604b96b0ede289513ce523b9cef82a9c4ab07f9e059a24036fc77a2da"
dependencies = [
 "git-ref-format",
 "git2",
@@ -2031,12 +2035,14 @@ dependencies = [
[[package]]
name = "radicle-std-ext"
version = "0.1.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4"

[[package]]
name = "radicle-surf"
-
version = "0.8.0"
-
source = "git+https://github.com/radicle-dev/radicle-git?rev=48022590d0439e7fb196605a573a725bbbf0e4d2#48022590d0439e7fb196605a573a725bbbf0e4d2"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "10168a9ee60ee17511fe0062cc2fa3115654f0c1f2d1692d8109a3f254c855de"
dependencies = [
 "anyhow",
 "base64",
modified Cargo.toml
@@ -27,11 +27,3 @@ default-members = [
inherits = "release"
debug = true
incremental = false
-

-
[patch.crates-io.radicle-git-ext]
-
git = "https://github.com/radicle-dev/radicle-git"
-
rev = "48022590d0439e7fb196605a573a725bbbf0e4d2"
-

-
[patch.crates-io.git-ref-format]
-
git = "https://github.com/radicle-dev/radicle-git"
-
rev = "48022590d0439e7fb196605a573a725bbbf0e4d2"
modified radicle-cli/examples/rad-issue.md
@@ -11,7 +11,7 @@ The issue is now listed under our project.

```
$ rad issue list
-
5bce692884916d8d5f0c05824ce9ff44c04a82a8 "flux capacitor underpowered"
+
17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 "flux capacitor underpowered"
```

Great! Now we've documented the issue for ourselves and others.
@@ -22,20 +22,20 @@ others to work on. This is to ensure work is not duplicated.
Let's assign ourselves to this one.

```
-
$ rad assign 5bce692884916d8d5f0c05824ce9ff44c04a82a8 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
$ rad assign 17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```

It will now show in the list of issues assigned to us.

```
$ rad issue list --assigned
-
5bce692884916d8d5f0c05824ce9ff44c04a82a8 "flux capacitor underpowered" z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 "flux capacitor underpowered" z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```

Note: this can always be undone with the `unassign` subcommand.

```
-
$ rad unassign 5bce692884916d8d5f0c05824ce9ff44c04a82a8 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
$ rad unassign 17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```

Great, now we have communicated to the world about our car's defect.
@@ -44,8 +44,8 @@ But wait! We've found an important detail about the car's power requirements.
It will help whoever works on a fix.

```
-
$ rad comment 5bce692884916d8d5f0c05824ce9ff44c04a82a8 --message 'The flux capacitor needs 1.21 Gigawatts'
+
$ rad comment 17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 --message 'The flux capacitor needs 1.21 Gigawatts'
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/6
-
$ rad comment 5bce692884916d8d5f0c05824ce9ff44c04a82a8 --reply-to z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/6 --message 'More power!'
+
$ rad comment 17e52fd6b2cd2e0f9317e18371dc978a691ed1e3 --reply-to z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/6 --message 'More power!'
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/7
```
modified radicle-cli/examples/rad-patch.md
@@ -45,7 +45,7 @@ No description provided.
╰───────────────────────────────────


-
ok Patch 15141cf1497627e2db54362972dd9533f62d1dcb created 🌱
+
ok Patch d4ef85f57a849bd845915d7a66a2192cd23811f6 created 🌱
```

It will now be listed as one of the project's open patches.
@@ -55,16 +55,16 @@ $ rad patch

- YOU PROPOSED -

-
define power requirements 15141cf1497 R0 3e674d1 (flux-capacitor-power) ahead 1, behind 0
+
define power requirements d4ef85f57a8 R0 3e674d1 (flux-capacitor-power) ahead 1, behind 0
└─ * opened by z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [..]

- OTHERS PROPOSED -

Nothing to show.

-
$ rad patch show 15141cf1497627e2db54362972dd9533f62d1dcb
+
$ rad patch show d4ef85f57a849bd845915d7a66a2192cd23811f6

-
patch 15141cf1497627e2db54362972dd9533f62d1dcb
+
patch d4ef85f57a849bd845915d7a66a2192cd23811f6

╭─ define power requirements ───────

@@ -93,26 +93,26 @@ $ git commit --message "Add README, just for the fun"
[flux-capacitor-power 27857ec] Add README, just for the fun
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md
-
$ rad patch update --message "Add README, just for the fun" --no-confirm 15141cf1497627e2db54362972dd9533f62d1dcb
+
$ rad patch update --message "Add README, just for the fun" --no-confirm d4ef85f57a849bd845915d7a66a2192cd23811f6

🌱 Updating patch for heartwood

ok Pushing HEAD to storage...
ok Analyzing remotes...

-
15141cf1497 R0 (3e674d1) -> R1 (27857ec)
+
d4ef85f57a8 R0 (3e674d1) -> R1 (27857ec)
1 commit(s) ahead, 0 commit(s) behind


-
ok Patch 15141cf1497627e2db54362972dd9533f62d1dcb updated 🌱
+
ok Patch d4ef85f57a849bd845915d7a66a2192cd23811f6 updated 🌱

```

And lets leave a quick comment for our team:

```
-
$ rad comment 15141cf1497627e2db54362972dd9533f62d1dcb --message 'I cannot wait to get back to the 90s!'
+
$ rad comment d4ef85f57a849bd845915d7a66a2192cd23811f6 --message 'I cannot wait to get back to the 90s!'
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/5
-
$ rad comment 15141cf1497627e2db54362972dd9533f62d1dcb --message 'I cannot wait to get back to the 90s!' --reply-to z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/5
+
$ rad comment d4ef85f57a849bd845915d7a66a2192cd23811f6 --message 'I cannot wait to get back to the 90s!' --reply-to z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/5
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/6
```
modified radicle-cob/Cargo.toml
@@ -14,8 +14,8 @@ keywords = ["radicle", "collaborative objects", "cob", "cobs"]

[dependencies]
fastrand = { version = "1.8.0" }
-
git-commit = { version = "0.2" }
-
git-ref-format = { version = "0.1" }
+
git-commit = { version = "0.3" }
+
git-ref-format = { version = "0.2" }
git-trailers = { version = "0.1" }
log = { version = "0.4.17" }
nonempty = { version = "0.8.1", features = ["serialize"] }
@@ -24,7 +24,7 @@ serde_json = { version = "1.0" }
thiserror = { version = "1.0" }

[dependencies.git2]
-
version = "0.15.0"
+
version = "0.16.1"
default-features = false
features = ["vendored-libgit2"]

@@ -44,7 +44,7 @@ features = ["derive"]
[dev-dependencies]
ed25519-compact = { version = "2.0.2", features = ["pem"] }
fastrand = { version = "1.8.0", default-features = false }
-
git-ref-format = { version = "0.1", features = ["macro"] }
+
git-ref-format = { version = "0.2", features = ["macro"] }
tempfile = { version = "3" }
qcheck = { version = "1", default-features = false }
qcheck-macros = { version = "1", default-features = false }
modified radicle-crypto/Cargo.toml
@@ -29,7 +29,7 @@ default-features = false
optional = true

[dependencies.git-ref-format]
-
version = "0.1"
+
version = "0.2"
optional = true

[dependencies.ssh-key]
modified radicle-httpd/Cargo.toml
@@ -24,6 +24,7 @@ fastrand = { version = "1.7.0" }
flate2 = { version = "1" }
hyper = { version = "0.14.17", default-features = false }
lexopt = { version = "0.2.1" }
+
radicle-surf = { version = "0.9.0", default-features = false, features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }
thiserror = { version = "1" }
@@ -38,11 +39,6 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["s
path = "../radicle"
version = "0.2.0"

-
[dependencies.radicle-surf]
-
git = "https://github.com/radicle-dev/radicle-git"
-
features = ["serde"]
-
rev = "48022590d0439e7fb196605a573a725bbbf0e4d2"
-

[dev-dependencies]
hyper = { version = "0.14.17", default-features = false, features = ["client"] }
radicle-cli = { path = "../radicle-cli" }
modified radicle-httpd/src/api/json.rs
@@ -1,6 +1,7 @@
//! Utilities for building JSON responses of our API.

use std::path::Path;
+
use std::str;

use serde::Serialize;
use serde_json::{json, Value};
@@ -46,14 +47,22 @@ pub(crate) fn session(session_id: String, session: &Session) -> Value {
}

/// Returns JSON for a blob with a given `path`.
-
pub(crate) fn blob(blob: &Blob, path: &str) -> Value {
-
    json!({
+
pub(crate) fn blob<T: AsRef<[u8]>>(blob: &Blob<T>, path: &str) -> Value {
+
    let mut response = json!({
        "binary": blob.is_binary(),
-
        "content": blob.content(),
        "name": name_in_path(path),
        "path": path,
        "lastCommit": commit(blob.commit())
-
    })
+
    });
+

+
    if !blob.is_binary() {
+
        match str::from_utf8(blob.content()) {
+
            Ok(content) => response["content"] = content.into(),
+
            Err(err) => return json!({ "error": err.to_string() }),
+
        }
+
    }
+

+
    response
}

/// Returns JSON for a tree with a given `path` and `stats`.
@@ -85,9 +94,9 @@ pub(crate) fn issue(id: IssueId, issue: Issue) -> Value {
    json!({
        "id": id.to_string(),
        "author": issue.author(),
-
        "assignees": issue.assigned().collect::<Vec<_>>(),
        "title": issue.title(),
        "state": issue.state(),
+
        "assignees": issue.assigned().collect::<Vec<_>>(),
        "discussion": issue.comments().collect::<Comments>(),
        "tags": issue.tags().collect::<Vec<_>>(),
    })
modified radicle-httpd/src/api/test.rs
@@ -22,6 +22,8 @@ use crate::api::{auth, Context};

pub const HEAD: &str = "1e978d19f251cd9821d9d9a76d1bd436bf0690d5";
pub const HEAD_1: &str = "f604ce9fd5b7cc77b7609beda45ea8760bee78f7";
+
pub const PATCH_ID: &str = "4250f0117659ee4de9af99e699a63395cd6ffa1c";
+
pub const ISSUE_ID: &str = "d8131af9738258fac139c4c96b71c02411f94892";

const PASSWORD: &str = "radicle";

modified radicle-httpd/src/api/v1/projects.rs
@@ -423,11 +423,14 @@ async fn issue_create_handler(
        .map_err(|_| Error::Auth("Unauthorized"))?;
    let repo = storage.repository(project)?;
    let mut issues = Issues::open(ctx.profile.public_key, &repo)?;
-
    issues
+
    let issue = issues
        .create(issue.title, issue.description, &issue.tags, &signer)
        .map_err(Error::from)?;

-
    Ok::<_, Error>((StatusCode::CREATED, Json(json!({ "success": true }))))
+
    Ok::<_, Error>((
+
        StatusCode::CREATED,
+
        Json(json!({ "success": true, "id": issue.id().to_string() })),
+
    ))
}

/// Update an issue.
@@ -546,7 +549,9 @@ mod routes {
    use axum::http::StatusCode;
    use serde_json::json;

-
    use crate::api::test::{self, get, patch, post, HEAD, HEAD_1};
+
    use crate::api::test::{self, get, patch, post, HEAD, HEAD_1, ISSUE_ID, PATCH_ID};
+

+
    const CREATED_ISSUE_ID: &str = "768c6735912a34856552ae6a9ca77d728962bf31";

    #[tokio::test]
    async fn test_projects_root() {
@@ -635,7 +640,8 @@ mod routes {
                                }
                              ]
                            }
-
                          ]
+
                          ],
+
                          "eof": "noneMissing",
                        }
                      }
                    ],
@@ -685,7 +691,8 @@ mod routes {
                                }
                              ]
                            }
-
                          ]
+
                          ],
+
                          "eof": "noneMissing",
                        }
                      }
                    ],
@@ -759,7 +766,8 @@ mod routes {
                            }
                          ]
                        }
-
                      ]
+
                      ],
+
                      "eof": "noneMissing",
                    }
                  }
                ],
@@ -996,7 +1004,7 @@ mod routes {
            response.json().await,
            json!([
              {
-
                "id": "90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
                "id": ISSUE_ID,
                "author": {
                    "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
                },
@@ -1044,19 +1052,21 @@ mod routes {
        .await;

        assert_eq!(response.status(), StatusCode::CREATED);
-
        assert_eq!(response.json().await, json!({ "success": true }));
+
        assert_eq!(
+
            response.json().await,
+
            json!({ "success": true, "id": CREATED_ISSUE_ID })
+
        );

-
        let issue_id = "bd2bde30b52db0fc2dae35f4e97ff9fdcc93dead";
        let response = get(
            &app,
-
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{issue_id}"),
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{CREATED_ISSUE_ID}"),
        )
        .await;

        assert_eq!(
            response.json().await,
            json!({
-
              "id": issue_id,
+
              "id": CREATED_ISSUE_ID,
              "author": {
                  "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
              },
@@ -1098,7 +1108,7 @@ mod routes {
        .unwrap();
        let response = patch(
            &app,
-
            "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{ISSUE_ID}"),
            Some(Body::from(body)),
            Some("u9MGAkkfkMOv0uDDB2WeUHBT7HbsO2Dy".to_string()),
        )
@@ -1109,14 +1119,14 @@ mod routes {

        let response = get(
            &app,
-
            "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{ISSUE_ID}"),
        )
        .await;

        assert_eq!(
            response.json().await,
            json!({
-
              "id": "90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
              "id": ISSUE_ID,
              "author": {
                  "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
              },
@@ -1168,7 +1178,7 @@ mod routes {
        .unwrap();
        let response = patch(
            &app,
-
            "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{ISSUE_ID}"),
            Some(Body::from(body)),
            Some("u9MGAkkfkMOv0uDDB2WeUHBT7HbsO2Dy".to_string()),
        )
@@ -1179,14 +1189,14 @@ mod routes {

        let response = get(
            &app,
-
            "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues/{ISSUE_ID}"),
        )
        .await;

        assert_eq!(
            response.json().await,
            json!({
-
              "id": "90c8f0bab59d9efe35e234acf3abce4168bba6b4",
+
              "id": ISSUE_ID,
              "author": {
                  "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
              },
@@ -1233,7 +1243,7 @@ mod routes {
            response.json().await,
            json!([
              {
-
                "id": "5de9f17ca5326258412ab02f9a5339b6482198ce",
+
                "id": PATCH_ID,
                "author": {
                    "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
                },
@@ -1255,7 +1265,7 @@ mod routes {

        let response = get(
            &app,
-
            "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/patches/5de9f17ca5326258412ab02f9a5339b6482198ce",
+
            format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/patches/{PATCH_ID}"),
        )
        .await;

@@ -1264,7 +1274,7 @@ mod routes {
            response.json().await,
            json!(
              {
-
                "id": "5de9f17ca5326258412ab02f9a5339b6482198ce",
+
                "id": PATCH_ID,
                "author": {
                    "id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
                },
modified radicle-node/Cargo.toml
@@ -18,7 +18,7 @@ colored = { version = "1.9.0" }
crossbeam-channel = { version = "0.5.6" }
cyphernet = { version = "0.1.0", features = ["tor", "dns", "ed25519", "p2p-ed25519"] }
fastrand = { version = "1.8.0" }
-
git-ref-format = { version = "0", features = ["serde", "macro"] }
+
git-ref-format = { version = "0.2", features = ["serde", "macro"] }
lexopt = { version = "0.2.1" }
libc = { version = "0.2.137" }
log = { version = "0.4.17", features = ["std"] }
modified radicle/Cargo.toml
@@ -31,7 +31,7 @@ tempfile = { version = "3.3.0" }
thiserror = { version = "1" }

[dependencies.git2]
-
version = "0.15.0"
+
version = "0.16.1"
default-features = false
features = ["vendored-libgit2"]

modified radicle/src/cob/issue.rs
@@ -260,6 +260,11 @@ pub struct IssueMut<'a, 'g> {
}

impl<'a, 'g> IssueMut<'a, 'g> {
+
    /// Get the issue id.
+
    pub fn id(&self) -> &ObjectId {
+
        &self.id
+
    }
+

    /// Get the internal logical clock.
    pub fn clock(&self) -> &clock::Lamport {
        &self.clock