diff --git a/.circleci/config.yml b/.circleci/config.yml index dc5ddb2..1b5159c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,75 +1,100 @@ version: 2.1 + +orbs: + docker: circleci/docker@0.5.20 + jobs: - setup-build-env: - working_directory: ~/rookeries_ci + build-rookeries: docker: - - image: dorianpula/circleci-test:2.0.0 + - image: dorianpula/rookeries-build:latest steps: - checkout - - setup_remote_docker - run: - name: Create build image - command: | - docker pull dorianpula/rookeries-build:latest || true - docker build . --tag dorianpula/rookeries-build:latest - - deploy: - name: Upload Build Images to Docker Hub - command: | - docker login -u ${DOCKER_USER} -p ${DOCKER_PASS} - docker push dorianpula/rookeries-build:latest + name: Build Rookeries + command: cargo make build-all test-site-generator: - working_directory: ~/rookeries_ci docker: - - image: dorianpula/circleci-test:2.0.0 + - image: dorianpula/rookeries-build:latest steps: - checkout - - setup_remote_docker + - run: + name: Audit dependencies. + command: cargo audit + - run: + name: Build Rookeries + command: | + cargo make build-all + # Initial run to resolve test issue. + INITIAL_RUN=$(mktemp -d) + cargo run -- init ${INITIAL_RUN} - run: name: Test static site generator - command: | - docker pull dorianpula/rookeries-build:latest - docker run dorianpula/rookeries-build:latest cargo audit - docker run dorianpula/rookeries-build:latest cargo make test + command: cargo make test + - run: + name: Check code with clippy + command: cargo clippy test-site-app: - working_directory: ~/rookeries_ci docker: - - image: dorianpula/circleci-test:2.0.0 + - image: dorianpula/rookeries-build:latest steps: - checkout - - setup_remote_docker + - run: + name: Build Rookeries + command: cargo make build-all - run: name: Test site webapp - command: | - docker pull dorianpula/rookeries-build:latest - docker run dorianpula/rookeries-build:latest cargo make test-js + command: cargo make test-js test-plugins: - working_directory: ~/rookeries_ci docker: - - image: dorianpula/circleci-test:2.0.0 + - image: dorianpula/rookeries-build:latest steps: - checkout - - setup_remote_docker - run: name: Test plugins - command: | - docker pull dorianpula/rookeries-build:latest - docker run dorianpula/rookeries-build:latest cargo make test-plugins + command: cargo make test-plugins workflows: version: 2 - build-test-push: +# TODO: Add a workflow that will create OS X and FreeBSD versions when a tag is present. And will upload to Object Storage. + scheduled-docker-build: jobs: - - setup-build-env + - docker/publish: + image: dorianpula/rookeries-build + docker-username: DOCKER_USER + docker-password: DOCKER_PASS + tag: latest + triggers: + - schedule: + cron: "0 15 * * 6" + filters: + branches: + only: + - master +# # -- Uncomment when we need to do manual docker rebuilds. -- +# manual-docker-build: +# jobs: +# - push_start: +# type: approval +# - docker/publish: +# image: dorianpula/rookeries-build +# docker-username: DOCKER_USER +# docker-password: DOCKER_PASS +# tag: latest +# requires: +# - push_start + test: + jobs: + - build-rookeries - test-site-generator: requires: - - setup-build-env + - build-rookeries - test-site-app: requires: - - setup-build-env + - build-rookeries - test-plugins: requires: - - setup-build-env + - build-rookeries diff --git a/.circleci/primary-test-image/Dockerfile b/.circleci/primary-test-image/Dockerfile deleted file mode 100644 index e681125..0000000 --- a/.circleci/primary-test-image/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM rust:1.34 - -RUN apt-get update -qq && \ - apt-get install -qq \ - apt-transport-https \ - ca-certificates \ - curl \ - gnupg2 \ - software-properties-common \ - python-pip \ - git - -# Docker and docker-compose -RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - &&\ - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \ - apt-get -qq update && apt-get install -y docker-ce && \ - pip install docker-compose - -RUN cargo install cargo-make diff --git a/Cargo.lock b/Cargo.lock index c19d82c..b1f65ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -24,7 +24,7 @@ dependencies = [ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-resolver 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -42,7 +42,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -80,11 +80,11 @@ dependencies = [ "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -104,8 +104,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -132,7 +132,7 @@ dependencies = [ "actix-server-config 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -170,7 +170,7 @@ dependencies = [ "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -186,7 +186,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -212,11 +212,11 @@ dependencies = [ "encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -240,10 +240,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aho-corasick" -version = "0.6.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -289,7 +289,7 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -304,7 +304,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -395,6 +395,14 @@ dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bstr" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byte-tools" version = "0.3.1" @@ -455,6 +463,15 @@ dependencies = [ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chrono-tz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "parse-zoneinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -490,6 +507,14 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.6.5" @@ -499,6 +524,16 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ctor" version = "0.1.12" @@ -527,7 +562,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -576,7 +611,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.72 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -674,19 +709,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "error-chain" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "failure" version = "0.1.5" @@ -806,9 +833,25 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.2.11" +name = "globset" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "globwalk" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ignore 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "h2" @@ -821,7 +864,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -879,6 +922,23 @@ dependencies = [ "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ignore" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.0.2" @@ -983,7 +1043,7 @@ dependencies = [ [[package]] name = "log" -version = "0.4.4" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1009,11 +1069,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.0.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "mime" @@ -1072,7 +1129,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1085,7 +1142,7 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1131,7 +1188,7 @@ name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1257,11 +1314,24 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parse-zoneinfo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pest" version = "2.1.1" @@ -1431,7 +1501,7 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1461,7 +1531,7 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1470,7 +1540,7 @@ name = "rand_chacha" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1553,7 +1623,7 @@ name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1599,23 +1669,19 @@ dependencies = [ [[package]] name = "regex" -version = "1.0.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.2" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "resolv-conf" @@ -1644,16 +1710,16 @@ dependencies = [ "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "fake 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "opener 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.72 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1891,22 +1957,23 @@ dependencies = [ [[package]] name = "tera" -version = "0.11.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "globwalk 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1929,7 +1996,7 @@ dependencies = [ [[package]] name = "thread_local" -version = "0.3.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1988,7 +2055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1999,7 +2066,7 @@ dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2065,7 +2132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2091,7 +2158,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2114,7 +2181,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2132,53 +2199,48 @@ name = "ucd-trie" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "ucd-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unic-char-property" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-char-range" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unic-common" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unic-segment" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-ucd-segment" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-char-property 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-ucd-version" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-common 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2231,11 +2293,6 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "utf8-ranges" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "uuid" version = "0.6.5" @@ -2401,14 +2458,14 @@ dependencies = [ "checksum actix-web 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0dc7ab62d04b9eeb0f368ad9c6ee20c2e3541fb9a25a5eb727c9118b59e8ff2" "checksum actix-web-codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe9e3cdec1e645b675f354766e0688c5705021c85ab3cf739be1c8999b91c76" "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"checksum aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c6d463cbe7ed28720b5b489e7c083eeb8f90d08be2a0d6bb9e1ffea9ce1afa" +"checksum aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum awc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4c4763e6aa29a801d761dc3464f081d439ea5249ba90c3c3bdfc8dd3f739d233" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" @@ -2419,6 +2476,7 @@ dependencies = [ "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" "checksum brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +"checksum bstr 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8a65814ca90dfc9705af76bb6ba3c6e2534489a72270e797e603783bb4990b" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" @@ -2427,12 +2485,15 @@ dependencies = [ "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c" "checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" +"checksum chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e0e430fad0384e4defc3dc6b1223d1b886087a8bf9b7080e5ae027f73851ea15" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03" "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" "checksum copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" "checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" @@ -2454,7 +2515,6 @@ dependencies = [ "checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" "checksum enum-as-inner 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" "checksum env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "f4d7e69c283751083d53d01eac767407343b8b69c4bd70058e08adc2637cb257" -"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ee967b904f8a2ffc72c1cce48981f08b1e5802a8c520aa02dd9cd78b95063a3e" @@ -2470,7 +2530,8 @@ dependencies = [ "checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +"checksum globwalk 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53cbcf0368596897b0a3b8ff2110acf2400e80ffad4ca9238b52ff282a9b267b" "checksum h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "a539b63339fbbb00e081e84b6e11bd1d9634a82d91da2984a18ac74a8823f392" "checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" @@ -2479,6 +2540,7 @@ dependencies = [ "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum ignore 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "522daefc3b69036f80c7d2990b28ff9e0471c683bad05ca258e0a01dd22c5a1e" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" @@ -2493,11 +2555,11 @@ dependencies = [ "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -"checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" "checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" @@ -2523,7 +2585,9 @@ dependencies = [ "checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" +"checksum parse-zoneinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "feece9d0113b400182a7d00adcff81ccf29158c49c5abd11e2eed8589bf6ff07" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "933085deae3f32071f135d799d75667b63c8dc1f4537159756e3d4ceab41868c" "checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" "checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" @@ -2562,8 +2626,8 @@ dependencies = [ "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" -"checksum regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbbea44c5490a1e84357ff28b7d518b4619a159fed5d25f6c1de2d19cc42814" -"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92" "checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" @@ -2596,10 +2660,10 @@ dependencies = [ "checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3" +"checksum tera 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8501ae034d1c2d2e8c29f3c259d919c11489220d8ee8a268e7e1fe3eff94c86a" "checksum termcolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "722426c4a0539da2c4ffd9b419d90ad540b4cff4a053be9069c908d4d07e2836" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" @@ -2617,13 +2681,12 @@ dependencies = [ "checksum trust-dns-resolver 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c9992e58dba365798803c0b91018ff6c8d3fc77e06977c4539af2a6bfe0a039" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" -"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" -"checksum unic-char-property 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce36d3f7ce754afdbccccf8ff0dd0134e50fb44aaae579f96218856e9e5dbd1e" -"checksum unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ab85fab42ad1b26cafc03bf891f69cb4d6e15f491030e89a0122197baa8ae8" -"checksum unic-common 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8d4a7ade929ef7d971e16ced21a8cd56a63869aa6032dfb8cb083cf7d077bf" -"checksum unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ca47cbb09fb5fcd066b5867d11dc528302fa465277882797d6a836e1ee6f9e" -"checksum unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48f1a08ce0409a9e391b88d1930118eec48af12742fc538bcec55f775865776e" -"checksum unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1f5e6c6c53c2d0ece4a5964bc55fcff8602153063cb4fab20958ff32998ff6" +"checksum unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +"checksum unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +"checksum unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +"checksum unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +"checksum unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" @@ -2631,7 +2694,6 @@ dependencies = [ "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" "checksum v_escape 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8865501b78eef9193c1b45486acf18ba889e5662eba98854d6fc59d8ecf3542d" "checksum v_escape_derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "306896ff4b75998501263a1dc000456de442e21d68fe8c8bdf75c66a33a58e23" diff --git a/Cargo.toml b/Cargo.toml index ce21001..14e8f45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ regex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -tera = "0.11" +tera = "1.0" toml = "0.5" uuid = { version = "0.6", features = ["serde", "v4"] } # Can not include the default bzip2 support due to OS X linking issues. diff --git a/Dockerfile b/Dockerfile index 5fcfbf1..e41003b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM rust:1.39-stretch MAINTAINER Dorian Pula # Cache the Rust dependencies -> http://whitfin.io/speeding-up-rust-docker-builds/ -RUN rustup component add rustfmt && \ +RUN rustup component add rustfmt clippy && \ cargo install cargo-make && \ cargo install cargo-audit diff --git a/docs/changelog.md b/docs/changelog.md index 93713ce..1b7501d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,7 +4,14 @@ releases_release_uri = "https://github.com/dorianpula/rookeries/releases/%s" releases_issue_uri = "https://bitbucket.org/dorianpula/rookeries/issues/%s" * :release:`0.18.0 <2019-02-??>` +* :feature:`38` Add support for TOML-based frontmatter for Markdown source files. +* :feature:`38` Use new and improved site format. * :support:`37` General code clean-up as part of using Rust 2018. +* :support:`37` Add support for migrating old versions of Rookeries site to a newer version. +* :support:`39` Significant improvements to the CI build process. +* :support:`38` Improve code layout and updates to site format and templates. +* :support:`38` Add support for new object store for built artifacts. +* :support:`38` Add backwards compatibility with v0.17.0 sites. * :release:`0.17.0 <2019-01-20>` * :feature:`35` Add support for automatically rebuilding the site when using diff --git a/plugins/plugin-builder.sh b/plugins/plugin-builder.sh index d549fb6..3c2d56d 100755 --- a/plugins/plugin-builder.sh +++ b/plugins/plugin-builder.sh @@ -14,7 +14,7 @@ case $1 in ;; esac -capital=$(echo "{action}" | awk '{ print toupper(substr($1, 1,1)) substr($1, 2) }') +capital=$(echo "${action}" | awk '{ print toupper(substr($1, 1,1)) substr($1, 2) }') description="${capital}ing" for plugin in ${PLUGINS}; diff --git a/rookeries-online-installer.sh b/rookeries-online-installer.sh index 487780c..0bcf881 100755 --- a/rookeries-online-installer.sh +++ b/rookeries-online-installer.sh @@ -46,12 +46,12 @@ check_shasum_installed () { } build_rookeries_path () { - echo "https://rookeries.org/${1}" + echo "https://rookeries.us-east-1.linodeobjects.com/${1}" } latest_stable_version () { local version_release_path - version_release_path="$(build_rookeries_path downloads/latest_stable_version)" + version_release_path="$(build_rookeries_path latest_stable_version)" local latest_stable_version latest_stable_version="$(curl -X GET "${version_release_path}" -sSf)" @@ -130,7 +130,7 @@ install () { bin_download_path="${DOWNLOAD_PATH}/${binary_to_fetch}" checksum_download_path="${DOWNLOAD_PATH}/${checksum_for_binary}" - rookeries_fetch_path="$(build_rookeries_path downloads/${rookeries_version})" + rookeries_fetch_path="$(build_rookeries_path ${rookeries_version})" if [[ ! -f "${bin_download_path}" ]] || [[ ! -f "${checksum_download_path}" ]] then diff --git a/src/cli.rs b/src/cli.rs index 244a2b7..9b1924d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,9 @@ +use crate::{current_version, files::get_rookeries_home_cache_dir}; use colored::Colorize; use env_logger::{Builder, Env, Target}; use log::{debug, error, info}; use serde::Deserialize; +use std::process::Command; pub const USAGE: &str = " @@ -73,7 +75,6 @@ pub fn header_message(message: &str) { pub fn success_message(message: &str) { info!(" {} {}", "✔".green(), message); } - pub fn caution_message(message: &str) { info!(" {} {}", "✶".yellow(), message); } @@ -88,3 +89,30 @@ pub fn error_message(error: Box) { } info!(""); } + +pub fn display_debug_info() { + debug!("Rookeries version: \t{}", current_version()); + debug!("Git commit: \t\t{}", env!("GIT_REVISION")); + + let os_info = Command::new("uname") + .args(&["-sr"]) + .output() + .and_then(|output| { + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).to_string()) + } else { + Ok(std::env::consts::OS.to_string()) + } + }) + .unwrap_or_else(|_| std::env::consts::OS.to_string()); + + debug!("Operating System: \t{}", os_info.trim()); + debug!("Architecture: \t\t{}", std::env::consts::ARCH); + + let cache_directory = match get_rookeries_home_cache_dir() { + Ok(path) => path.display().to_string(), + Err(err) => format!("{}", err), + }; + + debug!("Rookeries Home: \t{}", cache_directory); +} diff --git a/src/commands/build.rs b/src/commands/build.rs new file mode 100644 index 0000000..da8f7a9 --- /dev/null +++ b/src/commands/build.rs @@ -0,0 +1,314 @@ +use crate::migration::PreStable18Site; +use crate::{ + cli::{caution_message, header_message, success_message}, + errors::RookeriesError, + files::{build_path, copy_directory, copy_file, create_directory, FileType}, + Page, PageHeader, Project, Site, +}; +use colored::Colorize; +use serde_json::json; +use std::{ + ffi::OsString, + fs::{read_dir, read_to_string, remove_dir_all, write}, + path::Path, +}; +use uuid::Uuid; + +const ROOKERIES_UNKNOWN_PLACEHOLDER: &str = "???"; + +pub fn build_site(project: &Project, activate_dev_mode: bool) -> Result<(), RookeriesError> { + header_message("Building the site..."); + + // Check if project manifest is available. + if !project.site_manifest().exists() { + return Err(RookeriesError::MissingSiteToml); + } + + let project_manifest_details = read_to_string(project.site_manifest()).map_err(|err| { + RookeriesError::ReadFileFailure(err, "site.toml".to_string(), FileType::SiteToml) + })?; + + // TODO: Improve the migration workflow once requirements around templates is better understood. + let older_site: Option = match toml::from_str(&project_manifest_details) { + Err(_) => None, + Ok(site) => Some(site), + }; + + let project_site: Site = match older_site.clone() { + None => toml::from_str(&project_manifest_details) + .map_err(RookeriesError::SiteTomlDeserializationError)?, + Some(site) => site.migrate(), + }; + success_message(&format!( + "Found a project manifest for the \"{}\" site!", + &project_site.title.green(), + )); + + // Build a list of the pages to build out. + header_message("Searching for pages..."); + let mut site_source_pages: Vec = Vec::new(); + for entry in read_dir(project.root_dir())? { + // TODO: Make the search recursive through directories. + let path = entry?.path(); + if path.is_file() + && path.extension() != None + && path.extension().unwrap_or(&path.as_os_str()) == "md" + { + site_source_pages.push(path.into_os_string()); + } + } + site_source_pages.sort(); + for source in &site_source_pages { + let path = Path::new(source); + success_message(&format!("Found {}", path.display())); + } + + // TODO: Add in support for figuring out the index page or allowing one to be set. + header_message("Compiling page information..."); + + // TODO: Add warning about a missing index page. + // TODO: Add support for aliases (e.g. such as about being an index page) + let site_source_pages: Vec = site_source_pages + .iter() + .map(|source_page| { + // TODO: Extract separate per page build. + let source_path = Path::new(source_page); + + let slug = source_path + .file_stem() + .unwrap_or_else(|| source_path.as_os_str()) + .to_str() + .unwrap_or(ROOKERIES_UNKNOWN_PLACEHOLDER) + .to_string(); + + let raw_content = read_to_string(source_path) + .map_err(|err| { + let source_page_err = source_page + .clone() + .into_string() + .unwrap_or_else(|_| ROOKERIES_UNKNOWN_PLACEHOLDER.to_string()); + RookeriesError::ReadFileFailure(err, source_page_err, FileType::SourceMarkdown) + }) + .unwrap_or_else(|_| ROOKERIES_UNKNOWN_PLACEHOLDER.to_string()); + + // Extract the page header from the content. + let parsed_content: Vec<&str> = raw_content.split("+++").collect(); + let (header, content) = if parsed_content.len() > 1 { + (parsed_content[1], parsed_content[2]) + } else { + ("", parsed_content[parsed_content.len() - 1]) + }; + + // TODO: Remember to check valid titles post-migration. + let header: PageHeader = + toml::from_str(&header).unwrap_or(PageHeader::new(slug.clone())); + success_message(&format!("Compiling \"{}\" page", &slug.green())); + + Page { + title: header.title, + slug, + content: content.to_string(), + created_at: None, + } + }) + .collect(); + + // Create a build directory for the resulting files. + header_message("Preparing build directory..."); + let build_directory = project.build_dir(); + if build_directory.exists() { + caution_message("Recreating build directory..."); + remove_dir_all(&build_directory)?; + create_directory(&build_directory, FileType::BuildRoot)?; + } else { + create_directory(&build_directory, FileType::BuildRoot)?; + } + + header_message("Initialize page rendering system..."); + let mut render_engine = tera::Tera::default(); + // TODO: Add templates programmatically. + let template_path = project.template_dir().join("base_index.html"); + let another_template_path = project.template_dir().join("sample.html"); + render_engine + .add_template_files(vec![ + (&template_path, Some("index")), + (&another_template_path, Some("sample")), + ]) + .map_err(RookeriesError::TemplateSetupFailure)?; + + let status = json!({ + "version": env!("CARGO_PKG_VERSION").to_string(), + "app": env!("CARGO_PKG_NAME").to_string(), + "gitRevision": env!("GIT_REVISION").to_string(), + "contentBuildId": Uuid::new_v4(), + }); + + header_message("Creating the index page..."); + let mut ctx = tera::Context::new(); + // TODO: Figure out better support for Pre 0.18.0 version sites. + if older_site.is_some() { + let deprecation_message = format!( + "{} Using deprecated values: {} and {} in the templates.", + "WARNING!".yellow(), + "site.name".yellow(), + "plugins".yellow(), + ); + caution_message(&deprecation_message); + let deprecation_message = format!( + "{} Update these values to: {} and {} respectively in the templates.", + "RECOMMENDATION:".yellow(), + "site.title".yellow(), + "site.plugins".yellow(), + ); + caution_message(&deprecation_message); + ctx.insert("site", &older_site.unwrap()); + ctx.insert("plugins", &project_site.plugins) + } else { + ctx.insert("site", &project_site); + } + + // TODO: Ensure getting the right page as documented in the landingPage + ctx.insert("current_page", "index"); + ctx.insert("rookeries", &status); + if activate_dev_mode { + ctx.insert("dev_mode", "active"); + } + + // TODO: Add index pages into the site source pages. + let render_page_str = render_engine + .render("sample", &ctx) + .map_err(|err| RookeriesError::RenderTemplateFailure(err, "index".to_string()))?; + + write(build_directory.join("index.html"), render_page_str).map_err(|err| { + RookeriesError::WriteFileFailure(err, "index.html".to_string(), FileType::RenderedHtml) + })?; + success_message(&format!("Created the {} HTML page.", "index".green())); + + header_message("Creating the pages..."); + for page in &site_source_pages { + create_directory(&build_directory.join(&page.slug), FileType::Page)?; + ctx.insert("current_page", &page.slug); + let render_page_str = render_engine.render("index", &ctx).map_err(|err| { + RookeriesError::RenderTemplateFailure(err, format!("{}/index", &page.slug)) + })?; + write( + build_directory.join(&page.slug).join("index.html"), + render_page_str, + ) + .map_err(|err| { + RookeriesError::WriteFileFailure( + err, + format!("{}/index.html", &page.slug), + FileType::RenderedHtml, + ) + })?; + success_message(&format!("Created the {} HTML page.", &page.slug.green())); + } + + // Create the API representations. + header_message("Creating the API JSON files..."); + let api_path = &build_directory.join("api"); + create_directory(&api_path, FileType::APIRoot)?; + + // Create a build artifact. + let status = serde_json::to_string_pretty(&status).map_err(|err| { + RookeriesError::JsonSerializationError( + err, + "_build.json".to_string(), + FileType::BuildArtifactJson, + ) + })?; + write(api_path.join("_build.json"), status).map_err(|err| { + RookeriesError::WriteFileFailure( + err, + "api/_build.json".to_string(), + FileType::BuildArtifactJson, + ) + })?; + success_message(&format!( + "Created the {}.", + FileType::BuildArtifactJson.to_string().green() + )); + + let site_json = serde_json::to_string_pretty(&project_site).map_err(|err| { + RookeriesError::JsonSerializationError( + err, + "site.json".to_string(), + FileType::RenderedSiteJson, + ) + })?; + write(&api_path.join("site.json"), site_json).map_err(|err| { + RookeriesError::WriteFileFailure( + err, + "api/site.json".to_string(), + FileType::RenderedSiteJson, + ) + })?; + success_message(&format!( + "Created the {}.", + FileType::RenderedSiteJson.to_string().green() + )); + + // Create all the page API reps. + let api_pages_path = &api_path.join("pages"); + create_directory(&api_pages_path, FileType::PageAPI)?; + for page in site_source_pages { + let page_json_filename = format!("{}.json", &page.slug); + let page_json = serde_json::to_string_pretty(&page).map_err(|err| { + RookeriesError::JsonSerializationError( + err, + page_json_filename.clone(), + FileType::RenderedPageJson, + ) + })?; + + write(api_pages_path.join(&page_json_filename), page_json).map_err(|err| { + RookeriesError::WriteFileFailure(err, page_json_filename, FileType::RenderedPageJson) + })?; + success_message(&format!( + "Created the {} for the {} page.", + FileType::RenderedPageJson.to_string().green(), + &page.slug.green(), + )); + } + + // Bring in the static file directory. + // TODO: Ensure a separate copy over of JS assets from Rookeries. (from plugins and standalone compiled JS) + let assets_source_dir = + build_path(project.static_assets_dir().as_path(), FileType::StaticAsset)?; + + let assets_target_dir = Path::new(&build_directory).join("static"); + header_message("Copying static assets to built site..."); + copy_directory( + &assets_source_dir, + &assets_target_dir, + FileType::StaticAsset, + None, + )?; + success_message(&format!( + "Copied over {} directory!", + FileType::StaticAsset.to_string().green() + )); + + // Copy over plugins too! + // TODO: Copy over plugins by version. + header_message("Activate plugins..."); + if project_site.plugins.is_empty() { + success_message("No plugins to activate."); + } else { + let plugins_install_path = build_path(project.plugins_dir().as_path(), FileType::Plugin)?; + + let static_js_path = Path::new(&assets_target_dir).join("js"); + for plugin in project_site.plugins { + let plugin_file_name = format!("{}-plugin.js", plugin.name); + copy_file( + &plugins_install_path, + &static_js_path, + &plugin_file_name, + FileType::Plugin, + )?; + } + } + + Ok(()) +} diff --git a/src/commands/init.rs b/src/commands/init.rs new file mode 100644 index 0000000..ba36e45 --- /dev/null +++ b/src/commands/init.rs @@ -0,0 +1,173 @@ +use crate::{ + cli::{caution_message, header_message, success_message}, + current_version, + errors::RookeriesError, + files::{copy_directory, copy_file, create_directory, get_rookeries_home_cache_dir, FileType}, + template, Project, Site, +}; +use chrono::prelude::*; +use colored::Colorize; +use serde_json::{json, Value}; +use std::{collections::HashMap, fs::write}; + +/// Initializes the site. +/// +/// # Arguments +/// +/// * `project` - The project to base around the site. +/// +pub fn initialize_site(project: Project) -> Result<(), RookeriesError> { + header_message("Initializing a new site..."); + + // TODO: Add support for interactive wizard to populate values. + // TODO: Add support for .gitignore + git initialization. (Maybe post initialization plugins). + let project_initial_details: Site = Default::default(); + prepare_project_directory(&project)?; + + // Create a site toml if one is missing. + match &project.site_manifest().exists() { + true => caution_message("Found an existing site.toml. Skipping."), + false => create_site_manifest(&project_initial_details, &project)?, + } + + // Template copy over and setup + let rookeries_home_path = get_rookeries_home_cache_dir()?; + let template_path = &rookeries_home_path.join("template"); + if !template_path.exists() { + template::install_templates()?; + } + + header_message("Adding templates to site..."); + let site_template_path = project.template_dir(); + if site_template_path.exists() { + // TODO: Figure out override. + caution_message("Found existing templates. Skipping."); + } else { + copy_directory( + &template_path, + &site_template_path, + FileType::HtmlTemplate, + Some(vec!["ico", "md", "css", "js"]), + )?; + success_message(&format!( + "Copied over the {} directory!", + FileType::Template.to_string().green() + )); + } + + header_message("Adding template static assets to site..."); + let site_static_path = project.static_assets_dir(); + if site_static_path.exists() { + caution_message("Found existing static assets. Skipping."); + } else { + copy_directory( + &template_path, + &site_static_path, + FileType::StaticAsset, + Some(vec!["md", "html"]), + )?; + success_message(&format!( + "Copied over the {} directory!", + FileType::StaticAsset.to_string().green() + )); + } + + header_message("Adding sample markdown files to site..."); + let sample_pages = sample_pages(); + let mut ordered_pages: Vec<&String> = sample_pages.keys().collect(); + ordered_pages.sort(); + + for page in ordered_pages { + let page_file_name = format!("{}.md", &page); + let sample_page_path = project.root_dir().join(&page_file_name); + match &sample_page_path.exists() { + true => { + caution_message(&format!( + "Found existing {}. Skipping.", + &page_file_name.yellow() + )); + } + false => { + copy_file( + &template_path, + &project.root_dir(), + &page_file_name, + FileType::SampleMarkdown, + )?; + } + } + } + + let plugins_path = &rookeries_home_path.join("plugins"); + if !plugins_path.exists() { + template::install_plugins()?; + } + + // Plugin copy over + // TODO: Improve plugin setup... + header_message("Adding plugins to site..."); + let site_plugins_path = project.plugins_dir(); + if site_plugins_path.exists() { + // TODO: Figure out override. + caution_message("Found existing plugins. Skipping."); + } else { + create_directory(&site_plugins_path, FileType::Plugin)?; + for plugin in project_initial_details.plugins { + let plugin_file_name = format!("{}-plugin.js", &plugin.name); + copy_file( + &plugins_path, + &site_plugins_path, + &plugin_file_name, + FileType::Plugin, + )?; + success_message(&format!("Copied over the {} plugin!", &plugin.name.green())); + } + } + Ok(()) +} + +fn prepare_project_directory(project: &Project) -> Result<(), RookeriesError> { + // Build out the project directory. + let project_directory = project.root_dir(); + if !project_directory.exists() { + create_directory(&project_directory, FileType::ProjectRoot)?; + } + let project_dir = &project_directory.canonicalize()?; + let message = &format!( + "Created a new project in {}", + &project_dir.display().to_string().green() + ); + Ok(success_message(message)) +} + +fn create_site_manifest(site: &Site, project: &Project) -> Result<(), RookeriesError> { + let site_toml_content = + toml::to_string_pretty(&site).map_err(RookeriesError::SiteTomlSerializationError)?; + + // TODO: Consider a more complex template for the site. + let generation_timestamp: DateTime = Local::now(); + let generation_timestamp = generation_timestamp.format("%B %e, %Y @ %R").to_string(); + let site_toml_header = format!( + "# Site generated by Rookeries v{} on {}", + current_version(), + generation_timestamp + ); + + let site_toml = format!("{}\n\n{}", site_toml_header, site_toml_content); + + write(&project.site_manifest(), site_toml).map_err(|err| { + RookeriesError::WriteFileFailure(err, "site.toml".to_string(), FileType::SiteToml) + })?; + Ok(success_message("Created a site.toml!")) +} + +fn sample_pages() -> HashMap { + let mut sample_pages: HashMap = HashMap::new(); + sample_pages.insert("index".to_string(), json!({"title": "Welcome"})); + sample_pages.insert("demo".to_string(), json!({"title": "Demo"})); + sample_pages.insert( + "license".to_string(), + json!({"title": "License (Apache v2.0)"}), + ); + sample_pages +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..35b5e89 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,3 @@ +pub mod build; +pub mod init; +pub mod server; diff --git a/src/commands/server.rs b/src/commands/server.rs new file mode 100644 index 0000000..fdff544 --- /dev/null +++ b/src/commands/server.rs @@ -0,0 +1,136 @@ +//! # Server +//! +//! Manages running a local development server, to allow for seeing the results of building +//! a site in real-time. + +use crate::{ + cli::{caution_message, details_message, error_message, header_message, success_message}, + commands::build::build_site, + errors::RookeriesError, + Project, +}; +use actix_web::{middleware::Logger, App, HttpServer}; +use colored::Colorize; +use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; +use std::{net::SocketAddr, sync::mpsc::channel, thread::spawn, time::Duration}; + +/// The default port for running a local development server. +const DEFAULT_SERVER_PORT: u16 = 8080; + +pub fn serve_site( + project: Project, + server_port: u16, + open_browser: bool, +) -> Result<(), RookeriesError> { + header_message("Preparing to serve site..."); + + // Check if build directory is available. + let build_directory = project.build_dir(); + if !build_directory.exists() || !build_directory.is_dir() { + caution_message("This project needs a build first!"); + header_message("Attempting to build the site..."); + build_site(&project, true)?; + } + + spawn(move || watch(project.clone())); + success_message("Found a built site to serve!"); + + let server_addr = local_dev_server_addr(server_port); + + HttpServer::new(move || { + App::new() + .service(actix_files::Files::new("/", build_directory.clone()).index_file("index.html")) + .wrap(Logger::default()) + }) + .bind(server_addr) + .map_err(|err| RookeriesError::ServerPortBind(err, server_addr.port())) + .and_then(|res| { + success_message(&format!( + "Serving project at local port {}", + server_addr.port().to_string().green() + )); + if open_browser { + spawn(move || open_browser_tab(server_addr.port())); + } + Ok(res) + })? + .run()?; + Ok(()) +} + +/// Creates a socket address representation for the local development server. +fn local_dev_server_addr(port: u16) -> SocketAddr { + let port = match port { + x if x <= u16::min_value() || x >= u16::max_value() => DEFAULT_SERVER_PORT, + x => x, + }; + ([127, 0, 0, 1], port).into() +} + +fn open_browser_tab(port: u16) { + let url_to_open = format!("http://{}/", local_dev_server_addr(port)); + match opener::open(&url_to_open) + .map_err(|err| RookeriesError::OpenBrowserFailure(err, url_to_open.clone())) + { + Ok(_) => success_message(&format!( + "Opened {} in the default web browser.", + &url_to_open.green() + )), + Err(err) => error_message(err.into()), + }; +} + +fn watch(project: Project) -> notify::Result<()> { + let (tx, rx) = channel(); + + let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?; + + // Add watch to all source paths. + setup_watchers(&mut watcher, &project)?; + let build_dir = project.build_dir(); + + loop { + match rx.recv() { + // TODO: Add more fine-grained control to avoid a full rebuild for minor changes. + Ok(event) => { + details_message(&format!("{:?}", event)); + + let trigger_rebuild = match event { + DebouncedEvent::Rescan => false, + DebouncedEvent::Error(_, _) => false, + DebouncedEvent::Remove(path) => !path.starts_with(&build_dir), + DebouncedEvent::Create(path) => !path.starts_with(&build_dir), + DebouncedEvent::NoticeRemove(path) => !path.starts_with(&build_dir), + DebouncedEvent::NoticeWrite(path) => !path.starts_with(&build_dir), + _ => true, + }; + + if trigger_rebuild { + teardown_watchers(&mut watcher, &project)?; + details_message("Rebuild triggered!"); + if let Err(err) = build_site(&project, true) { + error_message(err.into()); + } + setup_watchers(&mut watcher, &project)?; + } + } + Err(e) => details_message(&format!("watch error: {:?}", e)), + } + } +} + +fn setup_watchers(watcher: &mut RecommendedWatcher, project: &Project) -> notify::Result<()> { + watcher.watch(project.root_dir(), RecursiveMode::NonRecursive)?; + watcher.watch(project.static_assets_dir(), RecursiveMode::Recursive)?; + watcher.watch(project.template_dir(), RecursiveMode::Recursive)?; + watcher.watch(project.plugins_dir(), RecursiveMode::NonRecursive)?; + Ok(()) +} + +fn teardown_watchers(watcher: &mut RecommendedWatcher, project: &Project) -> notify::Result<()> { + watcher.unwatch(project.root_dir())?; + watcher.unwatch(project.static_assets_dir())?; + watcher.unwatch(project.template_dir())?; + watcher.unwatch(project.plugins_dir())?; + Ok(()) +} diff --git a/src/errors.rs b/src/errors.rs index d0950c4..01820c9 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -2,6 +2,7 @@ use crate::{cli::error_message, files::FileType}; use std::{error, fmt, io}; use colored::Colorize; +use std::error::Error; #[derive(Debug)] pub enum RookeriesError { @@ -30,7 +31,7 @@ impl fmt::Display for RookeriesError { match *self { RookeriesError::GeneralIO(ref err) => { writeln!(f, "Encountered an unexpected I/O error.")?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::ServerPortBind(ref err, port) => { writeln!( @@ -38,7 +39,7 @@ impl fmt::Display for RookeriesError { "The server could not to bind to port \"{}\". Try using a different port.", port.to_string().red() )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::MissingSiteToml => writeln!( f, @@ -47,15 +48,15 @@ impl fmt::Display for RookeriesError { ), RookeriesError::CopyFileFailure(ref err, ref filename, ref file_type) => { writeln!(f, "Could not copy over {} {}.", file_type, filename.red())?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::WriteFileFailure(ref err, ref filename, ref file_type) => { writeln!(f, "Could not write {} {}.", file_type, filename.red())?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::ReadFileFailure(ref err, ref filename, ref file_type) => { writeln!(f, "Could not read {} {}.", file_type, filename.red())?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::CreateDirectoryFailure(ref err, ref directory_type) => { writeln!( @@ -63,7 +64,7 @@ impl fmt::Display for RookeriesError { "Could not create {} directory.", directory_type.to_string().red() )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::CopyDirectoryFailure( ref err, @@ -78,7 +79,7 @@ impl fmt::Display for RookeriesError { source_directory.red(), target_directory.red() )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::CopyDirectoryFailureMissingSource( ref source_directory, @@ -94,7 +95,7 @@ impl fmt::Display for RookeriesError { )?; write!( f, - "Exact cause: source directory {} is missing.", + "Cause: source directory {} is missing.", source_directory.red() ) } @@ -112,15 +113,16 @@ impl fmt::Display for RookeriesError { path.red() )?; } - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::TemplateSetupFailure(ref err) => { - writeln!(f, "Unable to setup site generation templates.")?; - write!(f, "Exact cause: {}", err) + writeln!(f, "Unable to set up site generation templates.")?; + write!(f, "Cause: {}", err) } RookeriesError::RenderTemplateFailure(ref err, ref page) => { writeln!(f, "Unable to render the {} page.", page.red())?; - write!(f, "\tExact cause: {}", err) + writeln!(f, "\tCause: {}", err)?; + write!(f, "\tSource: {}", err.source().unwrap()) } RookeriesError::JsonSerializationError(ref err, ref file_name, ref file_type) => { writeln!( @@ -129,15 +131,15 @@ impl fmt::Display for RookeriesError { file_type.to_string().red(), file_name.red(), )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::SiteTomlSerializationError(ref err) => { writeln!(f, "Could not serialize the site into TOML.")?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::SiteTomlDeserializationError(ref err) => { writeln!(f, "Could not deserialize the site from TOML.")?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::ExtractArchiveError(ref err, ref file_type) => { writeln!( @@ -145,7 +147,7 @@ impl fmt::Display for RookeriesError { "Unable to extract the {} installer archive.", file_type.to_string().red() )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } RookeriesError::UnableToAccessRookeriesHome => { write!(f, "Unable to access ROOKERIES_HOME.") @@ -156,7 +158,7 @@ impl fmt::Display for RookeriesError { "Failed to open the locally served site on {} in a browser.", desired_url.red(), )?; - write!(f, "Exact cause: {}", err) + write!(f, "Cause: {}", err) } } } diff --git a/src/files.rs b/src/files.rs index 41f97a0..6cbca71 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,5 +1,8 @@ -use crate::cli::{caution_message, details_message}; -use crate::{cli::success_message, errors::RookeriesError}; +use crate::{ + cli::{caution_message, details_message, success_message}, + current_version, + errors::RookeriesError, +}; use colored::Colorize; use directories::ProjectDirs; use std::{ @@ -25,7 +28,6 @@ pub enum FileType { RenderedPageJson, BuildArtifactJson, StaticAsset, - // TODO: This will probably go once the ROOKERIES_HOME code is removed. RookeriesHomeRoot, } @@ -201,5 +203,5 @@ pub fn build_path(built_path: &Path, file_type: FileType) -> Result Result { let rookeries_dirs = ProjectDirs::from("org", "Amber Penguin Software", "rookeries") .ok_or_else(|| RookeriesError::UnableToAccessRookeriesHome)?; - Ok(rookeries_dirs.data_dir().join(env!("CARGO_PKG_VERSION"))) + Ok(rookeries_dirs.data_dir().join(current_version())) } diff --git a/src/lib.rs b/src/lib.rs index 90e2397..1c7bd72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,89 @@ -use std::{collections::HashMap, default::Default}; - use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use serde_json::Value; +use std::{ + default::Default, + path::{Path, PathBuf}, +}; pub mod cli; +pub mod commands; pub mod errors; pub mod files; +pub mod migration; pub mod template; +/// Defines a project and its related assets. +#[derive(Debug, Clone)] +pub struct Project { + root: String, +} + +impl Project { + /// Creates a new project based on a root directory. + /// + /// # Arguments + /// + /// * `root` - A string slice of the relative path to the root location of the project. + /// + pub fn new(root: &str) -> Project { + Project { root: root.into() } + } + /// Gets a path to the root directory of the project. + pub fn root_dir(&self) -> PathBuf { + Path::new(&self.root).to_path_buf() + } + /// Gets a path to the project's build directory. This contains the built site. + pub fn build_dir(&self) -> PathBuf { + self.root_dir().join("build") + } + /// Gets a path to the project's directory of static assets. + pub fn static_assets_dir(&self) -> PathBuf { + self.root_dir().join("static") + } + /// Gets a path to the project's directory of templates. + pub fn template_dir(&self) -> PathBuf { + self.root_dir().join("template") + } + /// Gets a path to the directory of plugins connected to the project. + pub fn plugins_dir(&self) -> PathBuf { + self.root_dir().join("plugins") + } + /// Gets a path to the site manifest (site.toml) for the project. + pub fn site_manifest(&self) -> PathBuf { + self.root_dir().join("site.toml") + } +} + +/// Creates a project that uses the current working directory as the default root of the project. +impl Default for Project { + fn default() -> Project { + Project::new(".") + } +} + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Page { + // TODO: Make this present in the page as TOML header. pub slug: String, pub title: String, pub content: String, #[serde(rename = "created", skip_deserializing)] pub created_at: Option>, + // TODO: Add something like a skip option to not add a source markdown into the site. +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +/// Represents the optional TOML header in a Markdown page. +pub struct PageHeader { + pub title: String, +} + +impl PageHeader { + pub fn new(slug: String) -> Self { + PageHeader { + title: format!("Rookeries :: {}", &slug), + } + } } impl Default for Page { @@ -30,18 +98,42 @@ impl Default for Page { } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Site { +/// Represents a plugin attached to a site. +pub struct Plugin { pub name: String, + pub version: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +/// Represents the details of the managed site. +pub struct Site { + pub title: String, pub index: Option, - pub pages: Option>, + pub version: String, + pub plugins: Vec, +} + +impl Site { + pub fn new(title: String, index: Option, version: String) -> Self { + Site { + title: title.clone(), + index: index.clone(), + plugins: Vec::new(), + version, + } + } } impl Default for Site { fn default() -> Site { - Site { - name: Default::default(), - index: Default::default(), - pages: Some(HashMap::new()), - } + Site::new( + "New Rookeries Site".to_string(), + Default::default(), + current_version(), + ) } } + +pub fn current_version() -> String { + env!("CARGO_PKG_VERSION").to_string() +} diff --git a/src/main.rs b/src/main.rs index 8461ca8..51f889c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,71 +1,19 @@ -use actix_web::{middleware::Logger, App, HttpServer}; -use chrono::prelude::*; use colored::Colorize; use docopt::Docopt; -use log::{debug, info}; -use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; +use log::info; use rookeries::{ - cli::{ - caution_message, details_message, error_message, header_message, initialize_logging, - success_message, Args, USAGE, - }, - errors::{exit_on_fatal_error, RookeriesError}, - files::{ - build_path, copy_directory, copy_file, create_directory, get_rookeries_home_cache_dir, - FileType, - }, - template, Page, Site, + cli::{display_debug_info, header_message, initialize_logging, Args, USAGE}, + commands::{build::build_site, init::initialize_site, server::serve_site}, + current_version, + errors::exit_on_fatal_error, + Project, }; -use serde_json::{json, Value}; -use std::{ - collections::HashMap, - ffi::OsString, - fs::{read_dir, read_to_string, remove_dir_all, write}, - path::{Path, PathBuf}, - process::Command, - sync::mpsc::channel, - thread::spawn, - time::Duration, -}; -use uuid::Uuid; -const ROOKERIES_UNKNOWN_PLACEHOLDER: &str = "???"; - -fn get_project_directory(args: &Args) -> std::result::Result { - let project_directory = match &args.arg_project { - None => String::from("."), - Some(directory) => directory.clone(), - }; - - let project_directory = Path::new(&project_directory); - build_path(&project_directory, FileType::ProjectRoot) -} - -fn display_debug_info() { - debug!("Rookeries version: \t{}", env!("CARGO_PKG_VERSION")); - debug!("Git commit: \t\t{}", env!("GIT_REVISION")); - - let os_info = Command::new("uname") - .args(&["-sr"]) - .output() - .and_then(|output| { - if output.status.success() { - Ok(String::from_utf8_lossy(&output.stdout).to_string()) - } else { - Ok(std::env::consts::OS.to_string()) - } - }) - .unwrap_or_else(|_| std::env::consts::OS.to_string()); - - debug!("Operating System: \t{}", os_info.trim()); - debug!("Architecture: \t\t{}", std::env::consts::ARCH); - - let cache_directory = match get_rookeries_home_cache_dir() { - Ok(path) => path.display().to_string(), - Err(err) => format!("{}", err), - }; - - debug!("Rookeries Home: \t{}", cache_directory); +fn get_project(args: &Args) -> Project { + match &args.arg_project { + None => Default::default(), + Some(directory) => Project::new(directory), + } } fn main() { @@ -82,26 +30,29 @@ fn main() { if args.flag_version { info!("{}", get_app_version()); - std::process::exit(0); + return; } if args.cmd_serve { - serve_site(&args).unwrap_or_else(|err| exit_on_fatal_error(err.into())); + let project = get_project(&args); + serve_site(project, args.flag_port, args.flag_open) + .unwrap_or_else(|err| exit_on_fatal_error(err.into())); } if args.cmd_build { - let project_directory = get_project_directory(&args).unwrap_or_default(); - match build_site(project_directory, false) { + let project = get_project(&args); + match build_site(&project, false) { Err(err) => exit_on_fatal_error(err.into()), Ok(_) => header_message(&format!("Site built. Happy hacking! {}", "✔".green())), } } if args.cmd_init { - match initialize_site(&args) { + let project = get_project(&args); + match initialize_site(project) { Err(err) => exit_on_fatal_error(err.into()), Ok(_) => header_message(&format!( - "Site initialized. You can know build and serve the site. Happy hacking! {}", + "Site initialized. You can now start building your new site. Happy hacking! {}", "✔".green() )), } @@ -110,580 +61,10 @@ fn main() { info!(""); } -type Result = std::result::Result<(), RookeriesError>; - -fn serve_site(args: &Args) -> Result { - header_message("Preparing to serve site..."); - - let project_directory = get_project_directory(&args)?; - - // Check if build directory is available. - let build_directory = project_directory.clone().join("build"); - if !build_directory.exists() || !build_directory.is_dir() { - caution_message("This project needs a build first!"); - header_message("Attempting to build the site..."); - build_site(project_directory.clone(), true)?; - } - - let watched_folder = project_directory - .canonicalize() - .unwrap_or_default() - .to_str() - .unwrap_or_default() - .to_string(); - spawn(move || watch(watched_folder)); - - success_message("Found a built site to serve!"); - - let port = match args.flag_port { - x if x <= u16::min_value() || x >= u16::max_value() => 8000, - x => x, - }; - - HttpServer::new(move || { - App::new() - .service(actix_files::Files::new("/", build_directory.clone()).index_file("index.html")) - .wrap(Logger::default()) - }) - .bind(format!("127.0.0.1:{}", port)) - .map_err(|err| RookeriesError::ServerPortBind(err, port)) - .and_then(|res| { - success_message(&format!( - "Serving project at local port {}", - port.to_string().green() - )); - if args.flag_open { - spawn(move || open_browser_tab(port)); - } - Ok(res) - })? - .run()?; - Ok(()) -} - -fn open_browser_tab(port: u16) { - let url_to_open = format!("http://localhost:{}/", port); - match opener::open(&url_to_open) - .map_err(|err| RookeriesError::OpenBrowserFailure(err, url_to_open.clone())) - { - Ok(_) => success_message(&format!( - "Opened {} in the default web browser.", - &url_to_open.green() - )), - Err(err) => error_message(err.into()), - }; -} - -fn watch(watch_path: String) -> notify::Result<()> { - let (tx, rx) = channel(); - - let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?; - - // Add watch to all source paths. - let project_root_dir = Path::new(&watch_path); - setup_watchers(&mut watcher, project_root_dir)?; - - let build_dir = project_root_dir.join("build"); - - loop { - match rx.recv() { - // TODO: Add more fine-grained control to avoid a full rebuild for minor changes. - Ok(event) => { - details_message(&format!("{:?}", event)); - - let trigger_rebuild = match event { - DebouncedEvent::Rescan => false, - DebouncedEvent::Error(_, _) => false, - DebouncedEvent::Remove(path) => !path.starts_with(&build_dir), - DebouncedEvent::Create(path) => !path.starts_with(&build_dir), - DebouncedEvent::NoticeRemove(path) => !path.starts_with(&build_dir), - DebouncedEvent::NoticeWrite(path) => !path.starts_with(&build_dir), - _ => true, - }; - - if trigger_rebuild { - teardown_watchers(&mut watcher, &project_root_dir)?; - details_message("Rebuild triggered!"); - if let Err(err) = build_site(project_root_dir.to_path_buf(), true) { - error_message(err.into()); - } - setup_watchers(&mut watcher, &project_root_dir)?; - } - } - Err(e) => details_message(&format!("watch error: {:?}", e)), - } - } -} - -fn setup_watchers(watcher: &mut RecommendedWatcher, project_root_dir: &Path) -> notify::Result<()> { - watcher.watch(project_root_dir, RecursiveMode::NonRecursive)?; - - let static_assets_dir = project_root_dir.join("static"); - watcher.watch(static_assets_dir, RecursiveMode::Recursive)?; - - let template_dir = project_root_dir.join("template"); - watcher.watch(template_dir, RecursiveMode::Recursive)?; - - let plugins_dir = project_root_dir.join("plugins"); - watcher.watch(plugins_dir, RecursiveMode::NonRecursive)?; - Ok(()) -} - -fn teardown_watchers( - watcher: &mut RecommendedWatcher, - project_root_dir: &Path, -) -> notify::Result<()> { - watcher.unwatch(project_root_dir)?; - - let static_assets_dir = project_root_dir.join("static"); - watcher.unwatch(static_assets_dir)?; - - let template_dir = project_root_dir.join("template"); - watcher.unwatch(template_dir)?; - - let plugins_dir = project_root_dir.join("plugins"); - watcher.unwatch(plugins_dir)?; - Ok(()) -} - -fn build_site(project_directory: PathBuf, activate_dev_mode: bool) -> Result { - header_message("Building the site..."); - - // Check if project manifest is available. - let project_manifest = project_directory.clone().join("site.toml"); - if !project_manifest.exists() { - return Err(RookeriesError::MissingSiteToml); - } - - let project_manifest_details = read_to_string(project_manifest).map_err(|err| { - RookeriesError::ReadFileFailure(err, "site.toml".to_string(), FileType::SiteToml) - })?; - let project_site: Site = toml::from_str(&project_manifest_details) - .map_err(RookeriesError::SiteTomlDeserializationError)?; - success_message(&format!( - "Found a project manifest for the \"{}\" site!", - &project_site.name.green(), - )); - - // Build a list of the pages to build out. - header_message("Searching for pages..."); - let mut site_source_pages: Vec = Vec::new(); - for entry in read_dir(&project_directory.as_path())? { - // TODO: Make the search recursive through directories. - let path = entry?.path(); - if path.is_file() - && path.extension() != None - && path.extension().unwrap_or(&path.as_os_str()) == "md" - { - success_message(&format!("Found {}", path.display())); - site_source_pages.push(path.into_os_string()); - } - } - - // TODO: Add in support for figuring out the index page or allowing one to be set. - header_message("Compiling page information..."); - let page_overrides = project_site.clone().pages.unwrap_or_default(); - - // TODO: Add warning about a missing index page. - // TODO: Add support for aliases (e.g. such as about being an index page) - let site_source_pages: Vec = site_source_pages - .iter() - .map(|source_page| { - // TODO: Extract separate per page build. - let source_path = Path::new(source_page); - - let slug = source_path - .file_stem() - .unwrap_or_else(|| source_path.as_os_str()) - .to_str() - .unwrap_or(ROOKERIES_UNKNOWN_PLACEHOLDER) - .to_string(); - - let content = read_to_string(source_path) - .map_err(|err| { - let source_page_err = source_page - .clone() - .into_string() - .unwrap_or_else(|_| ROOKERIES_UNKNOWN_PLACEHOLDER.to_string()); - RookeriesError::ReadFileFailure(err, source_page_err, FileType::SourceMarkdown) - }) - .unwrap_or_else(|_| ROOKERIES_UNKNOWN_PLACEHOLDER.to_string()); - - // TODO: Be able to extract the title from the header of the markdown content. - let title = match &page_overrides.get(&slug) { - Some(page_entry) => match page_entry.get("title") { - Some(title_) => { - let title_str = serde_json::to_string(title_) - .unwrap_or_else(|_| ROOKERIES_UNKNOWN_PLACEHOLDER.to_string()); - title_str.replacen("\"", "", 2) - } - None => format!("Rookeries :: {}", &slug), - }, - None => format!("Rookeries :: {}", &slug), - }; - - success_message(&format!("Compiling \"{}\" page", &slug.green())); - - Page { - title, - slug, - content, - created_at: None, - } - }) - .collect(); - - // Create a build directory for the resulting files. - header_message("Preparing build directory..."); - let build_directory = project_directory.clone().join("build"); - if build_directory.exists() { - caution_message("Recreating build directory..."); - remove_dir_all(&build_directory)?; - create_directory(&build_directory, FileType::BuildRoot)?; - } else { - create_directory(&build_directory, FileType::BuildRoot)?; - } - - header_message("Initialize page rendering system..."); - let mut render_engine = tera::Tera::default(); - let template_path = &project_directory.join("template/base_index.html"); - let another_template_path = &project_directory.join("template/sample.html"); - render_engine - .add_template_files(vec![ - (&template_path, Some("index")), - (&another_template_path, Some("sample")), - ]) - .map_err(RookeriesError::TemplateSetupFailure)?; - - let status = json!({ - "version": env!("CARGO_PKG_VERSION").to_string(), - "app": env!("CARGO_PKG_NAME").to_string(), - "gitRevision": env!("GIT_REVISION").to_string(), - "contentBuildId": Uuid::new_v4(), - }); - - header_message("Creating the index page..."); - let plugin_list = vec!["hello-world", "dark-mode-switch"]; - let mut ctx = tera::Context::new(); - ctx.insert("site", &project_site); - // TODO: Ensure getting the right page as documented in the landingPage - ctx.insert("current_page", "index"); - ctx.insert("plugins", &plugin_list); - ctx.insert("rookeries", &status); - if activate_dev_mode { - ctx.insert("dev_mode", "active"); - } - - // TODO: Add index pages into the site source pages. - let render_page_str = render_engine - .render("sample", &ctx) - .map_err(|err| RookeriesError::RenderTemplateFailure(err, "index".to_string()))?; - - write(build_directory.join("index.html"), render_page_str).map_err(|err| { - RookeriesError::WriteFileFailure(err, "index.html".to_string(), FileType::RenderedHtml) - })?; - success_message(&format!("Created the {} HTML page.", "index".green())); - - header_message("Creating the pages..."); - for page in &site_source_pages { - create_directory(&build_directory.join(&page.slug), FileType::Page)?; - ctx.insert("current_page", &page.slug); - let render_page_str = render_engine.render("index", &ctx).map_err(|err| { - RookeriesError::RenderTemplateFailure(err, format!("{}/index", &page.slug)) - })?; - write( - build_directory.join(&page.slug).join("index.html"), - render_page_str, - ) - .map_err(|err| { - RookeriesError::WriteFileFailure( - err, - format!("{}/index.html", &page.slug), - FileType::RenderedHtml, - ) - })?; - success_message(&format!("Created the {} HTML page.", &page.slug.green())); - } - - // Create the API representations. - header_message("Creating the API JSON files..."); - let api_path = &build_directory.join("api"); - create_directory(&api_path, FileType::APIRoot)?; - - // Create a build artifact. - let status = serde_json::to_string_pretty(&status).map_err(|err| { - RookeriesError::JsonSerializationError( - err, - "_build.json".to_string(), - FileType::BuildArtifactJson, - ) - })?; - write(api_path.join("_build.json"), status).map_err(|err| { - RookeriesError::WriteFileFailure( - err, - "api/_build.json".to_string(), - FileType::BuildArtifactJson, - ) - })?; - success_message(&format!( - "Created the {}.", - FileType::BuildArtifactJson.to_string().green() - )); - - let site_json = serde_json::to_string_pretty(&project_site).map_err(|err| { - RookeriesError::JsonSerializationError( - err, - "site.json".to_string(), - FileType::RenderedSiteJson, - ) - })?; - write(&api_path.join("site.json"), site_json).map_err(|err| { - RookeriesError::WriteFileFailure( - err, - "api/site.json".to_string(), - FileType::RenderedSiteJson, - ) - })?; - success_message(&format!( - "Created the {}.", - FileType::RenderedSiteJson.to_string().green() - )); - - // Create all the page API reps. - let api_pages_path = &api_path.join("pages"); - create_directory(&api_pages_path, FileType::PageAPI)?; - for page in site_source_pages { - let page_json_filename = format!("{}.json", &page.slug); - let page_json = serde_json::to_string_pretty(&page).map_err(|err| { - RookeriesError::JsonSerializationError( - err, - page_json_filename.clone(), - FileType::RenderedPageJson, - ) - })?; - - write(api_pages_path.join(&page_json_filename), page_json).map_err(|err| { - RookeriesError::WriteFileFailure(err, page_json_filename, FileType::RenderedPageJson) - })?; - success_message(&format!( - "Created the {} for the {} page.", - FileType::RenderedPageJson.to_string().green(), - &page.slug.green(), - )); - } - - // Bring in the static file directory. - // TODO: Ensure a separate copy over of JS assets from Rookeries. (from plugins and standalone compiled JS) - let assets_source_dir = build_path( - &Path::new(&project_directory).join("static"), - FileType::StaticAsset, - )?; - - let assets_target_dir = Path::new(&build_directory).join("static"); - header_message("Copying static assets to built site..."); - copy_directory( - &assets_source_dir, - &assets_target_dir, - FileType::StaticAsset, - None, - )?; - success_message(&format!( - "Copied over {} directory!", - FileType::StaticAsset.to_string().green() - )); - - // Copy over plugins too! - header_message("Activate plugins..."); - // TODO: Copy over only active and available markdown plugins. - let plugin_list = vec!["hello-world", "dark-mode-switch"]; - let plugins_install_path = build_path( - &Path::new(&project_directory).join("plugins"), - FileType::Plugin, - )?; - - let static_js_path = Path::new(&assets_target_dir).join("js"); - for plugin in plugin_list { - let plugin_file_name = format!("{}-plugin.js", plugin); - copy_file( - &plugins_install_path, - &static_js_path, - &plugin_file_name, - FileType::Plugin, - )?; - } - - Ok(()) -} - -fn initialize_site(args: &Args) -> Result { - header_message("Initializing a new site..."); - - // TODO: Add support for an override flag. - // Build out the project directory. - let project_directory = match &args.arg_project { - None => String::from("."), - Some(directory) => directory.clone(), - }; - - let project_directory = Path::new(&project_directory); - if !project_directory.exists() { - create_directory(&project_directory.to_path_buf(), FileType::ProjectRoot)?; - let project_dir = &project_directory.canonicalize()?; - success_message(&format!( - "Created a new project in {}", - &project_dir.display().to_string().green() - )); - } - let project_directory = build_path(&project_directory, FileType::ProjectRoot)?; - - // TODO: Make the sample pages happen. - let mut sample_pages: HashMap = HashMap::new(); - sample_pages.insert("index".to_string(), json!({"title": "Welcome"})); - sample_pages.insert("demo".to_string(), json!({"title": "Demo"})); - sample_pages.insert( - "license".to_string(), - json!({"title": "License (Apache v2.0)"}), - ); - - // Create a site toml if one is missing. - let project_manifest = project_directory.join("site.toml"); - match &project_manifest.exists() { - true => { - caution_message("Found existing site.toml. Skipping."); - } - false => { - // TODO: Support an interactive setup of the Rookeries site. - let mut site = Site::default(); - site.name = "New Rookeries Site".to_string(); - site.pages = Some(sample_pages.clone()); - - let site_toml_content = - toml::to_string(&site).map_err(RookeriesError::SiteTomlSerializationError)?; - - // TODO: Consider a more complex template for the site. - let rookeries_version = env!("CARGO_PKG_VERSION"); - let generation_timestamp: DateTime = Local::now(); - let generation_timestamp = generation_timestamp.format("%B %e, %Y @ %R").to_string(); - let site_toml_header = format!( - "# Site generated by Rookeries v{} on {}", - rookeries_version, generation_timestamp - ); - - let site_toml = format!("{}\n\n{}", site_toml_header, site_toml_content); - - write(&project_manifest, site_toml).map_err(|err| { - RookeriesError::WriteFileFailure(err, "site.toml".to_string(), FileType::SiteToml) - })?; - success_message("Created a site.toml!"); - } - } - - // Create a Rookeries home directory if it doesn't exist. - let rookeries_home_path = get_rookeries_home_cache_dir()?; - if !rookeries_home_path.exists() { - header_message("Create local Rookeries home cache..."); - create_directory(&rookeries_home_path, FileType::RookeriesHomeRoot)?; - } - - // Template copy over and setup - let template_path = &rookeries_home_path.join("template"); - if !template_path.exists() { - template::install_templates()?; - } - - header_message("Adding templates to site..."); - - let site_template_path = &project_directory.join("template"); - if site_template_path.exists() { - // TODO: Figure out override. - caution_message("Found existing templates. Skipping."); - } else { - copy_directory( - &template_path, - &site_template_path, - FileType::HtmlTemplate, - Some(vec!["md", "css", "js"]), - )?; - success_message(&format!( - "Copied over the {} directory!", - FileType::Template.to_string().green() - )); - } - - header_message("Adding template static assets to site..."); - let site_static_path = &project_directory.join("static"); - if site_static_path.exists() { - caution_message("Found existing static assets. Skipping."); - } else { - copy_directory( - &template_path, - &site_static_path, - FileType::StaticAsset, - Some(vec!["md", "html"]), - )?; - success_message(&format!( - "Copied over the {} directory!", - FileType::StaticAsset.to_string().green() - )); - } - - header_message("Adding sample markdown files to site..."); - let mut ordered_pages: Vec<&String> = sample_pages.keys().collect(); - ordered_pages.sort(); - - for page in ordered_pages { - let page_file_name = format!("{}.md", &page); - let sample_page_path = project_directory.join(&page_file_name); - match &sample_page_path.exists() { - true => { - caution_message(&format!( - "Found existing {}. Skipping.", - &page_file_name.yellow() - )); - } - false => { - copy_file( - &template_path, - &project_directory, - &page_file_name, - FileType::SampleMarkdown, - )?; - } - } - } - - let plugins_path = &rookeries_home_path.join("plugins"); - if !plugins_path.exists() { - template::install_plugins()?; - } - - // Plugin copy over - header_message("Adding plugins to site..."); - let site_plugins_path = &project_directory.join("plugins"); - if site_plugins_path.exists() { - // TODO: Figure out override. - caution_message("Found existing plugins. Skipping."); - } else { - create_directory(&site_plugins_path, FileType::Plugin)?; - let plugin_list = vec!["hello-world", "dark-mode-switch"]; - for plugin in plugin_list { - let plugin_file_name = format!("{}-plugin.js", &plugin); - copy_file( - &plugins_path, - &site_plugins_path, - &plugin_file_name, - FileType::Plugin, - )?; - success_message(&format!("Copied over the {} plugin!", &plugin.green())); - } - } - Ok(()) -} - fn get_app_version() -> String { format!( "rookeries v{version} © 2013-2020 {authors}", - version = env!("CARGO_PKG_VERSION"), + version = current_version(), authors = env!("CARGO_PKG_AUTHORS"), ) } diff --git a/src/migration.rs b/src/migration.rs new file mode 100644 index 0000000..70f4cab --- /dev/null +++ b/src/migration.rs @@ -0,0 +1,96 @@ +//! # Migration +//! +//! Migrates older versions of Rookeries site to newer site formats. +use crate::{cli::caution_message, current_version, Plugin, Site}; +use colored::Colorize; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Clone, Debug)] +/// The format of a site for version 0.17.0 and earlier of Rookeries. +pub struct PreStable18Site { + name: String, + index: Option, + pages: Option>, +} + +impl PreStable18Site { + /// List of the default plugins for a pre-0.18.0 site. + fn default_plugins() -> Vec { + vec![ + Plugin { + name: "hello-world".to_string(), + version: "0.3.0".to_string(), + }, + Plugin { + name: "dark-mode-switch".to_string(), + version: "0.1.0".to_string(), + }, + ] + } + + /// Migrates from a pre 0.18.0 version of the site to the new format. + pub fn migrate(&self) -> Site { + // TODO: See how to update the templates: template/base_index.html & template/sample.html to site.title instead of site.name. + // TODO: Also migrate {% for plugin in site.plugins %} + // + let migration_message = format!( + "{} You are using a site using a deprecated site format and template created before {}!", + "WARNING!".yellow(), + "0.18.0".yellow(), + ); + caution_message(&migration_message); + + let pages_for_migration = match &self.pages { + Some(pages) => pages.clone(), + None => HashMap::new(), + }; + for (page, page_details) in pages_for_migration.iter() { + match page_details.get("title") { + Some(title) => { + let formatted_title = format!("\n\n+++\ntitle = {}\n+++\n", title); + let page_message = format!( + "{} Add the following to the {} page as Markdown frontend matter. {}", + "RECOMMENDATION:".yellow(), + format!("{}.md", page).yellow(), + formatted_title.cyan(), + ); + caution_message(&page_message); + } + None => continue, + } + } + + let plugins = PreStable18Site::default_plugins(); + let plugin_message: &str = &plugins + .iter() + .map(|plugin| format!("{}: v{}", plugin.name, plugin.version)) + .collect::>() + .join(", "); + let migration_message = format!( + "{} Sites created before {} use default plugins: [{}]!", + "WARNING!".yellow(), + current_version().yellow(), + plugin_message.yellow(), + ); + caution_message(&migration_message); + + let site = Site { + title: self.name.clone(), + index: self.index.clone(), + version: "0.17.0".to_string(), + plugins, + }; + + let updated_site_message = format!( + "{} Update {} to the the new format: \n\n{}\n", + "RECOMMENDATION:".yellow(), + "site.toml".yellow(), + toml::to_string_pretty(&site).unwrap_or_default().cyan(), + ); + caution_message(&updated_site_message); + + site + } +} diff --git a/src/template.rs b/src/template.rs index 82fcc27..82dffda 100644 --- a/src/template.rs +++ b/src/template.rs @@ -2,7 +2,7 @@ use crate::files::FileType; use crate::{ cli::{caution_message, details_message, header_message, success_message}, errors::RookeriesError, - files::get_rookeries_home_cache_dir, + files::{create_directory, get_rookeries_home_cache_dir}, }; use colored::Colorize; use std::fs; @@ -28,6 +28,13 @@ fn extract_and_install_archive( file_type: FileType, ) -> Result<(), RookeriesError> { let rookeries_home_cache = get_rookeries_home_cache_dir()?; + + // Create a Rookeries home directory if it doesn't exist. + if !rookeries_home_cache.exists() { + header_message("Create local Rookeries home cache..."); + create_directory(&rookeries_home_cache, FileType::RookeriesHomeRoot)?; + } + let reader = std::io::Cursor::new(installer_archive); let mut zip = zip::ZipArchive::new(reader) .map_err(|err| RookeriesError::ExtractArchiveError(err, file_type))?; diff --git a/template/base_index.html b/template/base_index.html index 19dd91a..70e8275 100644 --- a/template/base_index.html +++ b/template/base_index.html @@ -1,7 +1,7 @@ - {{ site.name }} :: {{ current_page }} + {{ site.title }} :: {{ current_page }} Demo License - +