diff --git a/scripts/dev.sh b/scripts/dev.sh index e88785f3..6164bcbb 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -1,10 +1,16 @@ #!/bin/bash set -euo pipefail -# A convenience script that launches a fresh postgres container instance that -# can be used by the graphql-engine server. After launch the verbose postgres -# logs will be printed. On shutdown we'll try to clean up the container -# completely. +# A development swiss army knife script. The goals are to: +# +# - encode some best practices and hard-won knowledge of quirks and corners of +# our tooling +# - simplify development; especially for new-comers; instead of writing a huge +# document describing how to do various dev tasks (or worse yet, not writing +# one), make it runnable +# +# This makes use of 'cabal.project.dev-sh*' files when building. See +# 'cabal.project.dev-sh.local'. echo_pretty() { @@ -164,24 +170,16 @@ if [ "$MODE" = "graphql-engine" ]; then echo_pretty "or press CTRL-C and invoke graphql-engine manually" echo_pretty "" - if [[ ! -e cabal.project.local ]]; then - echo_pretty 'No cabal.project.local file exists. Running' - echo_pretty ' $ ln -s cabal.project.dev cabal.project.local' - echo_pretty 'to use the default.' - echo_pretty '' - ln -s cabal.project.dev cabal.project.local - fi - - RUN_INVOCATION=(cabal new-run --RTS -- exe:graphql-engine +RTS -N -T -RTS + RUN_INVOCATION=(cabal new-run --project-file=cabal.project.dev-sh --RTS -- exe:graphql-engine +RTS -N -T -RTS --database-url="$DB_URL" serve --enable-console --console-assets-dir "$PROJECT_ROOT/console/static/dist") echo_pretty 'About to do:' - echo_pretty ' $ cabal new-build exe:graphql-engine' + echo_pretty ' $ cabal new-build --project-file=cabal.project.dev-sh exe:graphql-engine' echo_pretty " $ ${RUN_INVOCATION[*]}" echo_pretty '' - cabal new-build exe:graphql-engine + cabal new-build --project-file=cabal.project.dev-sh exe:graphql-engine wait_docker_postgres # Print helpful info after startup logs so it's visible: @@ -271,8 +269,8 @@ EOL echo if [ ! -z "${GRAPHQL_ENGINE_PID-}" ]; then - # This may already have been killed: - kill "$GRAPHQL_ENGINE_PID" &>/dev/null || true + # Kill the cabal new-run and its children. This may already have been killed: + pkill -P "$GRAPHQL_ENGINE_PID" &>/dev/null || true fi case "$MODE" in @@ -316,30 +314,35 @@ if [ "$MODE" = "postgres" ]; then docker logs -f --tail=0 "$PG_CONTAINER_NAME" elif [ "$MODE" = "test" ]; then - ################################# - ### Integration tests ### - ################################# + ######################################## + ### Integration / unit tests ### + ######################################## cd "$PROJECT_ROOT/server" + # We'll get an hpc error if these exist; they will be deleted below too: + rm -f graphql-engine-tests.tix graphql-engine.tix graphql-engine-combined.tix + export EVENT_WEBHOOK_HEADER="MyEnvValue" export WEBHOOK_FROM_ENV="http://127.0.0.1:5592" - # It's better UX to build first (possibly failing) before trying to launch PG: - cabal new-build exe:graphql-engine test:graphql-engine-tests + # It's better UX to build first (possibly failing) before trying to launch + # PG, but make sure that new-run uses the exact same build plan, else we risk + # rebuilding twice... ugh + cabal new-build --project-file=cabal.project.dev-sh exe:graphql-engine test:graphql-engine-tests launch_postgres_container wait_docker_postgres # These also depend on a running DB: if [ "$RUN_UNIT_TESTS" = true ]; then echo_pretty "Running Haskell test suite" - HASURA_GRAPHQL_DATABASE_URL="$DB_URL" cabal new-run -- test:graphql-engine-tests + HASURA_GRAPHQL_DATABASE_URL="$DB_URL" cabal new-run --project-file=cabal.project.dev-sh -- test:graphql-engine-tests fi if [ "$RUN_INTEGRATION_TESTS" = true ]; then echo_pretty "Starting graphql-engine" GRAPHQL_ENGINE_TEST_LOG=/tmp/hasura-dev-test-engine.log export HASURA_GRAPHQL_SERVER_PORT=8088 - cabal new-run -- exe:graphql-engine --database-url="$DB_URL" serve --stringify-numeric-types \ + cabal new-run --project-file=cabal.project.dev-sh -- exe:graphql-engine --database-url="$DB_URL" serve --stringify-numeric-types \ --enable-console --console-assets-dir ../console/static/dist \ &> "$GRAPHQL_ENGINE_TEST_LOG" & GRAPHQL_ENGINE_PID=$! @@ -396,10 +399,50 @@ elif [ "$MODE" = "test" ]; then set -u cd "$PROJECT_ROOT/server" - kill -INT "$GRAPHQL_ENGINE_PID" + # Kill the cabal new-run and its children. INT so we get hpc report: + pkill -INT -P "$GRAPHQL_ENGINE_PID" wait "$GRAPHQL_ENGINE_PID" || true echo + fi # RUN_INTEGRATION_TESTS + + # TODO generate coverage report when we CTRL-C from 'dev.sh graphql-engine'. + # If hpc available, combine any tix from haskell/unit tests: + if command -v hpc >/dev/null; then + if [ "$RUN_UNIT_TESTS" = true ] && [ "$RUN_INTEGRATION_TESTS" = true ]; then + # As below, it seems we variously get errors related to having two Main + # modules, so exclude: + hpc combine --exclude=Main graphql-engine-tests.tix graphql-engine.tix --union > graphql-engine-combined.tix + else + # One of these should exist + cp graphql-engine-tests.tix graphql-engine-combined.tix 2>/dev/null || true + cp graphql-engine.tix graphql-engine-combined.tix 2>/dev/null || true + fi + # Generate a report including the test code itself (see cabal.project.dev-sh.local): + # NOTE: we have to omit the .mix directory for the executable, since it + # seems hpc can't cope with two modules of the same name; '--exclude' + # didn't help. + echo_pretty "Generating code coverage report..." + COVERAGE_DIR="dist-newstyle/dev.sh-coverage" + hpc markup \ + --exclude=Main \ + --hpcdir dist-newstyle/build/*/ghc-*/graphql-engine-*/noopt/hpc/vanilla/mix/graphql-engine-* \ + --hpcdir dist-newstyle/build/*/ghc-*/graphql-engine-*/t/graphql-engine-tests/noopt/hpc/vanilla/mix/graphql-engine-tests \ + --reset-hpcdirs graphql-engine-combined.tix \ + --fun-entry-count \ + --destdir="$COVERAGE_DIR" >/dev/null + hpc report \ + --exclude=Main \ + --hpcdir dist-newstyle/build/*/ghc-*/graphql-engine-*/noopt/hpc/vanilla/mix/graphql-engine-* \ + --hpcdir dist-newstyle/build/*/ghc-*/graphql-engine-*/t/graphql-engine-tests/noopt/hpc/vanilla/mix/graphql-engine-tests \ + --reset-hpcdirs graphql-engine-combined.tix + echo_pretty "To view full coverage report open:" + echo_pretty " file://$(pwd)/$COVERAGE_DIR/hpc_index.html" + + else + echo_warn "Please install hpc to get a combined code coverage report for tests" fi + rm -f graphql-engine-tests.tix graphql-engine.tix graphql-engine-combined.tix + else echo "impossible; fix script." fi diff --git a/server/cabal.project.dev-sh b/server/cabal.project.dev-sh new file mode 120000 index 00000000..b4c9ceac --- /dev/null +++ b/server/cabal.project.dev-sh @@ -0,0 +1 @@ +cabal.project \ No newline at end of file diff --git a/server/cabal.project.dev-sh.freeze b/server/cabal.project.dev-sh.freeze new file mode 120000 index 00000000..ba0e55e3 --- /dev/null +++ b/server/cabal.project.dev-sh.freeze @@ -0,0 +1 @@ +cabal.project.freeze \ No newline at end of file diff --git a/server/cabal.project.dev-sh.local b/server/cabal.project.dev-sh.local new file mode 100644 index 00000000..4b35488a --- /dev/null +++ b/server/cabal.project.dev-sh.local @@ -0,0 +1,23 @@ +-- This is the local cabal configuration file used by the 'scripts/dev.sh' +-- development script, which uses `... --project-file=cabal.project.dev-sh` +-- which in turn consults this file. +-- +-- You can temporarily override values here rather than modifying 'dev.sh' +-- during development (although that might break certain things)values here +-- rather than modifying 'dev.sh' during development (although that might +-- break certain things). + +package * + documentation: true + +package graphql-engine + -- NOTE: this seems to work so long as there is no 'ghc-options: -O2' in the cabal file, + -- but new-build will report 'Build profile: -O1' for some reason. + -- See:https://github.com/haskell/cabal/issues/6221 + optimization: 0 + documentation: false + flags: +developer + -- NOTE: 'cabal new-build --enable-coverage' seems to rebuild all deps with coverage + -- which is not what we originally wanted. But building the test modules + -- themselves with coverage is actually nice for validation. + coverage: true