From cba345025c6254e75fb45947989ac511309da085 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Sep 2025 04:28:05 -0400 Subject: [PATCH] feat: add a Railpack builder to Dokku Closes #7615 --- Dockerfile | 5 + docker/etc/sudoers.d/dokku-docker | 2 +- docker/usr/local/bin/railpack | 20 +++ docs/appendices/file-formats/railpack-json.md | 3 + docs/deployment/builders/railpack.md | 163 ++++++++++++++++++ docs/template.html | 1 + plugins/builder-railpack/builder-build | 67 +++++++ plugins/builder-railpack/builder-detect | 17 ++ plugins/builder-railpack/builder-release | 30 ++++ plugins/builder-railpack/commands | 15 ++ plugins/builder-railpack/core-post-extract | 30 ++++ .../dockerfiles/builder-build.Dockerfile | 7 + .../dockerfiles/builder-release.Dockerfile | 2 + plugins/builder-railpack/help-functions | 33 ++++ plugins/builder-railpack/install | 16 ++ plugins/builder-railpack/internal-functions | 116 +++++++++++++ plugins/builder-railpack/plugin.toml | 4 + plugins/builder-railpack/post-app-clone-setup | 14 ++ .../builder-railpack/post-app-rename-setup | 15 ++ plugins/builder-railpack/post-delete | 15 ++ plugins/builder-railpack/report | 6 + plugins/builder-railpack/subcommands/default | 6 + plugins/builder-railpack/subcommands/report | 6 + plugins/builder-railpack/subcommands/set | 30 ++++ plugins/docker-options/docker-args-deploy | 2 +- tests/unit/builder-railpack.bats | 52 ++++++ tests/unit/report.bats | 1 + tests/unit/test_helper.bash | 6 + 28 files changed, 682 insertions(+), 2 deletions(-) create mode 100644 docker/usr/local/bin/railpack create mode 100644 docs/appendices/file-formats/railpack-json.md create mode 100644 docs/deployment/builders/railpack.md create mode 100755 plugins/builder-railpack/builder-build create mode 100755 plugins/builder-railpack/builder-detect create mode 100755 plugins/builder-railpack/builder-release create mode 100755 plugins/builder-railpack/commands create mode 100755 plugins/builder-railpack/core-post-extract create mode 100644 plugins/builder-railpack/dockerfiles/builder-build.Dockerfile create mode 100644 plugins/builder-railpack/dockerfiles/builder-release.Dockerfile create mode 100755 plugins/builder-railpack/help-functions create mode 100755 plugins/builder-railpack/install create mode 100755 plugins/builder-railpack/internal-functions create mode 100644 plugins/builder-railpack/plugin.toml create mode 100755 plugins/builder-railpack/post-app-clone-setup create mode 100755 plugins/builder-railpack/post-app-rename-setup create mode 100755 plugins/builder-railpack/post-delete create mode 100755 plugins/builder-railpack/report create mode 100755 plugins/builder-railpack/subcommands/default create mode 100755 plugins/builder-railpack/subcommands/report create mode 100755 plugins/builder-railpack/subcommands/set create mode 100644 tests/unit/builder-railpack.bats diff --git a/Dockerfile b/Dockerfile index f598c5230..f0b5b79a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,11 @@ RUN mkdir -p /etc/apt/keyrings \ && NIXPACKS_BIN_DIR=/usr/bin BIN_DIR=/usr/bin /tmp/nixpacks.bash \ && test -x /usr/bin/nixpacks \ && rm -rf /tmp/nixpacks.bash \ + && curl -o /tmp/railpack.bash -sSL https://railpack.com/install.sh \ + && chmod +x /tmp/railpack.bash \ + && RAILPACK_BIN_DIR=/usr/bin /tmp/railpack.bash \ + && test -x /usr/bin/railpack \ + && rm -rf /tmp/railpack.bash \ && echo "dokku dokku/hostname string $DOKKU_HOSTNAME" | debconf-set-selections \ && echo "dokku dokku/skip_key_file boolean $DOKKU_SKIP_KEY_FILE" | debconf-set-selections \ && echo "dokku dokku/vhost_enable boolean $DOKKU_VHOST_ENABLE" | debconf-set-selections \ diff --git a/docker/etc/sudoers.d/dokku-docker b/docker/etc/sudoers.d/dokku-docker index bebd88538..f3dc76faa 100644 --- a/docker/etc/sudoers.d/dokku-docker +++ b/docker/etc/sudoers.d/dokku-docker @@ -1 +1 @@ -dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-container-healthchecker,/usr/bin/docker-image-labeler,/usr/bin/lambda-builder,/usr/bin/nixpacks,/usr/bin/pack +dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-container-healthchecker,/usr/bin/docker-image-labeler,/usr/bin/lambda-builder,/usr/bin/nixpacks,/usr/bin/railpack,/usr/bin/pack diff --git a/docker/usr/local/bin/railpack b/docker/usr/local/bin/railpack new file mode 100644 index 000000000..35bee71d2 --- /dev/null +++ b/docker/usr/local/bin/railpack @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $TRACE ]] && set -x + +main() { + declare desc="re-runs railpack commands as sudo" + local RAILPACK_BIN="" + if [[ -x "/usr/bin/railpack" ]]; then + RAILPACK_BIN="/usr/bin/railpack" + fi + + if [[ -z "$RAILPACK_BIN" ]]; then + echo "! No railpack binary found" 1>&2 + exit 1 + fi + + sudo -E "$RAILPACK_BIN" "$@" +} + +main "$@" diff --git a/docs/appendices/file-formats/railpack-json.md b/docs/appendices/file-formats/railpack-json.md new file mode 100644 index 000000000..d6e390fb4 --- /dev/null +++ b/docs/appendices/file-formats/railpack-json.md @@ -0,0 +1,3 @@ +# railpack.json + +The `railpack.json` file is used to configure an application when built with the `railpack` builder. Please refer to the [railpack.json documentation](https://railpack.com/config/file) for more information. diff --git a/docs/deployment/builders/railpack.md b/docs/deployment/builders/railpack.md new file mode 100644 index 000000000..3483c7d72 --- /dev/null +++ b/docs/deployment/builders/railpack.md @@ -0,0 +1,163 @@ +# Railpack + +> [!IMPORTANT] +> New as of 0.32.0 + +The `railpack` builder builds apps via [Railpack](https://railpack.com/), a buildpack alternative. + +## Usage + +### Requirements + +The `railpack` cli tool is not included by default with Dokku or as a dependency. It must also be installed as shown on [this page](https://railpack.com/installation). + +Builds will proceed with the `railpack` cli for the app from then on. + +### Detection + +This builder will be auto-detected in the following case: + +- A `railpack.json` exists in the root of the app repository. + +The builder may also be selected via the `builder:set` command + +```shell +dokku builder:set node-js-app selected railpack +``` + +### Supported languages + +See the [upstream railpack documentation](https://railpack.com/) for further information on what languages and frameworks are supported. + +### Build-time configuration variables + +For security reasons - and as per [Docker recommendations](https://github.com/docker/docker/issues/13490) - railpack-based deploys have variables available only during runtime. + +For users that require customization in the `build` phase, you may use build arguments via the [docker-options plugin](/docs/advanced-usage/docker-options.md). All environment variables set by the `config` plugin are automatically exported within the railpack build environment, and thus `--env` only requires setting a key without a value. + +```shell +dokku docker-options:add node-js-app build '--env NODE_ENV' +``` + +Alternatively, a full value may be provided in the form of `--env KEY=VALUE`: + +```shell +dokku docker-options:add node-js-app build '--env NODE_ENV=production' +``` + +### Changing the `railpack.json` location + +The `railpack.json` is expected to be found in a specific directory, depending on the deploy approach: + +- The `WORKDIR` of the Docker image for deploys resulting from `git:from-image` and `git:load-image` commands. +- The root of the source code tree for all other deploys (git push, `git:from-archive`, `git:sync`). + +Sometimes it may be desirable to set a different path for a given app, e.g. when deploying from a monorepo. This can be done via the `railpackjson-path` property: + +```shell +dokku builder-railpack:set node-js-app railpackjson-path .dokku/railpack.json +``` + +The value is the path to the desired file *relative* to the base search directory, and will never be treated as absolute paths in any context. If that file does not exist within the repository, the build will fail. + +The default value may be set by passing an empty value for the option: + +```shell +dokku builder-railpack:set node-js-app railpackjson-path +``` + +The `railpackjson-path` property can also be set globally. The global default is `railpack.json`, and the global value is used when no app-specific value is set. + +```shell +dokku builder-railpack:set --global railpackjson-path railpack2.json +``` + +The default value may be set by passing an empty value for the option. + +```shell +dokku builder-railpack:set --global railpackjson-path +``` + +### Disabling cache + +Cache is enabled by default, but can be disabled by setting the `no-cache` property to `true`: + +```shell +dokku builder-railpack:set node-js-app no-cache true +``` + +The default value may be set by passing an empty value for the option: + +```shell +dokku builder-railpack:set node-js-app no-cache +``` + +The `no-cache` property can also be set globally. The global default is `false`, and the global value is used when no app-specific value is set. + +```shell +dokku builder-railpack:set --global no-cache true +``` + +The default value may be set by passing an empty value for the option. + +```shell +dokku builder-railpack:set --global no-cache +``` + +### Displaying builder-railpack reports for an app + +You can get a report about the app's storage status using the `builder-railpack:report` command: + +```shell +dokku builder-railpack:report +``` + +``` +=====> node-js-app builder-railpack information + Builder-railpack computed railpackjson path: railpack2.json + Builder-railpack global railpackjson path: railpack.json + Builder-railpack railpackjson path: railpack2.json + Builder-railpack computed no cache: true + Builder-railpack global no cache: false + Builder-railpack no cache: true +=====> python-sample builder-railpack information + Builder-railpack computed railpackjson path: railpack.json + Builder-railpack global railpackjson path: railpack.json + Builder-railpack railpackjson path: + Builder-railpack computed no cache: false + Builder-railpack global no cache: false + Builder-railpack no cache: +=====> ruby-sample builder-railpack information + Builder-railpack computed railpackjson path: railpack.json + Builder-railpack global railpackjson path: railpack.json + Builder-railpack railpackjson path: + Builder-railpack computed no cache: false + Builder-railpack global no cache: false + Builder-railpack no cache: +``` + +You can run the command for a specific app also. + +```shell +dokku builder-railpack:report node-js-app +``` + +``` +=====> node-js-app builder-railpack information + Builder-railpack computed railpackjson path: railpack2.json + Builder-railpack global railpackjson path: railpack.json + Builder-railpack railpackjson path: railpack2.json + Builder-railpack computed no cache: true + Builder-railpack global no cache: false + Builder-railpack no cache: true +``` + +You can pass flags which will output only the value of the specific information you want. For example: + +```shell +dokku builder-railpack:report node-js-app --builder-railpack-no-cache +``` + +``` +true +``` diff --git a/docs/template.html b/docs/template.html index d81802c40..9e26b3759 100644 --- a/docs/template.html +++ b/docs/template.html @@ -150,6 +150,7 @@ Lambda Builder Nixpacks Builder Null Builder + Railpack Builder Deployment Methods Git Deployment diff --git a/plugins/builder-railpack/builder-build b/plugins/builder-railpack/builder-build new file mode 100755 index 000000000..c1664fcac --- /dev/null +++ b/plugins/builder-railpack/builder-build @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/internal-functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-builder-build() { + declare desc="builder-railpack builder-build plugin trigger" + declare trigger="builder-build" + declare BUILDER_TYPE="$1" APP="$2" SOURCECODE_WORK_DIR="$3" + + if [[ "$BUILDER_TYPE" != "railpack" ]]; then + return + fi + + dokku_log_info1 "Building $APP from railpack" + + if ! command -v "railpack" &>/dev/null; then + dokku_log_fail "Missing railpack, install it" + fi + + local IMAGE=$(get_app_image_name "$APP") + local DOCKER_BUILD_LABEL_ARGS=("--label=dokku" "--label=org.label-schema.schema-version=1.0" "--label=org.label-schema.vendor=dokku" "--label=com.dokku.image-stage=build" "--label=com.dokku.builder-type=railpack" "--label=com.dokku.app-name=$APP") + + pushd "$SOURCECODE_WORK_DIR" &>/dev/null + + plugn trigger pre-build "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR" + + no_cache="$(fn-builder-railpack-computed-no-cache "$APP")" + RAILPACK_ARGS="" + if [[ "$no_cache" == "true" ]]; then + RAILPACK_ARGS="$RAILPACK_ARGS --no-cache" + fi + + local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE") + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE") + + # strip --link, --volume and -v args from DOCKER_ARGS + local DOCKER_ARGS=$(sed -e "s/^--link=[[:graph:]]\+[[:blank:]]\?//g" -e "s/^--link[[:blank:]]\?[[:graph:]]\+[[:blank:]]\?//g" -e "s/^--volume=[[:graph:]]\+[[:blank:]]\?//g" -e "s/^--volume[[:blank:]]\?[[:graph:]]\+[[:blank:]]\?//g" -e "s/^-v[[:blank:]]\?[[:graph:]]\+[[:blank:]]\?//g" <<<"$DOCKER_ARGS") + declare -a ARG_ARRAY + eval "ARG_ARRAY=($DOCKER_ARGS)" + + eval "$(config_export app "$APP" --merged)" + + if [[ -f "$SOURCECODE_WORK_DIR/Procfile" ]]; then + if procfile-util exists --process-type release; then + procfile-util delete --process-type release + fi + fi + + # shellcheck disable=SC2086 + if ! railpack build "${DOCKER_BUILD_LABEL_ARGS[@]}" $DOKKU_GLOBAL_BUILD_ARGS "${ARG_ARRAY[@]}" $RAILPACK_ARGS --name "$IMAGE" "$SOURCECODE_WORK_DIR"; then + dokku_log_warn "Failure building image" + return 1 + fi + + if ! suppress_output "$DOCKER_BIN" image build -f "$PLUGIN_AVAILABLE_PATH/builder-railpack/dockerfiles/builder-build.Dockerfile" --build-arg APP_IMAGE="$IMAGE" -t "$IMAGE" "$SOURCECODE_WORK_DIR"; then + dokku_log_warn "Failure injecting docker labels and custom entrypoint on image" + return 1 + fi + + plugn trigger post-build "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR" + popd &>/dev/null || pushd "/tmp" >/dev/null +} + +trigger-builder-railpack-builder-build "$@" diff --git a/plugins/builder-railpack/builder-detect b/plugins/builder-railpack/builder-detect new file mode 100755 index 000000000..1ba0b89ba --- /dev/null +++ b/plugins/builder-railpack/builder-detect @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/config/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-builder-detect() { + declare desc="builder-railpack builder-detect plugin trigger" + declare trigger="builder-detect" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + + if [[ -f "$SOURCECODE_WORK_DIR/railpack.json" ]]; then + echo "railpack" + return + fi +} + +trigger-builder-railpack-builder-detect "$@" diff --git a/plugins/builder-railpack/builder-release b/plugins/builder-railpack/builder-release new file mode 100755 index 000000000..7c3650421 --- /dev/null +++ b/plugins/builder-railpack/builder-release @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-builder-release() { + declare desc="builder-railpack builder-release plugin trigger" + declare trigger="builder-release" + declare BUILDER_TYPE="$1" APP="$2" IMAGE_TAG="$3" + + if [[ "$BUILDER_TYPE" != "railpack" ]]; then + return + fi + + local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") + plugn trigger pre-release-builder "$BUILDER_TYPE" "$APP" "$IMAGE" + + TMP_WORK_DIR="$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")" + trap "rm -rf '$TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT + + local DOCKER_BUILD_LABEL_ARGS="--label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=com.dokku.app-name=$APP --label=com.dokku.image-stage=release --label=dokku" + if ! suppress_output "$DOCKER_BIN" image build "${DOCKER_BUILD_LABEL_ARGS[@]}" $DOKKU_GLOBAL_BUILD_ARGS -f "$PLUGIN_AVAILABLE_PATH/builder-railpack/dockerfiles/builder-release.Dockerfile" --build-arg APP_IMAGE="$IMAGE" -t "$IMAGE" "$TMP_WORK_DIR"; then + dokku_log_warn "Failure injecting docker labels on image" + return 1 + fi + + plugn trigger post-release-builder "$BUILDER_TYPE" "$APP" "$IMAGE" +} + +trigger-builder-railpack-builder-release "$@" diff --git a/plugins/builder-railpack/commands b/plugins/builder-railpack/commands new file mode 100755 index 000000000..db753d311 --- /dev/null +++ b/plugins/builder-railpack/commands @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/help-functions" + +case "$1" in + help | builder-railpack:help) + cmd-builder-railpack-help "$@" + ;; + + *) + exit "$DOKKU_NOT_IMPLEMENTED_EXIT" + ;; + +esac diff --git a/plugins/builder-railpack/core-post-extract b/plugins/builder-railpack/core-post-extract new file mode 100755 index 000000000..ad863c076 --- /dev/null +++ b/plugins/builder-railpack/core-post-extract @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-core-post-extract() { + declare desc="builder-railpack post-extract plugin trigger" + declare trigger="post-extract" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + local NEW_RAILPACK_JSON="$(fn-builder-railpack-computed-railpackjson-path "$APP")" + + pushd "$TMP_WORK_DIR" >/dev/null + + if [[ -z "$NEW_RAILPACK_JSON" ]]; then + return + fi + + if [[ ! -f "$NEW_RAILPACK_JSON" ]]; then + rm -f railpack.json + return + fi + + if [[ "$NEW_RAILPACK_JSON" != "railpack.json" ]]; then + mv "$NEW_RAILPACK_JSON" railpack.json + fi + popd &>/dev/null || pushd "/tmp" >/dev/null +} + +trigger-builder-railpack-core-post-extract "$@" diff --git a/plugins/builder-railpack/dockerfiles/builder-build.Dockerfile b/plugins/builder-railpack/dockerfiles/builder-build.Dockerfile new file mode 100644 index 000000000..57aa80d1c --- /dev/null +++ b/plugins/builder-railpack/dockerfiles/builder-build.Dockerfile @@ -0,0 +1,7 @@ +ARG APP_IMAGE +FROM $APP_IMAGE + +RUN printf '#!/usr/bin/env bash\nexec bash -l -c -- \"$*\"\n' > /usr/local/bin/entrypoint && \ + chmod +x /usr/local/bin/entrypoint + +ENTRYPOINT ["/usr/local/bin/entrypoint"] diff --git a/plugins/builder-railpack/dockerfiles/builder-release.Dockerfile b/plugins/builder-railpack/dockerfiles/builder-release.Dockerfile new file mode 100644 index 000000000..cca256f21 --- /dev/null +++ b/plugins/builder-railpack/dockerfiles/builder-release.Dockerfile @@ -0,0 +1,2 @@ +ARG APP_IMAGE +FROM $APP_IMAGE diff --git a/plugins/builder-railpack/help-functions b/plugins/builder-railpack/help-functions new file mode 100755 index 000000000..ef1811401 --- /dev/null +++ b/plugins/builder-railpack/help-functions @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-help() { + declare desc="help command" + declare CMD="$1" + local plugin_name="builder-railpack" + local plugin_description="Manage the railpack builder integration for an app" + + if [[ "$CMD" == "${plugin_name}:help" ]]; then + echo -e "Usage: dokku ${plugin_name}[:COMMAND]" + echo '' + echo "$plugin_description" + echo '' + echo 'Additional commands:' + fn-help-content | sort | column -c2 -t -s, + elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then + fn-help-content + else + cat <] [], Displays a builder-railpack report for one or more apps + builder-railpack:set (), Set or clear a builder-railpack property for an app +help_content +} diff --git a/plugins/builder-railpack/install b/plugins/builder-railpack/install new file mode 100755 index 000000000..283188a33 --- /dev/null +++ b/plugins/builder-railpack/install @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-install() { + declare desc="installs the builder-railpack plugin" + declare trigger="install" + + fn-plugin-property-setup "builder-railpack" + + mkdir -p "${DOKKU_LIB_ROOT}/data/builder-railpack" + chown -R "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "${DOKKU_LIB_ROOT}/data/builder-railpack" +} + +trigger-builder-railpack-install "$@" diff --git a/plugins/builder-railpack/internal-functions b/plugins/builder-railpack/internal-functions new file mode 100755 index 000000000..a405b2ed0 --- /dev/null +++ b/plugins/builder-railpack/internal-functions @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-report() { + declare desc="displays a builder-railpack report for one or more apps" + declare cmd="builder-railpack:report" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" INFO_FLAG="$2" + + if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then + INFO_FLAG="$APP" + APP="" + fi + + if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then + INFO_FLAG="true" + fi + + if [[ -z "$APP" ]]; then + for app in $(dokku_apps); do + cmd-builder-railpack-report-single "$app" "$INFO_FLAG" | tee || true + done + else + cmd-builder-railpack-report-single "$APP" "$INFO_FLAG" + fi +} + +cmd-builder-railpack-report-single() { + declare APP="$1" INFO_FLAG="$2" + if [[ "$INFO_FLAG" == "true" ]]; then + INFO_FLAG="" + fi + verify_app_name "$APP" + local flag_map=( + "--builder-railpack-computed-railpackjson-path: $(fn-builder-railpack-computed-railpackjson-path "$APP")" + "--builder-railpack-global-railpackjson-path: $(fn-builder-railpack-global-railpackjson-path "$APP")" + "--builder-railpack-railpackjson-path: $(fn-builder-railpack-railpackjson-path "$APP")" + "--builder-railpack-computed-no-cache: $(fn-builder-railpack-computed-no-cache "$APP")" + "--builder-railpack-global-no-cache: $(fn-builder-railpack-global-no-cache "$APP")" + "--builder-railpack-no-cache: $(fn-builder-railpack-no-cache "$APP")" + ) + + if [[ -z "$INFO_FLAG" ]]; then + dokku_log_info2_quiet "${APP} builder-railpack information" + for flag in "${flag_map[@]}"; do + key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')" + dokku_log_verbose "$(printf "%-30s %-25s" "${key^}" "${flag#*: }")" + done + else + local match=false + local value_exists=false + for flag in "${flag_map[@]}"; do + valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)" + if [[ "$flag" == "${INFO_FLAG}:"* ]]; then + value=${flag#*: } + size="${#value}" + if [[ "$size" -ne 0 ]]; then + echo "$value" && match=true && value_exists=true + else + match=true + fi + fi + done + [[ "$match" == "true" ]] || dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}" + [[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed" + fi +} + +fn-builder-railpack-computed-railpackjson-path() { + declare APP="$1" + + file="$(fn-builder-railpack-railpackjson-path "$APP")" + if [[ "$file" == "" ]]; then + file="$(fn-builder-railpack-global-railpackjson-path "$APP")" + fi + + echo "$file" +} + +fn-builder-railpack-global-railpackjson-path() { + declare APP="$1" + + fn-plugin-property-get "builder-railpack" "--global" "railpackjson-path" "railpack.json" +} + +fn-builder-railpack-railpackjson-path() { + declare APP="$1" + + fn-plugin-property-get "builder-railpack" "$APP" "railpackjson-path" "" +} + +fn-builder-railpack-computed-no-cache() { + declare APP="$1" + + file="$(fn-builder-railpack-no-cache "$APP")" + if [[ "$file" == "" ]]; then + file="$(fn-builder-railpack-global-no-cache "$APP")" + fi + + echo "$file" +} + +fn-builder-railpack-global-no-cache() { + declare APP="$1" + + fn-plugin-property-get "builder-railpack" "--global" "no-cache" "false" +} + +fn-builder-railpack-no-cache() { + declare APP="$1" + + fn-plugin-property-get "builder-railpack" "$APP" "no-cache" "" +} diff --git a/plugins/builder-railpack/plugin.toml b/plugins/builder-railpack/plugin.toml new file mode 100644 index 000000000..d7eceec7d --- /dev/null +++ b/plugins/builder-railpack/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core builder-railpack plugin" +version = "0.36.5" +[plugin.config] diff --git a/plugins/builder-railpack/post-app-clone-setup b/plugins/builder-railpack/post-app-clone-setup new file mode 100755 index 000000000..0ff83fc92 --- /dev/null +++ b/plugins/builder-railpack/post-app-clone-setup @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-post-app-clone-setup() { + declare desc="sets up properties for new app" + declare trigger="post-app-clone-setup" + declare OLD_APP="$1" NEW_APP="$2" + + fn-plugin-property-clone "builder-railpack" "$OLD_APP" "$NEW_APP" +} + +trigger-builder-railpack-post-app-clone-setup "$@" diff --git a/plugins/builder-railpack/post-app-rename-setup b/plugins/builder-railpack/post-app-rename-setup new file mode 100755 index 000000000..10effd4e5 --- /dev/null +++ b/plugins/builder-railpack/post-app-rename-setup @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-post-app-rename-setup() { + declare desc="updates settings for new app" + declare trigger="post-app-rename-setup" + declare OLD_APP="$1" NEW_APP="$2" + + fn-plugin-property-clone "builder-railpack" "$OLD_APP" "$NEW_APP" + fn-plugin-property-destroy "builder-railpack" "$OLD_APP" +} + +trigger-builder-railpack-post-app-rename-setup "$@" diff --git a/plugins/builder-railpack/post-delete b/plugins/builder-railpack/post-delete new file mode 100755 index 000000000..5879fc046 --- /dev/null +++ b/plugins/builder-railpack/post-delete @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-railpack-post-delete() { + declare desc="destroys the builder-railpack properties for a given app" + declare trigger="post-delete" + declare APP="$1" + + fn-plugin-property-destroy "builder-railpack" "$APP" + rm -rf "${DOKKU_LIB_ROOT}/data/builder-railpack/$APP" +} + +trigger-builder-railpack-post-delete "$@" diff --git a/plugins/builder-railpack/report b/plugins/builder-railpack/report new file mode 100755 index 000000000..6fbd0d855 --- /dev/null +++ b/plugins/builder-railpack/report @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-report-single "$@" diff --git a/plugins/builder-railpack/subcommands/default b/plugins/builder-railpack/subcommands/default new file mode 100755 index 000000000..cc0e6748a --- /dev/null +++ b/plugins/builder-railpack/subcommands/default @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/help-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-help "builder-railpack:help" diff --git a/plugins/builder-railpack/subcommands/report b/plugins/builder-railpack/subcommands/report new file mode 100755 index 000000000..9801d5f29 --- /dev/null +++ b/plugins/builder-railpack/subcommands/report @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-railpack/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-report "$@" diff --git a/plugins/builder-railpack/subcommands/set b/plugins/builder-railpack/subcommands/set new file mode 100755 index 000000000..6001f0c6f --- /dev/null +++ b/plugins/builder-railpack/subcommands/set @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-railpack-set() { + declare desc="set or clear a builder-railpack property for an app" + declare cmd="builder-railpack:set" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" KEY="$2" VALUE="$3" + local VALID_KEYS=("railpackjson-path" "no-cache") + [[ "$APP" == "--global" ]] || verify_app_name "$APP" + + [[ -z "$KEY" ]] && dokku_log_fail "No key specified" + + if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then + dokku_log_fail "Invalid key specified, valid keys include: railpackjson-path, no-cache" + fi + + if [[ -n "$VALUE" ]]; then + dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}" + fn-plugin-property-write "builder-railpack" "$APP" "$KEY" "$VALUE" + else + dokku_log_info2_quiet "Unsetting ${KEY}" + fn-plugin-property-delete "builder-railpack" "$APP" "$KEY" + fi +} + +cmd-builder-railpack-set "$@" diff --git a/plugins/docker-options/docker-args-deploy b/plugins/docker-options/docker-args-deploy index fc768a0cf..35ea4bc97 100755 --- a/plugins/docker-options/docker-args-deploy +++ b/plugins/docker-options/docker-args-deploy @@ -47,7 +47,7 @@ trigger-docker-options-docker-args() { *) case "$IMAGE_SOURCE_TYPE" in - dockerfile | nixpacks) + dockerfile | nixpacks | railpack) case "$line" in --link* | -v* | --volume*) continue diff --git a/tests/unit/builder-railpack.bats b/tests/unit/builder-railpack.bats new file mode 100644 index 000000000..017123f0c --- /dev/null +++ b/tests/unit/builder-railpack.bats @@ -0,0 +1,52 @@ +#!/usr/bin/env bats + +load test_helper + +setup_file() { + install_railpack +} + +setup() { + create_app +} + +teardown() { + destroy_app +} + +@test "(builder-railpack:set)" { + run /bin/bash -c "dokku config:set $TEST_APP SECRET_KEY=fjdkslafjdk" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder:set $TEST_APP selected railpack" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_requirements_txt + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains 'load build definition from Dockerfile' + assert_output_contains "SECRET_KEY: fjdkslafjdk" + + run /bin/bash -c "dokku builder-railpack:set $TEST_APP railpackjson-path nonexistent.json" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains 'load build definition from Dockerfile' +} + +inject_requirements_txt() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + echo "flask" >>"$APP_REPO_DIR/requirements.txt" +} diff --git a/tests/unit/report.bats b/tests/unit/report.bats index a5f7271b6..719af8c80 100644 --- a/tests/unit/report.bats +++ b/tests/unit/report.bats @@ -103,6 +103,7 @@ teardown() { builder-herokuish builder-lambda builder-nixpacks + builder-railpack builder-pack buildpacks caddy diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 34ce53bcb..c53880f24 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -660,6 +660,12 @@ install_nixpacks() { fi } +install_railpack() { + if ! command -v "railpack" &>/dev/null; then + curl -sSL https://railpack.com/install.sh | FORCE=1 bash + fi +} + install_k3s() { run /bin/bash -c "dokku proxy:set --global k3s" echo "output: $output"