diff --git a/.hooks/pre-commit b/.hooks/pre-commit index 709fb95..6947bc7 100755 --- a/.hooks/pre-commit +++ b/.hooks/pre-commit @@ -1,7 +1,7 @@ #!/usr/bin/env bash # This hook lints the code, and if we're on develop or master, also forces the tests to pass. -make lint &> /dev/null 2>&1 || { +./fejctl lint &> /dev/null 2>&1 || { >&2 echo "Format check failed, use 'make lint' for more information."; exit 1; } diff --git a/Cargo.toml b/Cargo.toml index f359a51..46b01a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Jef Roosens "] edition = "2018" [lib] -name = "fej_lib" +name = "fej" src = "src/lib.rs" test = true bench = true @@ -13,8 +13,8 @@ doc = true doctest = true [[bin]] -name = "fej" -src = "src/main.rs" +name = "server" +src = "src/bin/server/main.rs" test = false bench = false doc = false diff --git a/Makefile b/Makefile deleted file mode 100644 index 5d8c22e..0000000 --- a/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -all: debug -.PHONY: all - -# Builds the debug release inside the Alpine container. For build caching, two -# volumes are used named `fej_build-cache` and `fej_registry-cache`. These -# images are automatically created for you if they don't exist. If you -# encounter any strange build errors, you can try removing this volumes to -# start a completely fresh build. -debug: - @ ./build -m dev -a run build -.PHONY: debug - -# Builds the release version. In contrary to the debug version, this build -# doesn't use volumes for caching, as this would require the build to happen -# during runtime instead of during the building of the image. Instead, it uses -# the new `--mount` feature from Buildkit. This does mean that only very recent -# Docker engines can build the release version (in my case, at the time of -# writing this, 20.10.5). -release: - @ ./build -m rel -.PHONY: release - -# This builds the release version, and pushes all relevant tags to my Docker -# Hub repository, namely chewingbever/fej -push: - @ ./build -m rel -a push -.PHONY: push - -# This builds the debug release, and runs it detached. The reason we detach the -# container is because Rocket has a tendency to ignore ctlr-c when inside a -# container, which gets annoying really fast. -run: - @ ./build -m dev -a run -.PHONY: run - -# As a workaround, we just have a stop command that stops the container. -stop: - @ docker stop -t 2 fej - @ docker stop fej_db -.PHONY: stop - -# This attaches to the running container, essentially giving the same result as -# just running `cargo run` locally. -logs: - @ docker logs -f fej -.PHONY: logs - -# This just starts up a shell inside the fej container -sh: - @ docker exec -it fej sh -.PHONY: sh - -# Starts a psql session in the database container -dbsh: - @ docker exec -it fej_db psql -U fej -d fej - -# Builds the debug version, and runs the tests (but doesn't detach). -test: - @ ./build -m dev -a run -l -- test --no-fail-fast -.PHONY: test - -# Runs the cargo code formatter on your code. -format: - @ cargo fmt -.PHONY: format - -# Lints your code. This also gets run in the pre-commit hook, basically -# preventing you from committing badly-formatted code. -lint: - @ cargo fmt -- --check -.PHONY: lint - -# This builds the documentation for the project, excluding the documentation. -docs: - @ cargo doc --no-deps -.PHONY: docs diff --git a/build b/build deleted file mode 100755 index 271a3d9..0000000 --- a/build +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash - -image="chewingbever/fej" -# Should be either dev or rel -mode="dev" -action="" -attach="--detach" - -while getopts ":i:m:a:l" c; do - case $c in - i ) image="$OPTARG" ;; - m ) mode="$OPTARG" ;; - a ) action="$OPTARG" ;; - l ) attach="" ;; - ? ) exit 1 ;; - esac -done -shift $((OPTIND-1)) - -# Extract current version from Cargo.toml & get current branch -patch_version=`grep -Po '(?<=version = ").*(?=")' Cargo.toml | head -n1` -major_version=`echo "$patch_version" | sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1/'` -minor_version=`echo "$patch_version" | sed -E 's/([0-9]+).([0-9]+).([0-9]+)/\1.\2/'` -branch=`git rev-parse --abbrev-ref HEAD` - -if [[ "$branch" = "master" ]]; then - tags=("$patch_version" "$minor_version" "$major_version" "latest") - -elif [[ "$branch" = "develop" ]]; then - tags=("$patch_version-dev" "$minor_version-dev" "$major_version-dev" "dev") - -else - tags=("$branch") - -fi - -# First, we build the builder -DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.builder -t "$image-builder:latest" . - -# Run the actual build command -if [ "$mode" = "rel" ]; then - DOCKER_BUILDKIT=1 docker build -t "$image:$tags" -f docker/Dockerfile.rel . - -elif [[ "$mode" = "dev" ]]; then - DOCKER_BUILDKIT=1 docker build -t "$image-dev:$tags" -f docker/Dockerfile.dev . - -else - >&2 echo "Invalid mode." - exit 1 - -fi - -if [[ "$action" = push ]]; then - [[ "$branch" =~ ^develop|master$ ]] || { - >&2 echo "You can only push from develop or master." - exit 2 - } - - [[ "$mode" = "rel" ]] || { - >&2 echo "You can only push release builds." - exit 3 - } - - for tag in "${tags[@]}"; do - # Create the tag - docker tag "$image:$tags" "$image:$tag" - - # Push the tag - docker push "$image:$tag" - - # Remove the tag again, if it's not the main tag - [[ "$tag" != "$tags" ]] && docker rmi "$image:$tag" - done - -elif [[ "$action" = run ]]; then - # Create the network & start the database container - docker network create fej - docker volume create fej_db-data - - docker run --rm \ - --detach \ - --name fej_db \ - --network fej \ - -p 5432:5432 \ - -e "POSTGRES_DB=fej" \ - -e "POSTGRES_USER=fej" \ - -e "POSTGRES_PASSWORD=fej" \ - -v 'fej_db-data:/var/lib/postgresql/data' \ - postgres:13-alpine - - if [[ "$mode" = "dev" ]]; then - # Create caching volumes if needed (they need to be named) - docker volume create fej_build-cache - docker volume create fej_registry-cache - - flags="-v fej_build-cache:/usr/src/app/target -v fej_registry-cache:/root/.cargo/registry" - fi - - docker run $attach $flags \ - --rm \ - --interactive \ - --tty \ - --publish 8000:8000 \ - --name fej \ - --env-file .env.container \ - --network fej \ - "$image$([[ "$mode" != "rel" ]] && echo "-dev"):$tags" "$@" -fi diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 9205fd4..d2029af 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -3,4 +3,4 @@ FROM chewingbever/fej-builder:latest ENV RUST_BACKTRACE 1 ENTRYPOINT ["cargo"] -CMD ["run"] +CMD ["run", "--bin", "server"] diff --git a/docker/Dockerfile.rel b/docker/Dockerfile.rel index 6b4e438..b64916a 100644 --- a/docker/Dockerfile.rel +++ b/docker/Dockerfile.rel @@ -7,7 +7,7 @@ FROM chewingbever/fej-builder:latest AS builder # NOTE: cargo install auto-appends bin to the path RUN --mount=type=cache,target=/usr/src/app/target \ --mount=type=cache,target=/root/.cargo/registry \ - cargo install --path . --bin fej --root /usr/local + cargo install --path . --root /usr/local # Now, we create the actual image @@ -17,7 +17,7 @@ FROM alpine:latest RUN apk update && apk add --no-cache openssl libgcc curl # Copy binary over to final image -COPY --from=builder /usr/local/bin/fej /usr/local/bin/fej +COPY --from=builder /usr/local/bin/server /usr/local/bin/server HEALTHCHECK \ --interval=10s \ @@ -26,4 +26,4 @@ HEALTHCHECK \ --retries=3 \ CMD curl -q localhost:8000 -CMD ["/usr/local/bin/fej"] +CMD ["/usr/local/bin/server"] diff --git a/fejctl b/fejctl new file mode 100755 index 0000000..c977f79 --- /dev/null +++ b/fejctl @@ -0,0 +1,159 @@ +#!/usr/bin/env bash + +image='chewingbever/fej' + +# Creates the needed images +# +# $1: wether to build the debug or the release image (default debug) +function create_images() { + # First, we build the builder + DOCKER_BUILDKIT=1 docker build \ + -f docker/Dockerfile.builder \ + -t "$image-builder:latest" . + + if [[ "$1" = "rel" ]]; then + DOCKER_BUILDKIT=1 docker build \ + -t "$image:latest" \ + -f docker/Dockerfile.rel . + + else + # Then, we create the debug image + DOCKER_BUILDKIT=1 docker build \ + -t "$image:dev" \ + -f docker/Dockerfile.dev . + fi +} + +# Execute the debug image (must be built first) +# +# $@: the arguments to pass to the image (passed as arguments to cargo) +function run_image() { + # Run the database image + docker run --rm \ + --detach \ + --name fej_db \ + --network fej \ + -p 5432:5432 \ + -e 'POSTGRES_DB=fej' \ + -e 'POSTGRES_USER=fej' \ + -e 'POSTGRES_PASSWORD=fej' \ + -v 'fej_db-data:/var/lib/postgresql/data' \ + postgres:13-alpine + + # Run the binary image + docker run \ + --detach \ + --rm \ + --interactive \ + --tty \ + --publish 8000:8000 \ + --name fej \ + --env-file .env.container \ + --network fej \ + -v 'fej_build-cache:/usr/src/app/target' \ + -v 'fej_registry-cache:/root/.cargo/registry' \ + "$image:dev" "$@" +} + +# Attach to the fej container +function logs() { + docker logs -f fej +} + +# Builds the given binary +# +# $1: the binary to build +function build() { + create_images + run_image build --bin "$1" + logs +} + +# Runs the given binary +# +# $1: the binary to run +function run() { + create_images + run_image run --bin "$1" + logs +} + +# Runs the tests +function tests() { + create_images + run_image test --no-fail-fast + logs +} + +# Stops both containers +function stop() { + docker stop fej_db + docker stop -t 0 fej +} + +function run_release() { + echo "Not implemented" +} + +# Tags & pushes the release version to Docker Hub +function publish() { + branch=`git rev-parse --abbrev-ref HEAD` + + if [[ "$branch" != master ]]; then + >&2 echo "You can only publish from master." + exit 2 + fi + + patch_version=`grep -Po '(?<=version = ").*(?=")' Cargo.toml | head -n1` + major_version=`echo "$patch_version" | sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1/'` + minor_version=`echo "$patch_version" | sed -E 's/([0-9]+).([0-9]+).([0-9]+)/\1.\2/'` + tags=("latest" "$patch_version" "$minor_version" "$major_version") + + for tag in "${tags[@]}"; do + # Create the tag + docker tag "$image:$tags" "$image:$tag" + + # Push the tag + docker push "$image:$tag" + + # Remove the tag again, if it's not the main tag + [[ "$tag" != "$tags" ]] && docker rmi "$image:$tag" + done + +} + +# Entrypoint to the script +# +# $1: action to perform, defaults to 'build' +# $2: binary to use, defaults to 'server' +function main() { + # Default values + cmd="${1:-build}" + bin="${2:-server}" + + case $cmd in + # Building + b | build ) build "$bin" ;; + br | build-release ) create_images rel ;; + + # Running + r | run ) run "$bin" ;; + rr | run-release ) run_release ;; + s | stop ) stop ;; + + # Ease of life + psql ) docker exec -it fej_db psql -U fej -d fej ;; + sh ) docker exec -it fej sh ;; + + # Misc + docs ) cargo doc --no-deps ;; + format ) cargo fmt ;; + l | logs ) logs ;; + lint ) cargo fmt -- --check ;; + p | push | publish ) publish ;; + t | test ) tests ;; + * ) >&2 echo "Invalid command."; exit 1 ;; + esac +} + +main "$@" diff --git a/src/main.rs b/src/bin/server/main.rs similarity index 97% rename from src/main.rs rename to src/bin/server/main.rs index 8b8edc7..a27e00f 100644 --- a/src/main.rs +++ b/src/bin/server/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate rocket; -use fej_lib::{catchers, ivago}; +use fej::{catchers, ivago}; // Very temporary solution for CORS // https://stackoverflow.com/questions/62412361/how-to-set-up-cors-or-options-for-rocket-rs