aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorKatharina Fey <kookie@spacekookie.de>2020-10-31 18:57:39 +0100
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:10:08 +0100
commit9dacf748651ea7139c0e9f3dee9ae66d949bf73f (patch)
tree7101bc20d5a531218ccd1b4cb9e04c67995f6d0e /apps
parent4e09fe2509904ee64d2470ca8d41006d51e4ffd6 (diff)
Add 'apps/servers/octopus/' from commit '623954d19fdf0dca47db319e5c88ee561aa8d25c'
git-subtree-dir: apps/servers/octopus git-subtree-mainline: 4e09fe2509904ee64d2470ca8d41006d51e4ffd6 git-subtree-split: 623954d19fdf0dca47db319e5c88ee561aa8d25c
Diffstat (limited to 'apps')
-rw-r--r--apps/servers/octopus/.envrc1
-rw-r--r--apps/servers/octopus/.gitignore3
-rw-r--r--apps/servers/octopus/Cargo.lock2330
-rw-r--r--apps/servers/octopus/Cargo.toml28
-rw-r--r--apps/servers/octopus/LICENSE661
-rw-r--r--apps/servers/octopus/README13
-rw-r--r--apps/servers/octopus/TODO12
-rw-r--r--apps/servers/octopus/fake-readme.md26
-rw-r--r--apps/servers/octopus/shell.nix10
-rw-r--r--apps/servers/octopus/src/cli.rs79
-rw-r--r--apps/servers/octopus/src/config.rs17
-rw-r--r--apps/servers/octopus/src/git/log.rs117
-rw-r--r--apps/servers/octopus/src/git/mod.rs58
-rw-r--r--apps/servers/octopus/src/git/tree.rs176
-rw-r--r--apps/servers/octopus/src/main.rs29
-rw-r--r--apps/servers/octopus/src/pages/files.rs20
-rw-r--r--apps/servers/octopus/src/pages/mod.rs15
-rw-r--r--apps/servers/octopus/src/pages/overview.rs24
-rw-r--r--apps/servers/octopus/src/pages/p404.rs13
-rw-r--r--apps/servers/octopus/src/pages/repo/about.rs26
-rw-r--r--apps/servers/octopus/src/pages/repo/details.rs38
-rw-r--r--apps/servers/octopus/src/pages/repo/mod.rs7
-rw-r--r--apps/servers/octopus/src/project/mod.rs8
-rw-r--r--apps/servers/octopus/src/repo.rs43
-rw-r--r--apps/servers/octopus/src/state/mod.rs14
-rw-r--r--apps/servers/octopus/src/templ_data/files.rs13
-rw-r--r--apps/servers/octopus/src/templ_data/mod.rs38
-rw-r--r--apps/servers/octopus/src/templ_data/overview.rs11
-rw-r--r--apps/servers/octopus/src/templ_data/repo.rs80
-rw-r--r--apps/servers/octopus/static/fakeavi.pngbin0 -> 29366 bytes
-rw-r--r--apps/servers/octopus/static/main.css81
-rw-r--r--apps/servers/octopus/static/octopus.pngbin0 -> 53947 bytes
-rw-r--r--apps/servers/octopus/static/octopus.svg58
-rw-r--r--apps/servers/octopus/static/rust.pngbin0 -> 56915 bytes
-rw-r--r--apps/servers/octopus/supergit/Cargo.toml10
-rw-r--r--apps/servers/octopus/supergit/src/bin/test.rs16
-rw-r--r--apps/servers/octopus/supergit/src/branch.rs22
-rw-r--r--apps/servers/octopus/supergit/src/commit.rs11
-rw-r--r--apps/servers/octopus/supergit/src/diff.rs5
-rw-r--r--apps/servers/octopus/supergit/src/files.rs7
-rw-r--r--apps/servers/octopus/supergit/src/lib.rs55
-rw-r--r--apps/servers/octopus/supergit/src/raw/#tree_walk.rs#1
l---------apps/servers/octopus/supergit/src/raw/.#tree_walk.rs1
-rw-r--r--apps/servers/octopus/supergit/src/raw/branch_walk.rs19
-rw-r--r--apps/servers/octopus/supergit/src/raw/mod.rs45
-rw-r--r--apps/servers/octopus/supergit/src/raw/tree_walk.rs1
-rw-r--r--apps/servers/octopus/templates/404.html11
-rw-r--r--apps/servers/octopus/templates/core.html40
-rw-r--r--apps/servers/octopus/templates/files.html17
-rw-r--r--apps/servers/octopus/templates/index.html7
-rw-r--r--apps/servers/octopus/templates/repo/about.html6
-rw-r--r--apps/servers/octopus/templates/repo/base.html37
-rw-r--r--apps/servers/octopus/templates/repo/details.html49
53 files changed, 4409 insertions, 0 deletions
diff --git a/apps/servers/octopus/.envrc b/apps/servers/octopus/.envrc
new file mode 100644
index 000000000000..051d09d292a8
--- /dev/null
+++ b/apps/servers/octopus/.envrc
@@ -0,0 +1 @@
+eval "$(lorri direnv)"
diff --git a/apps/servers/octopus/.gitignore b/apps/servers/octopus/.gitignore
new file mode 100644
index 000000000000..91ea9099ee28
--- /dev/null
+++ b/apps/servers/octopus/.gitignore
@@ -0,0 +1,3 @@
+/target
+**/*.rs.bk
+testrepo \ No newline at end of file
diff --git a/apps/servers/octopus/Cargo.lock b/apps/servers/octopus/Cargo.lock
new file mode 100644
index 000000000000..42f0f4d1df04
--- /dev/null
+++ b/apps/servers/octopus/Cargo.lock
@@ -0,0 +1,2330 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "actix-codec"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09e55f0a5c2ca15795035d90c46bd0e73a5123b72f68f12596d6ba5282051380"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "tokio",
+ "tokio-util 0.2.0",
+]
+
+[[package]]
+name = "actix-codec"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project 0.4.27",
+ "tokio",
+ "tokio-util 0.3.1",
+]
+
+[[package]]
+name = "actix-connect"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-rt",
+ "actix-service",
+ "actix-utils 1.0.6",
+ "derive_more",
+ "either",
+ "futures",
+ "http",
+ "log",
+ "trust-dns-proto",
+ "trust-dns-resolver",
+]
+
+[[package]]
+name = "actix-files"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "193b22cb1f7b4ff12a4eb2415d6d19e47e44ea93e05930b30d05375ea29d3529"
+dependencies = [
+ "actix-http",
+ "actix-service",
+ "actix-web",
+ "bitflags",
+ "bytes",
+ "derive_more",
+ "futures-core",
+ "futures-util",
+ "log",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
+ "v_htmlescape",
+]
+
+[[package]]
+name = "actix-http"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-connect",
+ "actix-rt",
+ "actix-service",
+ "actix-threadpool",
+ "actix-utils 1.0.6",
+ "base64",
+ "bitflags",
+ "brotli2",
+ "bytes",
+ "chrono",
+ "copyless",
+ "derive_more",
+ "either",
+ "encoding_rs",
+ "failure",
+ "flate2",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "fxhash",
+ "h2",
+ "http",
+ "httparse",
+ "indexmap",
+ "language-tags",
+ "lazy_static",
+ "log",
+ "mime",
+ "percent-encoding",
+ "pin-project 0.4.27",
+ "rand",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sha1",
+ "slab",
+ "time",
+]
+
+[[package]]
+name = "actix-macros"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a60f9ba7c4e6df97f3aacb14bb5c0cd7d98a49dcbaed0d7f292912ad9a6a3ed2"
+dependencies = [
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "actix-router"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd1f7dbda1645bf7da33554db60891755f6c01c1b2169e2f4c492098d30c235"
+dependencies = [
+ "bytestring",
+ "http",
+ "log",
+ "regex",
+ "serde",
+]
+
+[[package]]
+name = "actix-rt"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227"
+dependencies = [
+ "actix-macros",
+ "actix-threadpool",
+ "copyless",
+ "futures-channel",
+ "futures-util",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "actix-server"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e"
+dependencies = [
+ "actix-codec 0.3.0",
+ "actix-rt",
+ "actix-service",
+ "actix-utils 2.0.0",
+ "futures-channel",
+ "futures-util",
+ "log",
+ "mio",
+ "mio-uds",
+ "num_cpus",
+ "slab",
+ "socket2",
+]
+
+[[package]]
+name = "actix-service"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb"
+dependencies = [
+ "futures-util",
+ "pin-project 0.4.27",
+]
+
+[[package]]
+name = "actix-testing"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c"
+dependencies = [
+ "actix-macros",
+ "actix-rt",
+ "actix-server",
+ "actix-service",
+ "log",
+ "socket2",
+]
+
+[[package]]
+name = "actix-threadpool"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30"
+dependencies = [
+ "derive_more",
+ "futures-channel",
+ "lazy_static",
+ "log",
+ "num_cpus",
+ "parking_lot",
+ "threadpool",
+]
+
+[[package]]
+name = "actix-tls"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e5b4faaf105e9a6d389c606c298dcdb033061b00d532af9df56ff3a54995a8"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-rt",
+ "actix-service",
+ "actix-utils 1.0.6",
+ "derive_more",
+ "either",
+ "futures",
+ "log",
+]
+
+[[package]]
+name = "actix-utils"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcf8f5631bf01adec2267808f00e228b761c60c0584cc9fa0b5364f41d147f4e"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-rt",
+ "actix-service",
+ "bitflags",
+ "bytes",
+ "either",
+ "futures",
+ "log",
+ "pin-project 0.4.27",
+ "slab",
+]
+
+[[package]]
+name = "actix-utils"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a"
+dependencies = [
+ "actix-codec 0.3.0",
+ "actix-rt",
+ "actix-service",
+ "bitflags",
+ "bytes",
+ "either",
+ "futures-channel",
+ "futures-sink",
+ "futures-util",
+ "log",
+ "pin-project 0.4.27",
+ "slab",
+]
+
+[[package]]
+name = "actix-web"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3158e822461040822f0dbf1735b9c2ce1f95f93b651d7a7aded00b1efbb1f635"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-http",
+ "actix-macros",
+ "actix-router",
+ "actix-rt",
+ "actix-server",
+ "actix-service",
+ "actix-testing",
+ "actix-threadpool",
+ "actix-tls",
+ "actix-utils 1.0.6",
+ "actix-web-codegen",
+ "awc",
+ "bytes",
+ "derive_more",
+ "encoding_rs",
+ "futures",
+ "fxhash",
+ "log",
+ "mime",
+ "net2",
+ "pin-project 0.4.27",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "actix-web-codegen"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a71bf475cbe07281d0b3696abb48212db118e7e23219f13596ce865235ff5766"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "addr2line"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "arc-swap"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
+
+[[package]]
+name = "askama"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dc2a4b6d7f812d2b13d251ae792caecebd635d6401761162d4b71d5ebe1a010"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+ "askama_shared",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23ee2fff0f22ad5d215cace1227cd036c28e81e26206763bb837b6d0e766c87d"
+dependencies = [
+ "askama_shared",
+ "nom",
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "syn 0.15.44",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0de942230b5beedaa9e1d64df5b76fa1c97002e4c7982897be899cccf40621d"
+
+[[package]]
+name = "askama_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6dfa6b6d254fd066a8bbed9a8f913123e3f701db89216ad4f0aff04ad87718c"
+dependencies = [
+ "askama_escape",
+ "humansize",
+ "num-traits",
+ "serde",
+ "serde_derive",
+ "toml",
+]
+
+[[package]]
+name = "async-channel"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "vec-arena",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "124ac8c265e407641c3362b8f4d39cdb4e243885b71eef087be27199790f5a3a"
+dependencies = [
+ "async-executor",
+ "async-io",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e"
+dependencies = [
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "libc",
+ "log",
+ "nb-connect",
+ "once_cell",
+ "parking",
+ "polling",
+ "vec-arena",
+ "waker-fn",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9fa76751505e8df1c7a77762f60486f60c71bbd9b8557f4da6ad47d083732ed"
+dependencies = [
+ "async-global-executor",
+ "async-io",
+ "async-mutex",
+ "blocking",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "num_cpus",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "async-task"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
+
+[[package]]
+name = "async-trait"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "awc"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5"
+dependencies = [
+ "actix-codec 0.2.0",
+ "actix-http",
+ "actix-rt",
+ "actix-service",
+ "base64",
+ "bytes",
+ "derive_more",
+ "futures-core",
+ "log",
+ "mime",
+ "percent-encoding",
+ "rand",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
+dependencies = [
+ "addr2line",
+ "cfg-if 1.0.0",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "blocking"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
+[[package]]
+name = "brotli-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "brotli2"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e"
+dependencies = [
+ "brotli-sys",
+ "libc",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
+
+[[package]]
+name = "byteorder"
+version = "1.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+
+[[package]]
+name = "bytes"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
+
+[[package]]
+name = "bytestring"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363"
+dependencies = [
+ "bytes",
+]
+
+[[package]]
+name = "cache-padded"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
+
+[[package]]
+name = "cc"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
+dependencies = [
+ "jobserver",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+dependencies = [
+ "libc",
+ "num-integer",
+ "num-traits",
+ "time",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "cloudabi"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+dependencies = [
+ "cache-padded",
+]
+
+[[package]]
+name = "copyless"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
+
+[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "lazy_static",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "dtoa"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
+dependencies = [
+ "cfg-if 0.1.10",
+]
+
+[[package]]
+name = "enum-as-inner"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
+dependencies = [
+ "heck",
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
+
+[[package]]
+name = "failure"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
+dependencies = [
+ "backtrace",
+ "failure_derive",
+]
+
+[[package]]
+name = "failure_derive"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+ "synstructure",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
+dependencies = [
+ "cfg-if 0.1.10",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "fuchsia-zircon"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+dependencies = [
+ "bitflags",
+ "fuchsia-zircon-sys",
+]
+
+[[package]]
+name = "fuchsia-zircon-sys"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+
+[[package]]
+name = "futures"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
+
+[[package]]
+name = "futures-lite"
+version = "1.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
+
+[[package]]
+name = "futures-task"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "futures-util"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project 1.0.1",
+ "pin-utils",
+ "proc-macro-hack",
+ "proc-macro-nested",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
+
+[[package]]
+name = "git2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77519ef7c5beee314d0804d4534f01e0f9e8d9acdee2b7a48627e590b27e0ec4"
+dependencies = [
+ "bitflags",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "openssl-probe",
+ "openssl-sys",
+ "url",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "h2"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util 0.3.1",
+ "tracing",
+ "tracing-futures",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hostname"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
+dependencies = [
+ "libc",
+ "match_cfg",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "http"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "httparse"
+version = "1.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
+
+[[package]]
+name = "humansize"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
+
+[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
+name = "idna"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
+dependencies = [
+ "matches",
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "iovec"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "ipconfig"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
+dependencies = [
+ "socket2",
+ "widestring",
+ "winapi 0.3.9",
+ "winreg",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+
+[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
+
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "language-tags"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
+
+[[package]]
+name = "libgit2-sys"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9ec6bca50549d34a392611dde775123086acbd994e3fff64954777ce2dc2e51"
+dependencies = [
+ "cc",
+ "libc",
+ "libssh2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "libssh2-sys"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca46220853ba1c512fc82826d0834d87b06bcd3c2a42241b7de72f3d2fe17056"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
+
+[[package]]
+name = "lock_api"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
+dependencies = [
+ "cfg-if 0.1.10",
+]
+
+[[package]]
+name = "lru-cache"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "markdown"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef3aab6a1d529b112695f72beec5ee80e729cb45af58663ec902c8fac764ecdd"
+dependencies = [
+ "lazy_static",
+ "pipeline",
+ "regex",
+]
+
+[[package]]
+name = "match_cfg"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
+
+[[package]]
+name = "matches"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+
+[[package]]
+name = "memchr"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+
+[[package]]
+name = "mime"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "mio"
+version = "0.6.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
+dependencies = [
+ "cfg-if 0.1.10",
+ "fuchsia-zircon",
+ "fuchsia-zircon-sys",
+ "iovec",
+ "kernel32-sys",
+ "libc",
+ "log",
+ "miow",
+ "net2",
+ "slab",
+ "winapi 0.2.8",
+]
+
+[[package]]
+name = "mio-uds"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
+dependencies = [
+ "iovec",
+ "libc",
+ "mio",
+]
+
+[[package]]
+name = "miow"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+dependencies = [
+ "kernel32-sys",
+ "net2",
+ "winapi 0.2.8",
+ "ws2_32-sys",
+]
+
+[[package]]
+name = "nb-connect"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "nom"
+version = "4.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
+dependencies = [
+ "memchr",
+ "version_check 0.1.5",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
+
+[[package]]
+name = "once_cell"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
+dependencies = [
+ "cfg-if 0.1.10",
+ "cloudabi",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+
+[[package]]
+name = "pin-project"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
+dependencies = [
+ "pin-project-internal 0.4.27",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841"
+dependencies = [
+ "pin-project-internal 1.0.1",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pipeline"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d15b6607fa632996eb8a17c9041cb6071cb75ac057abd45dece578723ea8c7c0"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
+[[package]]
+name = "polling"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "log",
+ "wepoll-sys",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
+
+[[package]]
+name = "proc-macro-nested"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
+
+[[package]]
+name = "proc-macro2"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
+dependencies = [
+ "unicode-xid 0.1.0",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+dependencies = [
+ "unicode-xid 0.2.1",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
+dependencies = [
+ "proc-macro2 0.4.30",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+dependencies = [
+ "proc-macro2 1.0.24",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom",
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
+[[package]]
+name = "regex"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+ "thread_local",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
+
+[[package]]
+name = "resolv-conf"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a"
+dependencies = [
+ "hostname",
+ "quick-error",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
+
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "serde",
+ "url",
+]
+
+[[package]]
+name = "sha1"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
+dependencies = [
+ "arc-swap",
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+
+[[package]]
+name = "smallvec"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
+
+[[package]]
+name = "socket2"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "redox_syscall",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "supergit"
+version = "0.1.0"
+dependencies = [
+ "async-std",
+ "git2",
+]
+
+[[package]]
+name = "syn"
+version = "0.15.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
+dependencies = [
+ "proc-macro2 0.4.30",
+ "quote 0.6.13",
+ "unicode-xid 0.1.0",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "unicode-xid 0.2.1",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+ "unicode-xid 0.2.1",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "tinyvec"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
+
+[[package]]
+name = "tokio"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "iovec",
+ "lazy_static",
+ "libc",
+ "memchr",
+ "mio",
+ "mio-uds",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "slab",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
+dependencies = [
+ "cfg-if 0.1.10",
+ "log",
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
+dependencies = [
+ "pin-project 0.4.27",
+ "tracing",
+]
+
+[[package]]
+name = "trust-dns-proto"
+version = "0.18.0-alpha.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f"
+dependencies = [
+ "async-trait",
+ "enum-as-inner",
+ "failure",
+ "futures",
+ "idna",
+ "lazy_static",
+ "log",
+ "rand",
+ "smallvec",
+ "socket2",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "trust-dns-resolver"
+version = "0.18.0-alpha.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f"
+dependencies = [
+ "cfg-if 0.1.10",
+ "failure",
+ "futures",
+ "ipconfig",
+ "lazy_static",
+ "log",
+ "lru-cache",
+ "resolv-conf",
+ "smallvec",
+ "tokio",
+ "trust-dns-proto",
+]
+
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check 0.9.2",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+dependencies = [
+ "matches",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+
+[[package]]
+name = "url"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
+dependencies = [
+ "idna",
+ "matches",
+ "percent-encoding",
+]
+
+[[package]]
+name = "v_escape"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "660b101c07b5d0863deb9e7fb3138777e858d6d2a79f9e6049a27d1cc77c6da6"
+dependencies = [
+ "v_escape_derive",
+]
+
+[[package]]
+name = "v_escape_derive"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae"
+dependencies = [
+ "nom",
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+]
+
+[[package]]
+name = "v_htmlescape"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41"
+dependencies = [
+ "cfg-if 0.1.10",
+ "v_escape",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+
+[[package]]
+name = "vec-arena"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
+
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
+dependencies = [
+ "cfg-if 0.1.10",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
+dependencies = [
+ "cfg-if 0.1.10",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
+dependencies = [
+ "quote 1.0.7",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.7",
+ "syn 1.0.48",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
+
+[[package]]
+name = "web-sys"
+version = "0.3.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webgit"
+version = "0.2.0"
+dependencies = [
+ "actix-files",
+ "actix-rt",
+ "actix-web",
+ "askama",
+ "env_logger",
+ "git2",
+ "markdown",
+]
+
+[[package]]
+name = "wepoll-sys"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "widestring"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "winreg"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
diff --git a/apps/servers/octopus/Cargo.toml b/apps/servers/octopus/Cargo.toml
new file mode 100644
index 000000000000..afc01fdcab4a
--- /dev/null
+++ b/apps/servers/octopus/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "webgit"
+description = "A lightweight web frontend for git repositories"
+version = "0.2.0"
+authors = ["Kaiden Fey <kookie@spacekookie.de>"]
+edition = "2018"
+
+[dependencies]
+actix-files = "0.2"
+actix-rt = "1.0.0"
+actix-web = "2.0.0"
+askama = "0.8"
+env_logger = "0.6"
+git2 = "0.11"
+markdown = { version = "0.3.0", optional = true }
+
+[build-dependencies]
+askama = "0.8"
+
+[features]
+default = ["markdown-readme"]
+markdown-readme = ["markdown"]
+
+[workspace]
+members = [
+ ".",
+ "supergit",
+] \ No newline at end of file
diff --git a/apps/servers/octopus/LICENSE b/apps/servers/octopus/LICENSE
new file mode 100644
index 000000000000..0ad25db4bd1d
--- /dev/null
+++ b/apps/servers/octopus/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
diff --git a/apps/servers/octopus/README b/apps/servers/octopus/README
new file mode 100644
index 000000000000..2834983a3f38
--- /dev/null
+++ b/apps/servers/octopus/README
@@ -0,0 +1,13 @@
+ .'.'
+ .'-'.
+ . ( o O)
+ \_ ` _, _
+-.___'.) ( ,-'
+ '-.O.'-..-.. octopus git mono-repo explorer
+ ./\/\/ | \_.-._
+ ;
+ ._/
+
+
+A visualiser for mono-repos, with per project pages, histories, and
+committer data. \ No newline at end of file
diff --git a/apps/servers/octopus/TODO b/apps/servers/octopus/TODO
new file mode 100644
index 000000000000..38e8a108932c
--- /dev/null
+++ b/apps/servers/octopus/TODO
@@ -0,0 +1,12 @@
+TODO
+====
+
+Things that need doing for the next milestone/ version. For now this
+will just be a list of basic features and data that octopus needs
+access to from a repo.
+
+- contributors/ number of contributors
+- commits on $branch for the log
+- Render a README, or markdown README. Markdown support should be optional
+- Build a lazy abstraction over the repo tree for the explorer
+-
diff --git a/apps/servers/octopus/fake-readme.md b/apps/servers/octopus/fake-readme.md
new file mode 100644
index 000000000000..d851cd661635
--- /dev/null
+++ b/apps/servers/octopus/fake-readme.md
@@ -0,0 +1,26 @@
+# spacekookie's project monorepo
+
+**Welcome to my personal code and project monorepo archive!**
+
+It contains most projects I work on in my own time, including the
+tools to host and manage this collection of projects. Projects that
+are deemed note-worthy enough have their own project pages. When
+browsing the file hierarchy, you will be notified about these project
+pages, but not immediately taken to them.
+
+
+## Structure
+
+The root of the project contains a list of directories, each for one
+project. The git history of this repository might be very non-linear,
+considering that it was created by merging multiple earlier
+repositories in via sub-trees.
+
+You can also get a per-project history, by cloning only a sub-tree of
+this repo, or by looking at the log page on a project.
+
+
+## Self hosted
+
+The software that is hosting this code archive is called [`octopus`](./)
+and included in this repository.
diff --git a/apps/servers/octopus/shell.nix b/apps/servers/octopus/shell.nix
new file mode 100644
index 000000000000..aefc4932482f
--- /dev/null
+++ b/apps/servers/octopus/shell.nix
@@ -0,0 +1,10 @@
+with import <nixpkgs> {};
+
+stdenv.mkDerivation {
+ name = "webgit";
+ buildInputs = with pkgs; [
+ rustracer rustup clangStdenv
+
+ cargo-watch openssl pkg-config
+ ];
+}
diff --git a/apps/servers/octopus/src/cli.rs b/apps/servers/octopus/src/cli.rs
new file mode 100644
index 000000000000..500a901954ed
--- /dev/null
+++ b/apps/servers/octopus/src/cli.rs
@@ -0,0 +1,79 @@
+use clap::{App, Arg};
+use colored::Colorize;
+use std::{
+ env,
+ fs::File,
+ path::{Path, PathBuf},
+};
+
+pub struct Paths {
+ pub config: File,
+ pub data: PathBuf,
+}
+
+/// Initialise the application by getting valid path options
+pub fn init() -> Paths {
+ let app = App::new("webgit")
+ .about("The friendly and simple git web frontend")
+ .version("0.0.0")
+ .arg(
+ Arg::with_name("CONFIG")
+ .short("c")
+ .long("config")
+ .takes_value(true)
+ .help(
+ "Provide the path to the system configuration. Alternatively \
+ set WEBGIT_CONFIG_PATH in your env",
+ ),
+ )
+ .arg(
+ Arg::with_name("DATA_DIR")
+ .short("d")
+ .long("data-dir")
+ .takes_value(true)
+ .help(
+ "Specify where webgit should save git repositories. Alternatively \
+ set WEBGIT_DATA_DIR in your env",
+ ),
+ );
+
+ let matches = app.get_matches();
+
+ Paths {
+ config: File::open(
+ env::var_os("WEBGIT_CONFIG_PATH")
+ .map(|os| match os.into_string() {
+ Ok(p) => p.to_owned(),
+ Err(_) => {
+ eprintln!("{}: Failed to parse provided config path!", "Error:".red());
+ std::process::exit(2);
+ }
+ })
+ .unwrap_or_else(|| match matches.value_of("CONFIG") {
+ Some(p) => p.to_owned(),
+ None => {
+ eprintln!("{}: No config provided!", "Error:".red());
+ std::process::exit(2);
+ }
+ }),
+ )
+ .expect(&format!("{}: Config file not found!", "Error:".red())),
+ data: Path::new(
+ &env::var_os("WEBGIT_DATA_DIR")
+ .map(|os| {
+ os.into_string().expect(&format!(
+ "{}: Failed to parse provided data-dir path!",
+ "Error".red()
+ ))
+ })
+ .unwrap_or_else(|| match matches.value_of("CONFIG") {
+ Some(p) => p.to_owned(),
+ None => {
+ eprintln!("{}: No data dir provided!", "Error:".red());
+ std::process::exit(2);
+ }
+ }),
+ )
+ .into(),
+ }
+}
diff --git a/apps/servers/octopus/src/config.rs b/apps/servers/octopus/src/config.rs
new file mode 100644
index 000000000000..99e8cffd94b9
--- /dev/null
+++ b/apps/servers/octopus/src/config.rs
@@ -0,0 +1,17 @@
+//! Configuration to run octopus
+
+pub struct Config {
+ app_path: String,
+ port: u16,
+ handle_ssl: bool,
+ cache_path: String,
+ repos_path: String,
+ repo_discovery: bool,
+ repos: Vec<RepoConfig>
+}
+
+pub struct RepoConfig {
+ name: String,
+ description: String,
+ category: String,
+}
diff --git a/apps/servers/octopus/src/git/log.rs b/apps/servers/octopus/src/git/log.rs
new file mode 100644
index 000000000000..c8f4aa37ccf2
--- /dev/null
+++ b/apps/servers/octopus/src/git/log.rs
@@ -0,0 +1,117 @@
+//! libgit2 log parsing
+
+use crate::git::{tree::FileNode, Repo};
+use git2::Oid;
+use std::collections::{BTreeMap, BTreeSet};
+
+/// A file-commit referenced graph thing
+///
+/// git is _weird_! It's essentially just a glorified key-value store
+/// and it shows. There's no utilities to figure out how thing are
+/// related, and all the actual graph things in git are sugar on top
+/// of this store.
+///
+/// In order to make sense of anything in a repo we need to quite
+/// heavily parse the log. This type here is the result of this
+/// parsing: you can ask it smart questions like "when did this file
+/// change" and it will tell you (sort of).
+#[derive(Debug, Default)]
+pub(crate) struct CommitGraph {
+ /// The correct order of commits in the log
+ order: Vec<String>,
+ /// List of all files, and the commits in which they were touched
+ file_refs: BTreeMap<String, Vec<String>>,
+ /// Map of commit IDs to metadata
+ commit_refs: BTreeMap<String, CommitNode>,
+}
+
+#[derive(Debug)]
+pub(crate) struct CommitNode {
+ id: String,
+ author: String,
+ message: String,
+ touches: BTreeSet<String>,
+ time: i64,
+}
+
+fn build_diff_log(repo: &Repo, log: Vec<(String, Vec<FileNode>)>) -> Vec<CommitNode> {
+ todo!()
+}
+
+/// Walk through all commits from a given ref and build a commit graph
+pub(crate) fn create_commit_log(rev: String, repo: &Repo) -> CommitGraph {
+ let mut walker = repo.get_inner().revwalk().unwrap();
+ walker.push(Oid::from_str(rev.as_str()).unwrap()).unwrap();
+ let mut commits = walker
+ .into_iter()
+ .map(|oid| {
+ let oid = oid.unwrap();
+ repo.get_inner().find_commit(oid).unwrap()
+ })
+ .collect::<Vec<_>>();
+ commits.reverse();
+
+ let mut initial: Vec<(_, _)> = commits
+ .into_iter()
+ .map(|commit| {
+ let id = format!("{}", commit.id());
+ (id.clone(), repo.get_tree(id.as_str()))
+ })
+ .collect();
+
+ // split off rest of the diffs and dissolve the len(1) vec
+ let log = initial.split_off(1);
+ let previous = initial.remove(0).1;
+
+ let mut order = vec![];
+ let (commit_refs, file_refs) = log.into_iter().fold(
+ (BTreeMap::new(), BTreeMap::new()),
+ |(mut cm, mut fm), (cid, current)| {
+ let commit_id = format!("{}", cid);
+
+ let d = repo
+ .get_inner()
+ .diff_tree_to_tree(Some(&previous), Some(&current), None)
+ .unwrap();
+
+ // Store the commit to preserve order
+ order.push(commit_id.clone());
+
+ // For each file, store this commit as one that touched it
+ let touches = d.deltas().fold(BTreeSet::new(), |mut set, delta| {
+ let file_id = format!("{}", delta.new_file().id());
+ fm.entry(file_id.clone())
+ .or_insert(vec![])
+ .push(commit_id.clone());
+ set.insert(file_id);
+ set
+ });
+
+ // From the commit, build a metadata object
+ let commit_u = repo
+ .get_inner()
+ .find_commit(Oid::from_str(cid.as_str()).unwrap())
+ .unwrap();
+ let author_u = commit_u.author();
+ let commit = CommitNode {
+ id: commit_id,
+ message: commit_u.message().unwrap().to_owned(),
+ author: format!("{} {}", author_u.name().unwrap(), author_u.email().unwrap()),
+ touches,
+ time: author_u.when().seconds(),
+ };
+
+ // Insert the metadata object
+ cm.insert(cid.clone(), commit);
+
+ // We pass both the modified maps into the next commit
+ (cm, fm)
+ },
+ );
+
+ CommitGraph {
+ order,
+ file_refs,
+ commit_refs,
+ }
+}
diff --git a/apps/servers/octopus/src/git/mod.rs b/apps/servers/octopus/src/git/mod.rs
new file mode 100644
index 000000000000..244e2f45e6c5
--- /dev/null
+++ b/apps/servers/octopus/src/git/mod.rs
@@ -0,0 +1,58 @@
+//! Wrappers for libgit2
+
+pub mod log;
+pub mod tree;
+
+use git2::{self, Repository};
+use log::CommitGraph;
+use tree::Tree;
+
+/// A top-level wrapper API for all libgit2 functions
+pub struct Repo {
+ inner: Repository,
+ commits: Option<CommitGraph>,
+ rev: Option<String>,
+}
+
+impl Repo {
+ pub(crate) fn new(path: &str) -> Self {
+ Self {
+ inner: Repository::open(path).expect(&format!("`{}` is not a valid git repo", path)),
+ commits: None,
+ rev: None,
+ }
+ }
+
+ pub(self) fn get_inner(&self) -> &Repository {
+ &self.inner
+ }
+
+ pub(self) fn get_tree<'r>(&'r self, rev: &str) -> git2::Tree<'r> {
+ self.inner
+ .revparse_single(rev)
+ .unwrap()
+ .peel_to_tree()
+ .unwrap()
+ }
+
+ pub(crate) fn clear_cache(&mut self) {
+ self.rev = None;
+ self.commits = None;
+ }
+
+ /// Load and cache commits for a specific rev
+ pub(crate) fn load_commits(&mut self, rev: String) {
+ self.rev = Some(rev.clone());
+ self.commits = Some(log::create_commit_log(rev, &self));
+ }
+
+ /// Load the tree of files for the current rev
+ ///
+ /// Will fail if no rev was previously cached
+ pub(crate) fn get_file_tree(&self) -> Tree {
+ tree::parse_tree(
+ self.get_tree(self.rev.as_ref().unwrap().as_str()),
+ self.get_inner(),
+ )
+ }
+}
diff --git a/apps/servers/octopus/src/git/tree.rs b/apps/servers/octopus/src/git/tree.rs
new file mode 100644
index 000000000000..5343a57c2463
--- /dev/null
+++ b/apps/servers/octopus/src/git/tree.rs
@@ -0,0 +1,176 @@
+//! Tree handling utilities
+//!
+//! The way that libgit2 handles trees is super low-level and overkill
+//! for what we need. In this module we knock it down a notch or two.
+//!
+//! This code takes a tree returned by
+//! `crate::git::repo::Repo::get_tree()`, and transforms it into a
+//! `TreeData` type that the template engine can render.
+
+use crate::templ_data::repo::{CommitData, FileData, TreeData};
+use git2::{self, ObjectType, TreeWalkMode};
+use std::collections::BTreeMap;
+
+/// A cache of a repository tree
+#[derive(Default, Debug, Clone)]
+pub(crate) struct Tree {
+ inner: BTreeMap<String, TreeNode>,
+}
+
+impl Tree {
+ /// Insert a node into a subtree with it's full path
+ fn insert_to_subtree(&mut self, mut path: Vec<String>, name: String, node: TreeNode) {
+ // If we are given a path, resolve it first
+ let curr = if path.len() > 0 {
+ let rest = path.split_off(1);
+ let mut curr = self.inner.get_mut(&path[0]).unwrap();
+
+ for dir in rest {
+ match curr {
+ TreeNode::Dir(ref mut d) => {
+ curr = d.children.inner.get_mut(&dir).unwrap();
+ }
+ _ => panic!("Not a tree!"),
+ }
+ }
+
+ match curr {
+ TreeNode::Dir(ref mut d) => &mut d.children,
+ TreeNode::File(_) => panic!("Not a tree!"),
+ }
+ } else {
+ // If no path was given, we assume the root is meant
+ self
+ };
+
+ curr.inner.insert(name, node);
+ }
+
+ /// Walk through the tree and only return filenode objects
+ pub(crate) fn flatten(&self) -> Vec<FileNode> {
+ self.inner.values().fold(vec![], |mut vec, node| {
+ match node {
+ TreeNode::File(f) => vec.push(f.clone()),
+ TreeNode::Dir(d) => vec.append(&mut d.children.flatten()),
+ }
+
+ vec
+ })
+ }
+
+ /// Get all the commits that touch a file
+ pub(crate) fn grab_path_history(&self, mut path: String) -> String {
+ let mut path: Vec<String> = path
+ .split("/")
+ .filter_map(|seg| match seg {
+ "" => None,
+ val => Some(val.into()),
+ })
+ .collect();
+
+ let leaf = if path.len() > 0 {
+ let rest = path.split_off(1);
+ let mut curr = self.inner.get(&path[0]).unwrap();
+
+ for dir in rest {
+ match curr {
+ TreeNode::Dir(d) => curr = d.children.inner.get(&dir).unwrap(),
+ TreeNode::File(_) => break, // we reached the leaf
+ }
+ }
+
+ curr
+ } else {
+ panic!("No valid path!");
+ };
+
+ match leaf {
+ TreeNode::File(f) => f.id.clone(),
+ _ => panic!("Not a leaf!"),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum TreeNode {
+ File(FileNode),
+ Dir(DirNode),
+}
+
+impl TreeNode {
+ fn name(&self) -> String {
+ match self {
+ Self::File(f) => f.name.clone(),
+ Self::Dir(d) => d.name.clone(),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FileNode {
+ pub id: String,
+ pub path: Vec<String>,
+ pub name: String,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct DirNode {
+ pub path: Vec<String>,
+ pub name: String,
+ pub children: Tree,
+}
+
+impl DirNode {
+ fn append(&mut self, node: TreeNode) {
+ self.children.inner.insert(node.name(), node);
+ }
+}
+
+/// Take a series of path-segments and render a tree at that location
+pub(crate) fn parse_tree(tree: git2::Tree, repo: &git2::Repository) -> Tree {
+ let mut root = Tree::default();
+
+ tree.walk(TreeWalkMode::PreOrder, |path, entry| {
+ let path: Vec<String> = path
+ .split("/")
+ .filter_map(|seg| match seg {
+ "" => None,
+ val => Some(val.into()),
+ })
+ .collect();
+ let name = entry.name().unwrap().to_string();
+
+ match entry.kind() {
+ // For every tree in the tree we create a new TreeNode with the path we know about
+ Some(ObjectType::Tree) => {
+ root.insert_to_subtree(
+ path.clone(),
+ name.clone(),
+ TreeNode::Dir(DirNode {
+ path,
+ name,
+ children: Tree::default(),
+ }),
+ );
+ }
+ // If we encounter a blob, this is a file that we can simply insert into the tree
+ Some(ObjectType::Blob) => {
+ root.insert_to_subtree(
+ path.clone(),
+ name.clone(),
+ TreeNode::File(FileNode {
+ id: format!("{}", entry.id()),
+ path,
+ name,
+ }),
+ );
+ }
+ _ => {}
+ }
+
+ 0
+ })
+ .unwrap();
+
+ root
+}
diff --git a/apps/servers/octopus/src/main.rs b/apps/servers/octopus/src/main.rs
new file mode 100644
index 000000000000..8ed6445dd4a8
--- /dev/null
+++ b/apps/servers/octopus/src/main.rs
@@ -0,0 +1,29 @@
+//mod git;
+mod pages;
+mod repo;
+mod templ_data;
+
+mod project;
+
+use actix_files as fs;
+use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
+use std::io;
+use std::path::PathBuf;
+
+#[actix_rt::main]
+async fn main() -> io::Result<()> {
+ std::env::set_var("RUST_LOG", "actix_server=info,octopus=debug");
+ env_logger::init();
+ let root = PathBuf::new();
+
+ HttpServer::new(move || {
+ App::new()
+ .service(fs::Files::new("/static", root.join("static")))
+ .service(web::resource("/").route(web::get().to(pages::overview)))
+ .service(web::resource("/tree").route(web::get().to(pages::files)))
+ .default_service(web::resource("").route(web::get().to(pages::p404)))
+ })
+ .bind("127.0.0.1:8080")?
+ .run()
+ .await
+}
diff --git a/apps/servers/octopus/src/pages/files.rs b/apps/servers/octopus/src/pages/files.rs
new file mode 100644
index 000000000000..73a86a46918e
--- /dev/null
+++ b/apps/servers/octopus/src/pages/files.rs
@@ -0,0 +1,20 @@
+//! The main file browser
+
+use crate::templ_data::{files::Files, BaseData};
+use actix_web::{web, HttpRequest, HttpResponse, Result};
+use askama::Template;
+
+pub async fn render(req: HttpRequest) -> Result<HttpResponse> {
+ let files = Files {
+ base: BaseData {
+ sitename: "dev.spacekookie.de".into(),
+ ..BaseData::default()
+ },
+ readme: None,
+ path: "".into(),
+ }
+ .render()
+ .unwrap();
+
+ Ok(HttpResponse::Ok().content_type("text/html").body(files))
+}
diff --git a/apps/servers/octopus/src/pages/mod.rs b/apps/servers/octopus/src/pages/mod.rs
new file mode 100644
index 000000000000..2f1ed579c9a9
--- /dev/null
+++ b/apps/servers/octopus/src/pages/mod.rs
@@ -0,0 +1,15 @@
+//! All the pages in webgit
+//!
+//! A page is defined by it's template type as well as it's route,
+//! which is exported from the module and then called by the router
+
+// pub mod repo;
+
+mod overview;
+pub use overview::render as overview;
+
+mod p404;
+pub use p404::render as p404;
+
+mod files;
+pub use files::render as files;
diff --git a/apps/servers/octopus/src/pages/overview.rs b/apps/servers/octopus/src/pages/overview.rs
new file mode 100644
index 000000000000..ca8c9b37064c
--- /dev/null
+++ b/apps/servers/octopus/src/pages/overview.rs
@@ -0,0 +1,24 @@
+//! Overview page
+//!
+//! This is the first page a user sees when they just go to the site
+//! root. It renders the `README`, or `README.md` file from the modo
+//! repo root, to provide users with a starting point.
+
+use crate::templ_data::{overview::Index, BaseData};
+use actix_web::{web, HttpRequest, HttpResponse, Result};
+use askama::Template;
+
+pub async fn render(req: HttpRequest) -> Result<HttpResponse> {
+ let readme: String = markdown::to_html(include_str!("../../fake-readme.md").into());
+
+ let index = Index {
+ base: BaseData {
+ sitename: "dev.spacekookie.de".into(),
+ ..BaseData::default()
+ },
+ readme,
+ }
+ .render()
+ .unwrap();
+ Ok(HttpResponse::Ok().content_type("text/html").body(index))
+}
diff --git a/apps/servers/octopus/src/pages/p404.rs b/apps/servers/octopus/src/pages/p404.rs
new file mode 100644
index 000000000000..6427a19c60b7
--- /dev/null
+++ b/apps/servers/octopus/src/pages/p404.rs
@@ -0,0 +1,13 @@
+use actix_web::{HttpResponse, Result};
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "404.html")]
+struct P404;
+
+/// Render a simple 404 page
+pub async fn render() -> Result<HttpResponse> {
+ Ok(HttpResponse::NotFound()
+ .content_type("text/html")
+ .body(P404.render().unwrap()))
+}
diff --git a/apps/servers/octopus/src/pages/repo/about.rs b/apps/servers/octopus/src/pages/repo/about.rs
new file mode 100644
index 000000000000..1f207e2d56a5
--- /dev/null
+++ b/apps/servers/octopus/src/pages/repo/about.rs
@@ -0,0 +1,26 @@
+use crate::templ_data::repo::*;
+use actix_web::{web, HttpRequest, HttpResponse, Result};
+use askama::Template;
+
+/// Renders the "repository/about" subpage
+pub async fn render(req: HttpRequest, path: web::Path<String>) -> Result<HttpResponse> {
+ let repo = pages::About {
+ readme: include_str!("../../../README").to_string(),
+ repo: RepoData {
+ owner: "spacekookie".into(),
+ name: "octopus".into(),
+ tagline: "A lightweight web frontend for git repositories".into(),
+ num_commit: 141,
+ num_branch: 1,
+ num_tag: 0,
+ num_contributor: 3,
+ size: "13.12M".into(),
+ logo: "fakeavi.png".into(),
+ },
+ base: Default::default(),
+ }
+ .render()
+ .unwrap();
+
+ Ok(HttpResponse::Ok().content_type("text/html").body(repo))
+}
diff --git a/apps/servers/octopus/src/pages/repo/details.rs b/apps/servers/octopus/src/pages/repo/details.rs
new file mode 100644
index 000000000000..7298e15af4b8
--- /dev/null
+++ b/apps/servers/octopus/src/pages/repo/details.rs
@@ -0,0 +1,38 @@
+use crate::templ_data::repo::*;
+use actix_web::{web, HttpRequest, HttpResponse, Result};
+use askama::Template;
+
+/// Renders the "repository/about" subpage
+pub async fn render(req: HttpRequest, path: web::Path<String>) -> Result<HttpResponse> {
+ let last_commit = CommitData {
+ hash: "84a9a0".into(),
+ message: "Updating just like... a bunch of shit".into(),
+ author: "Katharina Fey".into(),
+ date: "Today".into(),
+ diff: (125, 55),
+ };
+
+ let repo = pages::Details {
+ branches: vec![BranchData {
+ name: "develop".into(),
+ last_commit: last_commit.clone(),
+ }],
+ commits: vec![last_commit],
+ repo: RepoData {
+ owner: "spacekookie".into(),
+ name: "octopus".into(),
+ tagline: "A lightweight web frontend for git repositories".into(),
+ num_commit: 141,
+ num_branch: 1,
+ num_tag: 0,
+ num_contributor: 3,
+ size: "13.12M".into(),
+ logo: "fakeavi.png".into(),
+ },
+ base: Default::default(),
+ }
+ .render()
+ .unwrap();
+
+ Ok(HttpResponse::Ok().content_type("text/html").body(repo))
+}
diff --git a/apps/servers/octopus/src/pages/repo/mod.rs b/apps/servers/octopus/src/pages/repo/mod.rs
new file mode 100644
index 000000000000..2b93592624ac
--- /dev/null
+++ b/apps/servers/octopus/src/pages/repo/mod.rs
@@ -0,0 +1,7 @@
+//! The repository page subtree
+
+mod about;
+mod details;
+
+pub use about::render as about;
+pub use details::render as details;
diff --git a/apps/servers/octopus/src/project/mod.rs b/apps/servers/octopus/src/project/mod.rs
new file mode 100644
index 000000000000..cf81fd406873
--- /dev/null
+++ b/apps/servers/octopus/src/project/mod.rs
@@ -0,0 +1,8 @@
+//! Octopus project
+
+use std::{fs::File, path::PathBuf};
+
+/// Check if a directory is a valid project
+pub(crate) fn is_valid(p: PathBuf) -> bool {
+ p.join(".octopus").exists()
+}
diff --git a/apps/servers/octopus/src/repo.rs b/apps/servers/octopus/src/repo.rs
new file mode 100644
index 000000000000..ca1f889027e7
--- /dev/null
+++ b/apps/servers/octopus/src/repo.rs
@@ -0,0 +1,43 @@
+use git2::{Commit, Error, Repository as Backend};
+use std::collections::HashSet;
+
+/// A structure that represents an existing bare repo on disk
+pub struct Repository {
+ inner: Backend,
+}
+
+impl Repository {
+ /// Open an existing bare repo from disk storage
+ pub fn open(path: &'static str) -> Self {
+ Self {
+ inner: Backend::open_bare(path).unwrap(),
+ }
+ }
+
+ /// Get all commits on head
+ pub fn head<'a>(&'a self) -> Result<Vec<Commit<'a>>, Error> {
+ let mut walker = self.inner.revwalk().unwrap();
+ walker.push_head()?;
+ walker
+ .into_iter()
+ .map(|oid| {
+ let oid = oid.unwrap();
+ self.inner.find_commit(oid)
+ })
+ .collect()
+ }
+
+ /// Return the list of contributors
+ fn contributors(&self) -> Result<Vec<String>, Error> {
+ let head = self.head()?;
+ Ok(head
+ .iter()
+ .map(|c| c.author())
+ .fold(HashSet::new(), |mut set, author| {
+ set.insert(author.name().unwrap().to_owned());
+ set
+ })
+ .into_iter()
+ .collect())
+ }
+}
diff --git a/apps/servers/octopus/src/state/mod.rs b/apps/servers/octopus/src/state/mod.rs
new file mode 100644
index 000000000000..5e7f9276bd97
--- /dev/null
+++ b/apps/servers/octopus/src/state/mod.rs
@@ -0,0 +1,14 @@
+//! Core octopus state handling
+
+use std::sync::Arc;
+
+pub(crate) type StateRef = Arc<OctoState>;
+
+/// Holds all state handles for the application
+pub(crate) struct OctoState {}
+
+impl OctoState {
+ pub(crate) fn new() -> StateRef {
+ Arc::new(Self {})
+ }
+}
diff --git a/apps/servers/octopus/src/templ_data/files.rs b/apps/servers/octopus/src/templ_data/files.rs
new file mode 100644
index 000000000000..27a5bde99e5d
--- /dev/null
+++ b/apps/servers/octopus/src/templ_data/files.rs
@@ -0,0 +1,13 @@
+//! File browser template data
+
+use super::BaseData;
+use askama::Template;
+
+// This struct needs escapng=none to render README files it encounters along the way
+#[derive(Template)]
+#[template(path = "files.html", escape = "none")]
+pub(crate) struct Files {
+ pub base: BaseData,
+ pub path: String,
+ pub readme: Option<String>,
+}
diff --git a/apps/servers/octopus/src/templ_data/mod.rs b/apps/servers/octopus/src/templ_data/mod.rs
new file mode 100644
index 000000000000..7645e95ef824
--- /dev/null
+++ b/apps/servers/octopus/src/templ_data/mod.rs
@@ -0,0 +1,38 @@
+//! Octopus template data structures
+//!
+//! All pages are generated by the server via template files that have
+//! data inputs. Because the templates follow a well-defined
+//! structure (i.e. `core` extended by `<type>/base` extended by
+//! `<type>/<page>`, the structure of these template data structures
+//! is the same.
+//!
+//! The actual page initialisation and rendering is nested in the
+//! `page` module, which then uses the appropriate template structures
+//! defined here.
+
+pub(crate) mod overview;
+pub(crate) mod files;
+
+use std::env;
+
+/// A basic application wide template structure
+pub(crate) struct BaseData {
+ pub version: String,
+ pub source: String,
+ pub siteurl: String,
+ pub sitename: String,
+ pub has_wiki: bool,
+}
+
+impl Default for BaseData {
+ fn default() -> Self {
+ Self {
+ version: env!("CARGO_PKG_VERSION").into(),
+ source: env::var("_OCTOPUS_SOURCE")
+ .unwrap_or("https://dev.spacekookie.de/web/octopus".to_string()),
+ siteurl: env::var("_OCTOPUS_SITE_URL").unwrap_or("localhost:8080".to_string()),
+ sitename: env::var("_OCTOPUS_SITE_NAME").unwrap_or("test-octopus".to_string()),
+ has_wiki: true,
+ }
+ }
+}
diff --git a/apps/servers/octopus/src/templ_data/overview.rs b/apps/servers/octopus/src/templ_data/overview.rs
new file mode 100644
index 000000000000..693db0a85e77
--- /dev/null
+++ b/apps/servers/octopus/src/templ_data/overview.rs
@@ -0,0 +1,11 @@
+//! Template data for the main overview
+
+use super::BaseData;
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "index.html", escape = "none")]
+pub(crate) struct Index {
+ pub base: BaseData,
+ pub readme: String,
+}
diff --git a/apps/servers/octopus/src/templ_data/repo.rs b/apps/servers/octopus/src/templ_data/repo.rs
new file mode 100644
index 000000000000..989c9b404620
--- /dev/null
+++ b/apps/servers/octopus/src/templ_data/repo.rs
@@ -0,0 +1,80 @@
+//! Repository specific template data
+
+use std::collections::BTreeMap;
+
+/// A simple overview of a repository
+///
+/// This type can be generated by the octopus Repository state wrapper
+#[derive(Clone)]
+pub(crate) struct RepoData {
+ pub owner: String,
+ pub name: String,
+ pub tagline: String,
+ pub num_commit: usize,
+ pub num_branch: usize,
+ pub num_tag: usize,
+ pub num_contributor: usize,
+ pub size: String,
+ pub logo: String,
+}
+
+/// Data about an individual commit
+#[derive(Clone)]
+pub(crate) struct CommitData {
+ pub hash: String,
+ pub message: String,
+ pub author: String,
+ pub date: String,
+ pub diff: (usize, usize),
+}
+
+/// Data about a branch
+#[derive(Clone)]
+pub(crate) struct BranchData {
+ pub name: String,
+ pub last_commit: CommitData,
+}
+
+/// Data about a repository tree
+#[derive(Clone)]
+pub(crate) struct TreeData {
+ /// The path segments in the current directory
+ curr_dir: Vec<String>,
+ /// The set of children in this tree segment
+ files: BTreeMap<String, FileData>,
+}
+
+/// Information about a concrete file in a tree
+#[derive(Clone)]
+pub(crate) struct FileData {
+ name: String,
+ last_commit: CommitData,
+ is_directory: bool,
+}
+
+pub(crate) mod pages {
+ use super::*;
+ use crate::templ_data::BaseData;
+ use askama::Template;
+
+ #[derive(Template)]
+ #[template(path = "repo/about.html")]
+ pub(crate) struct About {
+ pub base: BaseData,
+ pub repo: RepoData,
+
+ // Template specific
+ pub readme: String,
+ }
+
+ #[derive(Template)]
+ #[template(path = "repo/details.html")]
+ pub(crate) struct Details {
+ pub base: BaseData,
+ pub repo: RepoData,
+
+ // Template specifics
+ pub branches: Vec<BranchData>,
+ pub commits: Vec<CommitData>,
+ }
+}
diff --git a/apps/servers/octopus/static/fakeavi.png b/apps/servers/octopus/static/fakeavi.png
new file mode 100644
index 000000000000..1538bb91f953
--- /dev/null
+++ b/apps/servers/octopus/static/fakeavi.png
Binary files differ
diff --git a/apps/servers/octopus/static/main.css b/apps/servers/octopus/static/main.css
new file mode 100644
index 000000000000..f16cd221130b
--- /dev/null
+++ b/apps/servers/octopus/static/main.css
@@ -0,0 +1,81 @@
+body {
+ margin: 0;
+ font-family: "Roboto", sans-serif;
+ font-weight: 300;
+}
+
+header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+ background: #703f96;
+ padding: 0 2rem;
+ color: white;
+}
+
+header img {
+ max-height: 4rem;
+ padding: 0 1rem 0 0;
+}
+
+header h1 {
+ font-weight: 300;
+ font-size: 2em;
+}
+
+header a {
+ color: #EFEFEF;
+ text-decoration: none;
+ border-bottom: white 1px solid;
+}
+
+header a:hover {
+ color: white;
+}
+
+.nav {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.nav a {
+ font-size: 1.25rem;
+ margin-left: 1rem;
+}
+
+.wrap {
+ padding: 2rem 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: left;
+}
+
+.content {
+ font-weight: 300;
+ width: 700px;
+}
+
+.content h1 {
+ font-weight: 300;
+}
+
+.footer {
+ padding-top: 10rem;
+}
+
+/*************************************************************************/
+
+.explorer {
+ display: grid;
+ margin: 2em -5
+ border: black 0.5px dashed;
+}
+
+.expl-header {
+ font-size: 1.25em;
+ background: #EFEFEF;
+ padding: 0.5em;
+}
diff --git a/apps/servers/octopus/static/octopus.png b/apps/servers/octopus/static/octopus.png
new file mode 100644
index 000000000000..b12ab38a636e
--- /dev/null
+++ b/apps/servers/octopus/static/octopus.png
Binary files differ
diff --git a/apps/servers/octopus/static/octopus.svg b/apps/servers/octopus/static/octopus.svg
new file mode 100644
index 000000000000..e6ebae349a2c
--- /dev/null
+++ b/apps/servers/octopus/static/octopus.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg901"
+ width="192"
+ height="192"
+ viewBox="0 0 192 192"
+ sodipodi:docname="octopus.svg"
+ inkscape:version="1.0.1 (c497b03c, 2020-09-10)">
+ <metadata
+ id="metadata907">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs905" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1225"
+ inkscape:window-height="755"
+ id="namedview903"
+ showgrid="false"
+ inkscape:zoom="3.092402"
+ inkscape:cx="88.86127"
+ inkscape:cy="96.365421"
+ inkscape:window-x="55"
+ inkscape:window-y="23"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg901" />
+ <path
+ style="fill:#ffffff;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers fill stroke;fill-opacity:1"
+ d="m 30.597426,70.945088 c 0,0 7.972569,-5.063659 4.471103,-13.467177 -3.501466,-8.403518 -15.999007,-7.272276 -20.523978,-2.801173 -4.524971,4.471103 -5.5484768,11.312429 -4.309497,14.975501 1.238981,3.663072 5.063659,8.996074 12.874622,12.012722 7.810962,3.016647 17.830542,2.370223 25.372161,0.969636 7.541619,-1.400586 10.504398,-2.424091 11.420166,-2.47796 0.915768,-0.05387 2.208617,-0.161606 0.43095,1.831536 -1.777668,1.993142 -5.171396,5.871689 -10.612136,6.087164 -5.440739,0.215475 -18.261492,-1.023506 -24.779605,0.592556 -6.518114,1.616061 -13.736521,7.0568 -13.844258,15.837397 -0.107738,8.7806 5.171396,17.3996 14.490682,17.72281 9.319287,0.32321 16.914774,-4.20176 16.160612,-13.62879 -0.754162,-9.427019 -8.780599,-7.54161 -8.780599,-7.54161 l 0.107737,0.37708 c 0,0 4.740447,2.63956 5.333003,3.93241 0.592555,1.29285 2.316354,5.54848 -1.723799,8.18805 -4.040153,2.63956 -11.420166,5.54847 -15.999006,-0.8619 -4.57884,-6.41038 -2.962779,-14.813898 4.255628,-17.022515 7.218406,-2.208617 14.544551,-0.8619 22.786463,0.754162 8.241912,1.616061 15.783531,1.454455 21.116533,-1.185112 5.333002,-2.639567 4.471103,0.484818 4.471103,0.484818 0,0 -2.424092,4.363367 -4.094022,8.349647 -1.66993,3.98629 -3.878547,11.04309 -4.471102,17.83055 -0.592556,6.78745 -0.161607,13.4133 1.562192,21.70908 1.723799,8.29579 3.824678,15.35259 0.646425,19.44661 -3.178254,4.09402 -9.750237,6.03329 -13.79039,1.83153 -4.040153,-4.20176 -3.393728,-11.25856 -0.80803,-13.73652 2.585698,-2.47796 6.410376,-1.50832 7.649356,0 1.238981,1.50833 1.292849,1.7238 1.66993,1.7238 0.377081,0 0.646425,-1.8854 -0.538687,-4.04015 -1.185111,-2.15475 -3.932416,-6.78746 -10.935348,-5.27913 -7.002931,1.50832 -10.989216,12.87462 -8.241912,20.68558 2.747304,7.81096 13.521046,13.14396 21.27814,10.71987 7.757094,-2.42409 14.113601,-8.72673 12.874621,-19.33886 -1.23898,-10.61214 -4.524972,-20.36238 -2.693436,-27.68852 1.831537,-7.32614 10.719873,-18.69244 12.066591,-22.8942 1.346718,-4.20176 1.66993,-6.087164 1.885405,-6.518114 0.215475,-0.430949 0.592556,-0.377081 0.80803,0.484819 0.215475,0.861899 2.747304,12.120455 2.585698,18.907915 -0.161606,6.78746 -1.508323,12.55141 -1.66993,18.85405 -0.161606,6.30264 0.323213,19.39273 7.002932,24.29479 6.679716,4.90205 15.406446,7.11067 24.187046,0.53868 8.7806,-6.57198 7.00294,-17.99215 2.10088,-21.1704 -4.90205,-3.17825 -8.67286,-3.12438 -12.22819,-0.10774 -3.55534,3.01665 -1.50833,5.49461 -1.23898,5.65622 0.26934,0.1616 0.64642,-0.16161 0.91576,-1.18511 0.26935,-1.02351 2.04702,-3.87855 5.27914,-3.82468 3.23212,0.0539 6.62585,2.63956 5.97942,6.94906 -0.64642,4.3095 -0.48481,8.29578 -7.43388,10.45053 -6.94906,2.15475 -13.14396,-1.40059 -15.514185,-8.7806 -2.370223,-7.38001 -1.723798,-14.49068 -0.215475,-21.54748 1.508324,-7.0568 4.4711,-16.26835 4.95592,-21.43975 0.48482,-5.17139 0.21548,-8.726728 0.43095,-9.48089 0.21548,-0.754162 0.70029,-0.107738 1.61606,0.80803 0.91577,0.915768 5.49461,5.87169 11.52791,10.07345 6.03329,4.20176 12.01272,7.38001 22.40938,8.34965 10.39666,0.96964 18.69244,1.13124 22.19391,4.20176 3.50146,3.07051 5.81782,9.04994 2.90891,12.71301 -2.90891,3.66307 -9.85798,5.54848 -14.38295,1.23898 -4.52497,-4.30949 -1.83154,-8.99607 0.64643,-11.20469 2.47796,-2.20862 2.90891,-1.40058 2.85504,-1.99314 -0.0539,-0.59256 -0.91577,-0.37708 -1.88541,-0.21548 -0.96963,0.16161 -7.48775,3.98629 -8.02644,7.43389 -0.53868,3.44759 -0.86189,9.31928 4.20176,12.33593 5.06366,3.01665 15.19098,3.4476 19.76982,-2.90891 4.57884,-6.35651 5.44074,-17.02251 -2.10088,-23.27128 -7.54162,-6.24877 -19.98529,-4.20176 -27.47304,-7.97257 -7.48775,-3.77081 -13.73652,-8.134175 -17.77667,-13.413308 -4.04016,-5.279134 -3.98629,-5.333002 -4.04016,-6.033296 -0.0539,-0.700293 0.10774,-0.646424 1.5622,-0.377081 1.45445,0.269344 14.22133,2.154749 22.84033,-1.993142 8.61899,-4.14789 17.77667,-10.612135 23.54062,-11.797247 5.76395,-1.185111 10.77374,0.107738 12.33594,5.548477 1.56219,5.44074 0.37708,10.773742 -3.23213,12.713015 -3.6092,1.939274 -9.64249,2.531829 -10.88147,-2.747304 -1.23899,-5.279133 2.69343,-8.080306 6.08716,-7.703225 3.39373,0.377081 4.74045,0.915768 3.17825,-1.023506 -1.56219,-1.939273 -5.87169,-3.71694 -10.07345,-1.400586 -4.20175,2.316354 -5.54847,5.656214 -4.57884,10.235054 0.96964,4.578841 3.87855,7.002932 8.67287,7.972569 4.79431,0.969637 11.68951,-1.454455 14.16747,-7.218407 2.47796,-5.763951 5.65621,-12.228196 -1.40059,-19.931421 -7.0568,-7.703226 -18.26149,-2.908911 -23.54063,-0.969637 -5.27913,1.939273 -13.03622,7.0568 -19.28499,7.272275 -6.24877,0.215475 -9.31929,-0.215475 -10.55827,-1.077374 -1.23898,-0.861899 -0.91577,-1.939273 0.59256,-2.747304 1.50832,-0.808031 16.16061,-4.417234 18.63857,-5.710083 2.47796,-1.292849 17.29185,-6.787457 17.07638,-18.369229 -0.21548,-11.581772 -3.55534,-13.521046 -5.81782,-15.190976 -2.26249,-1.66993 -6.67972,-5.656214 -13.35944,-3.824678 -6.67972,1.831536 -10.45053,5.171396 -11.3663,8.026437 -0.91577,2.855042 -2.10088,7.757094 0.43095,11.043085 2.53183,3.285992 6.73359,5.333003 8.67286,4.363366 1.93928,-0.969637 2.63957,-0.915768 2.69344,-1.400587 0.0539,-0.484818 0.16161,-1.077374 -1.99314,-1.077374 -2.15475,0 -5.22527,-0.161606 -6.62585,-4.794315 -1.40059,-4.632709 1.93927,-8.618993 5.44074,-9.804105 3.50146,-1.185111 8.9422,-1.669929 12.49754,1.993143 3.55533,3.663072 5.17139,10.82761 0.91576,14.9755 -4.25562,4.147891 -8.18804,5.117528 -17.02251,6.141033 -8.83447,1.023505 -18.69244,3.824678 -20.52398,4.632709 -1.83153,0.808031 -2.58569,1.723799 -2.90891,1.454455 -0.32321,-0.269344 0.26935,-4.417234 0.7003,-6.410376 0.43095,-1.993142 1.61606,-17.022512 -0.8619,-23.863838 -2.47796,-6.841326 -5.97943,-13.682651 -13.467179,-15.8374 -7.48775,-2.154748 -17.184117,-2.531829 -23.863837,1.723799 -6.67972,4.255628 -13.35944,16.160612 -12.659146,25.479899 0.700293,9.319286 1.131242,12.820752 1.939273,15.999006 0.808031,3.178254 1.66993,6.087164 1.400586,6.733588 -0.269343,0.646425 -0.323212,0.8619 -2.208617,0.538687 -1.885404,-0.323212 -9.480892,0.592556 -14.490682,2.639567 -5.00979,2.047011 -12.713015,4.848184 -15.40645,5.063659 -2.693436,0.215474 -7.972569,0.646424 -11.851116,-3.178254 -3.878547,-3.824678 -3.285991,-10.181186 -2.747304,-11.743378 0.538687,-1.562193 2.962779,-5.171396 7.110669,-4.524972 4.147891,0.646425 5.87169,3.501466 5.87169,5.171396 0,1.66993 0.161606,5.81782 -0.808031,6.733588 -0.969637,0.915768 -0.969637,1.292849 -0.646425,1.562193 0.323213,0.269344 0.646425,0.377081 0.646425,0.377081 z"
+ id="path913"
+ inkscape:export-filename="/Users/spacekookie/path913.png"
+ inkscape:export-xdpi="300"
+ inkscape:export-ydpi="300" />
+</svg>
diff --git a/apps/servers/octopus/static/rust.png b/apps/servers/octopus/static/rust.png
new file mode 100644
index 000000000000..a706d7cd20fc
--- /dev/null
+++ b/apps/servers/octopus/static/rust.png
Binary files differ
diff --git a/apps/servers/octopus/supergit/Cargo.toml b/apps/servers/octopus/supergit/Cargo.toml
new file mode 100644
index 000000000000..029503e87c44
--- /dev/null
+++ b/apps/servers/octopus/supergit/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "supergit"
+description = "A strongly typed, read-only representation of a git repository"
+version = "0.1.0"
+authors = ["Mx Kookie <kookie@spacekookie.de>"]
+edition = "2018"
+
+[dependencies]
+git2 = "0.11"
+async-std = { version = "1.0", features = ["unstable"] } \ No newline at end of file
diff --git a/apps/servers/octopus/supergit/src/bin/test.rs b/apps/servers/octopus/supergit/src/bin/test.rs
new file mode 100644
index 000000000000..166047e9dd17
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/bin/test.rs
@@ -0,0 +1,16 @@
+//! A test binary to use during development
+
+use supergit::raw::RawRepository;
+
+fn main() {
+ let path = match std::env::args().nth(1) {
+ Some(p) => p,
+ None => {
+ eprintln!("USAGE: supergit-test <path>");
+ std::process::exit(2);
+ }
+ };
+
+ let rr = RawRepository::open(path.as_str()).unwrap();
+
+}
diff --git a/apps/servers/octopus/supergit/src/branch.rs b/apps/servers/octopus/supergit/src/branch.rs
new file mode 100644
index 000000000000..81abbffed112
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/branch.rs
@@ -0,0 +1,22 @@
+use crate::{Commit, CommitId};
+
+/// Abstraction for a branch history slice
+pub struct Branch {
+ name: String,
+ head: CommitId,
+ history: Vec<BranchCommit>,
+}
+
+/// A commit represented as a relationship to a branch
+///
+/// Most commits will be simple, meaning they are in sequence on the
+/// branch. Two types of merge commits exist: normal, and octopus.
+/// All branches leading into this branch are a reverse tree
+pub enum BranchCommit {
+ /// A single commit
+ Commit(Commit),
+ /// A merge commit from one other branch
+ Merge(Branch),
+ /// An octopus merge with multiple branches
+ Octopus(Vec<Branch>),
+}
diff --git a/apps/servers/octopus/supergit/src/commit.rs b/apps/servers/octopus/supergit/src/commit.rs
new file mode 100644
index 000000000000..46a6ab4f7c8b
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/commit.rs
@@ -0,0 +1,11 @@
+pub type CommitId = usize;
+
+/// Represent a commit on a repository
+///
+/// This abstraction only contains metadata required to fetch the full
+/// commit from disk, if it is queried. Any operation on this type
+/// will block to first load
+pub struct Commit {
+ pub id: CommitId,
+ hash: String,
+}
diff --git a/apps/servers/octopus/supergit/src/diff.rs b/apps/servers/octopus/supergit/src/diff.rs
new file mode 100644
index 000000000000..e92a4cd1804c
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/diff.rs
@@ -0,0 +1,5 @@
+
+/// A diff between two commits
+pub struct Diff {
+
+}
diff --git a/apps/servers/octopus/supergit/src/files.rs b/apps/servers/octopus/supergit/src/files.rs
new file mode 100644
index 000000000000..681b8877256f
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/files.rs
@@ -0,0 +1,7 @@
+
+pub type FileId = usize;
+
+/// A file to have ever existed in a git repo
+pub struct File {
+ id: FileId,
+}
diff --git a/apps/servers/octopus/supergit/src/lib.rs b/apps/servers/octopus/supergit/src/lib.rs
new file mode 100644
index 000000000000..887ccc029e1f
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/lib.rs
@@ -0,0 +1,55 @@
+//! Strongly typed git repository explorer library
+//!
+//! This library exposes a read-only view into a git repository. To
+//! get started, open an existing bare repo, and then call `sync()` to
+//! build a cache of it. Every time you want your view of the repo to
+//! update, call `sync()` again. If you want the sync operation to be
+//! blocking, call `sync_blocking()` instead.
+//!
+//!
+
+
+mod branch;
+pub use branch::{Branch, BranchCommit};
+
+mod commit;
+pub use commit::{CommitId, Commit};
+
+mod diff;
+pub use diff::Diff;
+
+pub mod raw;
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use async_std::sync::{Arc, RwLock};
+
+use raw::RawRepository;
+
+/// Represents a git repository with lazy data loading
+pub struct Repository {
+ raw: RawRepository,
+}
+
+impl Repository {
+ pub fn open(path: &std::path::Path) -> Arc<Self> {
+ todo!()
+ }
+
+ /// Sync the repository with the backing git files
+ ///
+ /// This function can be invoked manually, but should be invoked
+ /// basically every time your program expects changes to have
+ /// happened. Polling this function is not recommended.
+ pub fn sync(&self) {
+ todo!()
+ }
+}
+
+/////////// IDs are created from the same pool to save on code size ////////////
+
+const ID_CTR: AtomicUsize = AtomicUsize::new(0);
+
+/// Get monotonically increasing IDs for objects
+pub(crate) fn id() -> usize {
+ ID_CTR.fetch_add(1, Ordering::Relaxed)
+}
diff --git a/apps/servers/octopus/supergit/src/raw/#tree_walk.rs# b/apps/servers/octopus/supergit/src/raw/#tree_walk.rs#
new file mode 100644
index 000000000000..a693f624af46
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/raw/#tree_walk.rs#
@@ -0,0 +1 @@
+//! Walk the file tree for a particular commit \ No newline at end of file
diff --git a/apps/servers/octopus/supergit/src/raw/.#tree_walk.rs b/apps/servers/octopus/supergit/src/raw/.#tree_walk.rs
new file mode 120000
index 000000000000..a0d266615930
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/raw/.#tree_walk.rs
@@ -0,0 +1 @@
+spacekookie@qq.53166 \ No newline at end of file
diff --git a/apps/servers/octopus/supergit/src/raw/branch_walk.rs b/apps/servers/octopus/supergit/src/raw/branch_walk.rs
new file mode 100644
index 000000000000..d4232c486560
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/raw/branch_walk.rs
@@ -0,0 +1,19 @@
+//! Walk along a branch parsing commit metadata
+
+use std::collections::{BTreeMap, BTreeSet};
+
+pub struct CommitHistory {
+ /// The correct order of commit IDs
+ order: Vec<String>,
+ /// Map of commit IDs to commit metadata
+ meta: BTreeMap<String, CommitNode>,
+}
+
+pub struct CommitNode {
+ id: String,
+ author: String,
+ commiter: String,
+ message: String,
+ touches: BTreeSet<String>,
+ time: u64,
+}
diff --git a/apps/servers/octopus/supergit/src/raw/mod.rs b/apps/servers/octopus/supergit/src/raw/mod.rs
new file mode 100644
index 000000000000..7bf6c0a396a4
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/raw/mod.rs
@@ -0,0 +1,45 @@
+//! Raw representation wrappers for libgit2
+
+mod branch_walk;
+mod tree_walk;
+
+use crate::{Branch, BranchCommit};
+use git2::{self, Repository};
+
+pub type RawResult<T> = Result<T, RawError>;
+
+/// An error abstraction for raw git operations
+#[derive(Debug)]
+pub enum RawError {
+ AllBad,
+}
+
+impl From<git2::Error> for RawError {
+ fn from(_: git2::Error) -> Self {
+ Self::AllBad
+ }
+}
+
+/// Represent a raw branch
+pub struct RawBranch {
+ name: String,
+ head: String,
+}
+
+/// Wrap a libgit2 repository to provide an API fascade
+pub struct RawRepository {
+ inner: Repository,
+}
+
+impl RawRepository {
+ pub fn open(path: &str) -> RawResult<Self> {
+ Ok(Self {
+ inner: Repository::open(path)?,
+ })
+ }
+
+ /// Parse branch data from repository
+ pub fn parse_branches(&self) -> RawResult<Vec<RawBranch>> {
+ todo!()
+ }
+}
diff --git a/apps/servers/octopus/supergit/src/raw/tree_walk.rs b/apps/servers/octopus/supergit/src/raw/tree_walk.rs
new file mode 100644
index 000000000000..05337640cd19
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/raw/tree_walk.rs
@@ -0,0 +1 @@
+//! Walk the file tree for a particular commit
diff --git a/apps/servers/octopus/templates/404.html b/apps/servers/octopus/templates/404.html
new file mode 100644
index 000000000000..02168e78d059
--- /dev/null
+++ b/apps/servers/octopus/templates/404.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>404 - octopus</title>
+ </head>
+
+ <body>
+ <h1>404</h1>
+ <p>This ain't it chief</p>
+ </body>
+</html>
diff --git a/apps/servers/octopus/templates/core.html b/apps/servers/octopus/templates/core.html
new file mode 100644
index 000000000000..4112cc669436
--- /dev/null
+++ b/apps/servers/octopus/templates/core.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link href="/static/main.css" rel="stylesheet">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta http-equiv="Cache-control" content="no-cache">
+
+ {% block title %}{% endblock %}
+ </head>
+ <body>
+
+ <header>
+ <div class="nav">
+ <img src="/static/octopus.png" />
+ <h1>{{ base.sitename }}</h1>
+ </div>
+ <div class="nav">
+ <a href="{{ base.siteurl }}/">Overview</a>
+ <a href="{{ base.siteurl }}/tree">File Browser</a>
+ <a href="{{ base.siteurl }}/wiki">Wiki</a>
+ </div>
+ </header>
+
+ {# Limits the width #}
+ <div class="wrap">
+ <div class="content">
+ {% block content %}{% endblock %}
+ </div>
+
+ <div class="footer">
+<pre>
+Generated by <strong>octopus {{ base.version }}</strong>
+In compliance with the <a href="https://en.wikipedia.org/wiki/Affero_General_Public_License">AGPL</a> you can find the sources for this software <strong><a href="{{ base.source }}">here</a></strong>!
+</pre>
+ </div>
+
+ </div>
+ </body>
+</html>
diff --git a/apps/servers/octopus/templates/files.html b/apps/servers/octopus/templates/files.html
new file mode 100644
index 000000000000..02b02018f68f
--- /dev/null
+++ b/apps/servers/octopus/templates/files.html
@@ -0,0 +1,17 @@
+{% extends "core.html" %}
+
+{% block content %}
+
+<h1>File Tree</h1>
+
+<p>Following is a copy of the file tree. You can clone any sub-directry that is marked as a "project".</p>
+
+<div class="explorer">
+
+ {# header section #}
+ <div class="expl-header">
+ <span><strong>Mx Kookie</strong> <a href="">Just fucking around with stuff</a></span>
+ </div>
+</div>
+
+{% endblock %}
diff --git a/apps/servers/octopus/templates/index.html b/apps/servers/octopus/templates/index.html
new file mode 100644
index 000000000000..fd23cc2c311a
--- /dev/null
+++ b/apps/servers/octopus/templates/index.html
@@ -0,0 +1,7 @@
+{% extends "core.html" %}
+
+{% block content %}
+
+{{ readme }}
+
+{% endblock %}
diff --git a/apps/servers/octopus/templates/repo/about.html b/apps/servers/octopus/templates/repo/about.html
new file mode 100644
index 000000000000..5c476aa79df1
--- /dev/null
+++ b/apps/servers/octopus/templates/repo/about.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+
+{% block child_content %}
+<pre class="readme">{{ readme }}</pre>
+{% endblock %}
+
diff --git a/apps/servers/octopus/templates/repo/base.html b/apps/servers/octopus/templates/repo/base.html
new file mode 100644
index 000000000000..10994557285b
--- /dev/null
+++ b/apps/servers/octopus/templates/repo/base.html
@@ -0,0 +1,37 @@
+{% extends "../core.html" %}
+{% block title %}<title>octopus | /{{ repo.owner }}/{{ repo.name }}</title>{% endblock %}
+
+{% block content %}
+<div class="tagline-container">
+ <img class="repo-logo" src="/static/{{ repo.logo }}" />
+ <h1><a href="">{{ repo.owner }}</a> / <a href="">{{ repo.name }}</a></h1>
+ <p>🐙 It's a water animal</p>
+ <div class="starbox">
+ <a href="">Clone</a>
+ <a href="">Star</a>
+ <a href="">RSS</a>
+ </div>
+</div> <!-- tagline container -->
+<div class="subheader">
+
+ <a href="">{{ repo.num_commit }} commits</a>
+ <a href="">{{ repo.num_branch}} branch</a>
+ <a href="">{{ repo.num_tag }} tags</a>
+ <a href="">{{ repo.num_contributor }} contributor</a>
+ <a href="">size: {{ repo.size }}</a>
+ <input id="repo-search" type="search" placeholder="Search repository" />
+ <!-- <label for="repo-search">Search for files in the repository</label> -->
+</div> <!-- subheader -->
+<hr />
+<div class="repo-navigation">
+ <a href="{{ base.url }}/{{ repo.name }}" class="nav-item">about</a>
+ <a href="{{ base.url }}/{{ repo.name }}/details" class="nav-item">details</a>
+ <a href="{{ base.url }}/{{ repo.name }}/files" class="nav-item">files</a>
+ <a href="{{ base.url }}/{{ repo.name }}/log" class="nav-item">log</a>
+ <a href="{{ base.url }}/{{ repo.name }}/patches" class="nav-item">patches</a>
+ <a href="{{ base.url }}/{{ repo.name }}/contribute" class="nav-item">contribute</a>
+</div>
+
+{% block child_content %}{% endblock %}
+
+{% endblock %}
diff --git a/apps/servers/octopus/templates/repo/details.html b/apps/servers/octopus/templates/repo/details.html
new file mode 100644
index 000000000000..46bf9b6461f2
--- /dev/null
+++ b/apps/servers/octopus/templates/repo/details.html
@@ -0,0 +1,49 @@
+{% extends "base.html" %}
+
+{% block child_content %}
+<table class="details-table">
+ <thead>
+ <tr>
+ <th>Branch</th>
+ <th>Commit message</th>
+ <th>Author</th>
+ <th>Date</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for b in branches %}
+ <tr>
+ <td>{{ b.name }}</td>
+ <td>{{ b.last_commit.message }}</td>
+ <td>{{ b.last_commit.author }}</td>
+ <td>{{ b.last_commit.date }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+
+<table class="details-table">
+ <thead>
+ <tr>
+ <th>Hash</th>
+ <th>Commit message</th>
+ <th>Author</th>
+ <th>Date</th>
+ <th>Lines</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for c in commits %}
+ <tr>
+ <td>{{ c.hash }}</td>
+ <td>{{ c.message }}</td>
+ <td>{{ c.author }}</td>
+ <td>{{ c.date }}</td>
+ <td>+{{ c.diff.0 }}/ -{{ c.diff.1 }}
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+
+
+{% endblock %}