Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dockerize aztec-cli #3031

Merged
merged 10 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build_manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ aztec-faucet:
dependencies:
- yarn-project

aztec-cli:
buildDir: yarn-project
projectDir: yarn-project/cli
dependencies:
- yarn-project
Comment on lines +127 to +131
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note on this:
I tried using the sandbox image instead of building a new docker image but two reasons why I didn't do that:
(1) the sandbox image runs yarn focus to trim down the number of dependencies so the cli's deps aren't available
(2) in the bash wrapper I'd have to point the entrypoint to a internal path inside the container (/usr/src/yarn-project/cli/dest/bin/index.js). If in the future the container's internal layout changes then the bash wrapper would stop working


pxe:
buildDir: yarn-project
projectDir: yarn-project/pxe
Expand Down
32 changes: 32 additions & 0 deletions yarn-project/cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project AS yarn-project

# Start a new image as we need arch specific.
FROM node:18-alpine as builder
ARG COMMIT_TAG=""
RUN apk add jq
COPY --from=yarn-project /usr/src/ /usr/src/

WORKDIR /usr/src/yarn-project/cli
RUN if [[ -n "${COMMIT_TAG}" ]]; then \
jq --arg v ${COMMIT_TAG} '.version = $v' package.json > _temp && mv _temp package.json; \
fi

# Productionify. See comment in yarn-project-base/Dockerfile.
RUN yarn cache clean && yarn workspaces focus --production

# Create final, minimal size image.
FROM node:18-alpine

COPY --from=builder /usr/src/ /usr/src/

ENV XDG_CACHE_HOME /cache
RUN mkdir /cache && chmod 777 /cache
VOLUME [ "/cache" ]

# run as non-root user
RUN addgroup -S aztec && adduser -S aztec -G aztec
USER aztec

ENV NODE_OPTIONS "--no-warnings"

ENTRYPOINT ["node", "/usr/src/yarn-project/cli/dest/bin/index.js"]
175 changes: 175 additions & 0 deletions yarn-project/cli/aztec-cli
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env bash

# Wrapper script around @aztec/cli using Docker. This is intended to be used by devs that don't have
# a NodeJS environment setup locally. The script starts a Docker container passing any commands and
# arguments to the CLI running inside the container.
# If this wrapper script detecs a global install of @aztec/cli it falls back on that instead of Docker.

set -euo pipefail

# use node.js aztec-cli if available
# check local node_modules (e.g. @aztec/cli is a dependency in current package.json)
# this doesn't check node_modules hierarchy
if [[ -f "$PWD/node_modules/.bin/aztec-cli" ]]; then
"$PWD/node_modules/.bin/aztec-cli" $@
exit 0
elif command -v npm &> /dev/null; then
# next look for a global install
# e.g. if user has run `npm install -g @aztec/cli` after playing with the cli in dockerized form
GLOBAL_NPM_BIN_DIR=$(npm --global prefix)/bin
if [[ -f "$GLOBAL_NPM_BIN_DIR/aztec-cli" ]]; then
"$GLOBAL_NPM_BIN_DIR/aztec-cli" $@
exit 0
fi
elif command -v yarn &> /dev/null; then
YARN_VER=$(yarn --version)
if [[ "$YARN_VER" =~ ^1\..* ]]; then
GLOBAL_YARN_BIN_DIR=$(yarn --global bin)
if [[ -f "$GLOBAL_YARN_BIN_DIR/aztec-cli" ]]; then
"$GLOBAL_YARN_BIN_DIR/aztec-cli" $@
exit 0
fi
fi
fi

# fallback on docker

CLI_IMAGE=${CLI_IMAGE:-"aztecprotocol/aztec-cli"}
CLI_VERSION=${CLI_VERSION:-"latest"}

DOCKER_PATH=""

# any host bindings we might send to the container
DOCKER_HOST=""

# env vars to pass to the container
DOCKER_ENV=""

# volumes to pass to the container
DOCKER_VOLUME=""

# assume the sandbox is running on listening on the host interface
# unless something else was passed through
DEFAULT_PXE_URL="http://host.docker.internal:8080"
PXE_URL=${PXE_URL:-$DEFAULT_PXE_URL}

# need to know if we're running on macOS or Linux
UNAME=$(uname -s)

if command -v podman &> /dev/null; then
DOCKER_PATH="podman"
elif command -v docker &> /dev/null; then
DOCKER_PATH="docker"
else
echo "No docker or podman found"
exit 1
fi

# set up host.docker.internal alias on Linux, just like it is on mac
if [[ "$UNAME" == "Linux" && "$PXE_URL" == "$DEFAULT_PXE_URL" ]]; then
DOCKER_HOST="$DOCKER_HOST --add-host host.docker.internal:host-gateway"
fi

# Build a list of mount points
function add_mount() {
DIR="$1"

# grab its dirname if its a file
# e.g. passing the file path to a JSON artifact
if [[ -f "$DIR" ]]; then
DIR=$(dirname "$DIR")
fi

if [[ ! -d "$DIR" ]]; then
return
fi

# check if it's already been added
REALDIR=$(realpath $DIR)
if [[ "$DOCKER_VOLUME" =~ "$REALDIR:" ]]; then
return
fi

DOCKER_VOLUME="$DOCKER_VOLUME -v $REALDIR:$REALDIR"
}

# we need to look at which command was run in order to set up mount points
AZTEC_CLI_COMMAND="$1"
AZTEC_CLI_EXTRA_ARGS=""

# first process commands with positional arguments
# assumes positional arguments are the first thing
if [[ "$AZTEC_CLI_COMMAND" == "compile" || "$AZTEC_CLI_COMMAND" == "deploy" || "$AZTEC_CLI_COMMAND" == "inspect-contract" ]]; then
add_mount "$2"
fi

# aztec-cli unbox Token my_token_contract
# if my_token_contract is missing then pass current folder
if [[ "$AZTEC_CLI_COMMAND" == "unbox" ]]; then
DIR="${3:-$PWD}"

if [[ ! -d "$DIR" ]]; then
mkdir -p "$DIR"
fi

add_mount "$DIR"
fi

# process flags
if [[ "$AZTEC_CLI_COMMAND" == "compile" || "$AZTEC_CLI_COMMAND" == "call" || "$AZTEC_CLI_COMMAND" == "send" ]]; then

# bash's builtin getops only works with single characeter flags
# GNU getopt doesn't exist on macOS
# process the flags manually
# NOTE: this won't work with assignement-style flags, e.g. --outdir=/foo
for (( i=2; i <= "$#"; i++ )); do
arg_value=${!i}
next_index=$((i+1))
# edge case: odd number of flags
if [[ "$next_index" -gt "$#" ]]; then
continue
fi
next_arg=${!next_index}
case $arg_value in
-o | --outdir)
add_mount "$next_arg"
;;
-ts | --typescript)
add_mount "$next_arg"
;;
-i | --interface)
add_mount "$next_arg"
;;
-c | --contract-artifact)
add_mount "$next_arg"
;;
*)
;;
esac
done
fi

if [[ "$AZTEC_CLI_COMMAND" == "compile" ]]; then
# can't use Nargo inside the container
AZTEC_CLI_EXTRA_ARGS="$AZTEC_CLI_EXTRA_ARGS --compiler wasm"
fi

DOCKER_ENV="$DOCKER_ENV -e PXE_URL=$PXE_URL"

# pass along any private keys
if [[ ! -z "${PRIVATE_KEY:-}" ]]; then
DOCKER_ENV="$DOCKER_ENV -e PRIVATE_KEY=$PRIVATE_KEY"
fi

DOCKER_VOLUME="$DOCKER_VOLUME -v cache:/cache"

DOCKER_CMD="$DOCKER_PATH run \
--rm \
--user $(id -u):$(id -g) \
--workdir \"$PWD\" \
$DOCKER_HOST \
$DOCKER_ENV \
$DOCKER_VOLUME \
$CLI_IMAGE:$CLI_VERSION $@ $AZTEC_CLI_EXTRA_ARGS"

eval "$DOCKER_CMD"