diff --git a/docs/appendices/0.20.0-migration-guide.md b/docs/appendices/0.20.0-migration-guide.md index f42c3a4fe67..502e7ed4bf6 100644 --- a/docs/appendices/0.20.0-migration-guide.md +++ b/docs/appendices/0.20.0-migration-guide.md @@ -47,18 +47,38 @@ Dokku will no longer distribute packages for Debian versions that are not `stabl ### App report flags -The following changes on the `apps:report` command were made in this release +The `--git-sha` flag was moved from the `apps:report` command to the `git:report` command. + +## Changes + +### App report flags + +The following changes on the `apps:report` command were made in this release: -- The `--git-sha` flag was moved from the `apps:report` command to the `git:report` command. - The `--deploy-source` flag was renamed to `--app-deploy-source`. - The `--locked` flag was renamed to `--app-locked`. +### Network report flags + +The following changes on the `network:report` command were made in this release: + +- The `--network-listeners` flag was renamed to `--network-web-listeners`. + +### Plugin Triggers + +- The `network-get-port` trigger was incorrectly documented to have the `isHerokuishContainer` argument as the third argument. The code has been fixed to respect the documentation. +- The `network-get-listeners` trigger now takes an additional `processType` argument. If not specified, it defaults to `web`. This default will be removed in an upcoming release. + ## Deprecations - `nginx:show-conf` has been deprecated in favor of `nginx:show-config`. -- `proxy#is_app_proxy_enabled()` is deprecated in favor of `plugn trigger proxy-is-enabled` -- `proxy#get_app_proxy_type()` is deprecated in favor of `plugn trigger proxy-type` -- `apps#apps_create()` is deprecated in favor of `plugn trigger app-create` -- `apps#apps_destroy()` is deprecated in favor of `plugn trigger app-destroy` -- `apps#apps_exists()` is deprecated in favor of `plugn trigger app-exists` -- `apps#apps_maybe_create()` is deprecated in favor of `plugn trigger app-maybe-create` +- `proxy#is_app_proxy_enabled()` is deprecated in favor of `plugn trigger proxy-is-enabled`. +- `proxy#get_app_proxy_type()` is deprecated in favor of `plugn trigger proxy-type`. +- `apps#apps_create()` is deprecated in favor of `plugn trigger app-create`. +- `apps#apps_destroy()` is deprecated in favor of `plugn trigger app-destroy`. +- `apps#apps_exists()` is deprecated in favor of `plugn trigger app-exists`. +- `apps#apps_maybe_create()` is deprecated in favor of `plugn trigger app-maybe-create`. +- `plugin trigger network-get-listeners` usage without a second `processType` argument is deprecated. +- `.NGINX_PORT` variable usage within `nginx.conf.sigil` templates is deprecated in favor of `.PROXY_PORT`. +- `.NGINX_SSL_PORT` variable usage within `nginx.conf.sigil` templates is deprecated in favor of `.PROXY_SSL_PORT`. +- `.DOKKU_APP_LISTENERS` variable usage within `nginx.conf.sigil` templates is deprecated in favor of `.DOKKU_APP_WEB_LISTENERS`. diff --git a/docs/configuration/nginx.md b/docs/configuration/nginx.md index 3b94226f5b0..a7a744806f2 100644 --- a/docs/configuration/nginx.md +++ b/docs/configuration/nginx.md @@ -143,7 +143,6 @@ Dokku uses a templating library by the name of [sigil](https://github.com/glider {{ .APP }} Application name {{ .APP_SSL_PATH }} Path to SSL certificate and key {{ .DOKKU_ROOT }} Global Dokku root directory (ex: app dir would be `{{ .DOKKU_ROOT }}/{{ .APP }}`) -{{ .DOKKU_APP_LISTENERS }} List of IP:PORT pairs of app containers {{ .PROXY_PORT }} Non-SSL nginx listener port (same as `DOKKU_PROXY_PORT` config var) {{ .PROXY_SSL_PORT }} SSL nginx listener port (same as `DOKKU_PROXY_SSL_PORT` config var) {{ .NOSSL_SERVER_NAME }} List of non-SSL VHOSTS @@ -154,6 +153,8 @@ Dokku uses a templating library by the name of [sigil](https://github.com/glider {{ .SSL_SERVER_NAME }} List of SSL VHOSTS ``` +Finally, each process type has it's network listeners - a list of IP:PORT pairs for the respective app containers - exposed via an `.DOKKU_APP_${PROCESS_TYPE}_LISTENERS` variable - the `PROCESS_TYPE` will be upper-cased with hyphens transformed into underscores. Users can use the new variables to expose non-web processes via the nginx proxy. + > Note: Application config variables are available for use in custom templates. To do so, use the form of `{{ var "FOO" }}` to access a variable named `FOO`. ### Customizing via configuration files included by the default templates diff --git a/docs/configuration/ssl.md b/docs/configuration/ssl.md index 0ad972ad172..707f9e422f2 100644 --- a/docs/configuration/ssl.md +++ b/docs/configuration/ssl.md @@ -150,7 +150,7 @@ server { } upstream {{ .APP }} { -{{ range .DOKKU_APP_LISTENERS | split " " }} +{{ range .DOKKU_APP_WEB_LISTENERS | split " " }} server {{ . }}; {{ end }} } diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 27780a8a2a1..cadea183eba 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -678,7 +678,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: Return the listeners (host:port combinations) for a given app container - Invoked by: `internally triggered by a deploy` -- Arguments: `$APP` +- Arguments: `$APP $PROCESS_TYPE` - Example: ```shell diff --git a/plugins/checks/subcommands/run b/plugins/checks/subcommands/run index 3a5e9781677..3c5b747a18c 100755 --- a/plugins/checks/subcommands/run +++ b/plugins/checks/subcommands/run @@ -63,7 +63,7 @@ check_process() { dokku_log_info1 "Running checks for app ($APP.$PROC_TYPE.$CONTAINER_INDEX)" CONTAINER_ID=$(<"$DOKKU_CONTAINER_ID_FILE") IP="$(plugn trigger network-get-ipaddr "$APP" "$PROC_TYPE" "$CONTAINER_ID")" - PORT="$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$IS_HEROKUISH_CONTAINER" "$CONTAINER_ID")" + PORT="$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$CONTAINER_ID" "$IS_HEROKUISH_CONTAINER")" plugn trigger check-deploy "$APP" "$CONTAINER_ID" "$PROC_TYPE" "$PORT" "$IP" } diff --git a/plugins/common/functions b/plugins/common/functions index d6133bd2fd9..a9982f45c6d 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -902,10 +902,10 @@ internal_get_app_urls() { echo "$(<"$DOKKU_ROOT/HOSTNAME"):$app_port (container)" done else - local DOKKU_APP_LISTENERS PORT - DOKKU_APP_LISTENERS="$(plugn trigger network-get-listeners "$APP" | xargs)" - for DOKKU_APP_LISTENER in $DOKKU_APP_LISTENERS; do - PORT="$(echo "$DOKKU_APP_LISTENER" | cut -d ':' -f2)" + local DOKKU_APP_WEB_LISTENERS PORT + DOKKU_APP_WEB_LISTENERS="$(plugn trigger network-get-listeners "$APP" "web" | xargs)" + for DOKKU_APP_WEB_LISTENER in $DOKKU_APP_WEB_LISTENERS; do + PORT="$(echo "$DOKKU_APP_WEB_LISTENER" | cut -d ':' -f2)" echo "$SCHEME://$(<"$DOKKU_ROOT/HOSTNAME"):$PORT (container)" done shopt -u nullglob diff --git a/plugins/network/network.go b/plugins/network/network.go index 0ae0bff53d6..880f869f1b0 100644 --- a/plugins/network/network.go +++ b/plugins/network/network.go @@ -72,7 +72,7 @@ func BuildConfig(appName string) { } ipAddress := GetContainerIpaddress(appName, processType, containerID) - port := GetContainerPort(appName, processType, isHerokuishContainer, containerID) + port := GetContainerPort(appName, processType, containerID, isHerokuishContainer) if ipAddress != "" { args := []string{appName, processType, containerIndexString, ipAddress} @@ -95,10 +95,6 @@ func BuildConfig(appName string) { // GetContainerIpaddress returns the ipaddr for a given app container func GetContainerIpaddress(appName, processType, containerID string) (ipAddr string) { - if processType != "web" { - return - } - if b, err := common.DockerInspect(containerID, "'{{ .HostConfig.NetworkMode }}'"); err == nil { if string(b[:]) == "host" { return "127.0.0.1" @@ -119,11 +115,7 @@ func GetContainerIpaddress(appName, processType, containerID string) (ipAddr str } // GetContainerPort returns the port for a given app container -func GetContainerPort(appName, processType string, isHerokuishContainer bool, containerID string) (port string) { - if processType != "web" { - return - } - +func GetContainerPort(appName, processType string, containerID string, isHerokuishContainer bool) (port string) { dockerfilePorts := make([]string, 0) if !isHerokuishContainer { configValue := config.GetWithDefault(appName, "DOKKU_DOCKERFILE_PORTS", "") @@ -165,14 +157,17 @@ func GetDefaultValue(property string) (value string) { } // GetListeners returns a string array of app listeners -func GetListeners(appName string) []string { +func GetListeners(appName string, processType string) []string { appRoot := common.AppRoot(appName) - files, _ := filepath.Glob(appRoot + "/IP.web.*") + ipPrefix := fmt.Sprintf("/IP.%s.", processType) + portPrefix := fmt.Sprintf("/PORT.%s.", processType) + + files, _ := filepath.Glob(appRoot + ipPrefix + "*") var listeners []string for _, ipfile := range files { - portfile := strings.Replace(ipfile, "/IP.web.", "/PORT.web.", 1) + portfile := strings.Replace(ipfile, ipPrefix, portPrefix, 1) ipAddress := common.ReadFirstLine(ipfile) port := common.ReadFirstLine(portfile) listeners = append(listeners, fmt.Sprintf("%s:%s", ipAddress, port)) @@ -221,7 +216,7 @@ func ReportSingleApp(appName, infoFlag string) error { "--network-bind-all-interfaces": common.PropertyGet("network", appName, "bind-all-interfaces"), "--network-attach-post-create": common.PropertyGet("network", appName, "attach-post-create"), "--network-attach-post-deploy": common.PropertyGet("network", appName, "attach-post-deploy"), - "--network-listeners": strings.Join(GetListeners(appName), " "), + "--network-web-listeners": strings.Join(GetListeners(appName, "web"), " "), } trimPrefix := false diff --git a/plugins/network/src/triggers/triggers.go b/plugins/network/src/triggers/triggers.go index 1218e283fdd..d62af8f858f 100644 --- a/plugins/network/src/triggers/triggers.go +++ b/plugins/network/src/triggers/triggers.go @@ -38,13 +38,14 @@ func main() { network.TriggerNetworkGetIppaddr(appName, processType, containerID) case "network-get-listeners": appName := flag.Arg(0) - network.TriggerNetworkGetListeners(appName) + processType := flag.Arg(1) + network.TriggerNetworkGetListeners(appName, processType) case "network-get-port": appName := flag.Arg(0) processType := flag.Arg(1) - isHerokuishContainer := common.ToBool(flag.Arg(2)) - containerID := flag.Arg(3) - network.TriggerNetworkGetPort(appName, processType, isHerokuishContainer, containerID) + containerID := flag.Arg(2) + isHerokuishContainer := common.ToBool(flag.Arg(3)) + network.TriggerNetworkGetPort(appName, processType, containerID, isHerokuishContainer) case "network-get-property": appName := flag.Arg(0) property := flag.Arg(1) diff --git a/plugins/network/triggers.go b/plugins/network/triggers.go index 1ce32368dcb..631767d178b 100644 --- a/plugins/network/triggers.go +++ b/plugins/network/triggers.go @@ -40,10 +40,6 @@ func TriggerInstall() { // TriggerNetworkComputePorts computes the ports for a given app container func TriggerNetworkComputePorts(appName string, processType string, isHerokuishContainer bool) { - if processType != "web" { - return - } - var dockerfilePorts []string if !isHerokuishContainer { dokkuDockerfilePorts := strings.Trim(config.GetWithDefault(appName, "DOKKU_DOCKERFILE_PORTS", ""), " ") @@ -84,14 +80,18 @@ func TriggerNetworkGetIppaddr(appName string, processType string, containerID st } // TriggerNetworkGetListeners returns the listeners (host:port combinations) for a given app container -func TriggerNetworkGetListeners(appName string) { - listeners := GetListeners(appName) +func TriggerNetworkGetListeners(appName string, processType string) { + if processType == "" { + common.LogWarn("Deprecated: Please specify a processType argument for the network-get-listeners plugin trigger") + processType = "web" + } + listeners := GetListeners(appName, processType) fmt.Println(strings.Join(listeners, " ")) } // TriggerNetworkGetPort writes the port to stdout for a given app container -func TriggerNetworkGetPort(appName string, processType string, isHerokuishContainer bool, containerID string) { - port := GetContainerPort(appName, processType, isHerokuishContainer, containerID) +func TriggerNetworkGetPort(appName string, processType string, containerID string, isHerokuishContainer bool) { + port := GetContainerPort(appName, processType, containerID, isHerokuishContainer) fmt.Println(port) } diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 98d83b280a2..330a3cae965 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -296,7 +296,7 @@ nginx_build_config() { if [[ "$(plugn trigger proxy-is-enabled "$APP")" == "true" ]]; then if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -z "$DOKKU_APP_LISTEN_IP" ]]; then - DOKKU_APP_LISTENERS="$(plugn trigger network-get-listeners "$APP" | xargs)" + DOKKU_APP_LISTENERS="$(plugn trigger network-get-listeners "$APP" "web" | xargs)" elif [[ -n "$DOKKU_APP_LISTEN_PORT" ]] && [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then local PASSED_LISTEN_IP_PORT=true fi @@ -387,6 +387,7 @@ nginx_build_config() { eval "$(config_export app "$APP")" local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME" + # Deprecated: Remove this after a few versions DOKKU_APP_LISTENERS="$DOKKU_APP_LISTENERS" DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT" PASSED_LISTEN_IP_PORT="$PASSED_LISTEN_IP_PORT" @@ -400,11 +401,34 @@ nginx_build_config() { GRPC_SUPPORTED="$GRPC_SUPPORTED" DOKKU_APP_LISTEN_PORT="$DOKKU_APP_LISTEN_PORT" DOKKU_APP_LISTEN_IP="$DOKKU_APP_LISTEN_IP" APP_SSL_PATH="$APP_SSL_PATH" SSL_INUSE="$SSL_INUSE" SSL_SERVER_NAME="$SSL_SERVER_NAME" - # @TODO: Remove this after a few versions + # Deprecated: Remove this after a few versions NGINX_PORT="$PROXY_PORT" NGINX_SSL_PORT="$PROXY_SSL_PORT" PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT" RAW_TCP_PORTS="$RAW_TCP_PORTS" PROXY_PORT_MAP="$PROXY_PORT_MAP" PROXY_UPSTREAM_PORTS="$PROXY_UPSTREAM_PORTS") + local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + while read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^#.* ]] && continue + line="$(strip_inline_comments "$line")" + PROC_TYPE=${line%%=*} + LISTENERS="$(plugn trigger network-get-listeners "$APP" "$PROC_TYPE" | xargs)" + UPP_PROC_TYPE="${PROC_TYPE^^}" + UPP_PROC_TYPE="${UPP_PROC_TYPE//-/_}" + SIGIL_PARAMS+=( "DOKKU_APP_${UPP_PROC_TYPE}_LISTENERS=$LISTENERS" ) + done <"$DOKKU_SCALE_FILE" + + if grep DOKKU_APP_LISTENERS "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of DOKKU_APP_LISTENERS within nginx.conf.sigil templates is deprecated in favor of DOKKU_APP_WEB_LISTENERS" + fi + + if grep NGINX_SSL_PORT "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of NGINX_SSL_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_SSL_PORT" + fi + + if grep NGINX_PORT "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of NGINX_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_PORT" + fi + # execute sigil template processing xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)" sigil "${SIGIL_PARAMS[@]}" | cat -s >"$NGINX_CONF" diff --git a/plugins/nginx-vhosts/templates/nginx.conf.sigil b/plugins/nginx-vhosts/templates/nginx.conf.sigil index f1abb99b620..dc642e5a6ff 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.sigil +++ b/plugins/nginx-vhosts/templates/nginx.conf.sigil @@ -154,10 +154,10 @@ server { {{ end }} {{ end }} -{{ if $.DOKKU_APP_LISTENERS }} +{{ if $.DOKKU_APP_WEB_LISTENERS }} {{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} upstream {{ $.APP }}-{{ $upstream_port }} { -{{ range $listeners := $.DOKKU_APP_LISTENERS | split " " }} +{{ range $listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }} {{ $listener_list := $listeners | split ":" }} {{ $listener_ip := index $listener_list 0 }} server {{ $listener_ip }}:{{ $upstream_port }};{{ end }} diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 03c72c1c79b..6f4c7c0f536 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -108,14 +108,14 @@ trigger-scheduler-docker-local-scheduler-deploy() { START_CMD=$(fn-scheduler-docker-local-extract-start-cmd "$APP" "$PROC_TYPE" "$START_CMD" "$DOKKU_HEROKUISH") # shellcheck disable=SC2086 - cid=$("$DOCKER_BIN" container create --label=com.dokku.container-type=deploy --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --init "${ARG_ARRAY[@]}" $IMAGE $START_CMD) + cid=$("$DOCKER_BIN" container create --label=com.dokku.container-type=deploy --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --init --env PORT=$DOKKU_PORT "${ARG_ARRAY[@]}" $IMAGE $START_CMD) fi plugn trigger post-container-create "app" "$cid" "$APP" "deploy" "$PROC_TYPE" "$DOCKER_BIN" container start "$cid" >/dev/null || true ipaddr=$(plugn trigger network-get-ipaddr "$APP" "$PROC_TYPE" "$cid") - port=$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$DOKKU_HEROKUISH" "$cid") + port=$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$cid" "$DOKKU_HEROKUISH") kill_new() { declare desc="wrapper function to kill newly started app container" diff --git a/tests/unit/40_nginx-vhosts_2.bats b/tests/unit/40_nginx-vhosts_2.bats index 30c96be01ad..6fbc2dd649a 100644 --- a/tests/unit/40_nginx-vhosts_2.bats +++ b/tests/unit/40_nginx-vhosts_2.bats @@ -76,6 +76,7 @@ teardown() { add_domain "wildcard1.dokku.me" add_domain "wildcard2.dokku.me" deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_ssl_nginx_template + assert_ssl_domain "wildcard1.dokku.me" assert_ssl_domain "wildcard2.dokku.me" assert_http_redirect "http://${CUSTOM_TEMPLATE_SSL_DOMAIN}" "https://${CUSTOM_TEMPLATE_SSL_DOMAIN}:443/" @@ -84,9 +85,23 @@ teardown() { @test "(nginx-vhosts) nginx:build-config (custom nginx template - no ssl)" { add_domain "www.test.app.dokku.me" - deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template + run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:scale $TEST_APP worker=1" + echo "output: $output" + echo "status: $status" + assert_success + assert_nonssl_domain "www.test.app.dokku.me" assert_http_success "customtemplate.dokku.me" + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_output_contains "${TEST_APP}-worker-5000" } @test "(nginx-vhosts) nginx:build-config (failed validate_nginx)" { diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 0e5c8890926..b02ba0389c1 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -374,10 +374,10 @@ server { } {{ end }}{{ end }} -{{ if $.DOKKU_APP_LISTENERS }} +{{ if $.DOKKU_APP_WEB_LISTENERS }} {{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} upstream {{ $.APP }}-{{ \$upstream_port }} { -{{ range \$listeners := $.DOKKU_APP_LISTENERS | split " " }} +{{ range \$listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }} {{ \$listener_list := \$listeners | split ":" }} {{ \$listener_ip := index \$listener_list 0 }} server {{ \$listener_ip }}:{{ \$upstream_port }};{{ end }} @@ -421,15 +421,23 @@ server { } {{ end }} -{{ if $.DOKKU_APP_LISTENERS }} +{{ if $.DOKKU_APP_WEB_LISTENERS }} {{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} upstream {{ $.APP }}-{{ \$upstream_port }} { -{{ range \$listeners := $.DOKKU_APP_LISTENERS | split " " }} +{{ range \$listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }} {{ \$listener_list := \$listeners | split ":" }} {{ \$listener_ip := index \$listener_list 0 }} server {{ \$listener_ip }}:{{ \$upstream_port }};{{ end }} } {{ end }}{{ end }} +{{ if $.DOKKU_APP_WORKER_LISTENERS }} +{{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} +upstream {{ $.APP }}-worker-{{ \$upstream_port }} { +{{ range \$listeners := $.DOKKU_APP_WORKER_LISTENERS | split " " }} + server {{ \$listeners }};{{ end }} +} +{{ end }}{{ end }} + EOF cat "$APP_REPO_DIR/nginx.conf.sigil" }