diff --git a/.devcontainer/build.sh b/.devcontainer/build.sh new file mode 100755 index 0000000..ebdf864 --- /dev/null +++ b/.devcontainer/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# This script is meant to be executed within the "postCreateCommand" field in +# .devcontainer/devcontainer.json, i.e., it expects the devcontainer config to install +# necessary folders with source code beforehand. + +set -eo pipefail +set -x + +cd "${HOME}"/catkin_ws/src +./mushr/mushr_utils/install/mushr_install.bash --trivial-only +mushr_noetic run 'cd catkin_ws && catkin build' diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c231c5c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,15 @@ +{ + "image": "osrf/ros:noetic-desktop-full", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "username": "vscode", + "userUid": "1000", + "userGid": "1000" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + }, + "remoteUser": "vscode", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/catkin_ws/src/mushr,type=bind", + "workspaceFolder": "/home/vscode/catkin_ws", + "postCreateCommand": "sudo chown -R vscode:vscode . && ln -s ${containerWorkspaceFolder}/src/mushr/.vscode ${containerWorkspaceFolder} && ./src/mushr/.devcontainer/build.sh" +} diff --git a/.devcontainer/smoke_test.sh b/.devcontainer/smoke_test.sh new file mode 100755 index 0000000..1769ac5 --- /dev/null +++ b/.devcontainer/smoke_test.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +mushr_noetic run 'source ~/.bashrc && roslaunch mushr_sim teleop.launch' > /tmp/output_roslaunch & +sleep 5 +if tail -n 1 /tmp/output_roslaunch | grep -q "Rosbridge WebSocket server started at ws://0.0.0.0:9090"; then + echo "✅ roslaunch-teleop-test passed" +else + cat /tmp/output_roslaunch # print output for debugging purposes + echo "❌ roslaunch-teleop-test failed" + exit 1; +fi + +docker run --rm -p "8080:8080" ghcr.io/foxglove/studio:latest & +sleep 10; +if curl -sSf http://localhost:8080 >/dev/null; then + echo "✅ start-foxglove-studio passed" +else + echo "❌ start-foxglove-studio failed" + exit 1; +fi + +exit 0; \ No newline at end of file diff --git a/.github/workflows/devcontainer-test.sh b/.github/workflows/devcontainer-test.sh new file mode 100755 index 0000000..a04022c --- /dev/null +++ b/.github/workflows/devcontainer-test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +SRC_DIR="$(pwd)" + +echo "Running Smoke Test" + +# Build +export DOCKER_BUILDKIT=1 +echo "(*) Installing @devcontainer/cli" +npm install -g @devcontainers/cli + +echo "Building Dev Container" +ID_LABEL="test-container=mushr" +devcontainer up --id-label ${ID_LABEL} --workspace-folder "${SRC_DIR}" + +# Test +devcontainer exec --workspace-folder "${SRC_DIR}" --id-label ${ID_LABEL} /bin/sh -c './src/mushr/.devcontainer/smoke_test.sh' + +# Clean up +docker rm -f $(docker container ls -f "label=${ID_LABEL}" -q) diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml new file mode 100644 index 0000000..2cb966d --- /dev/null +++ b/.github/workflows/test-pr.yaml @@ -0,0 +1,16 @@ +name: "CI" +on: + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout main + id: checkout_release + uses: actions/checkout@v3 + - name: Devcontainer container smoke test + id: test_devcontainer + shell: bash + run: ./.github/workflows/devcontainer-test.sh \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3e0f4f0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "tasks": [ + { + "label": "Roslaunch teleop.launch", + "type": "shell", + "command": "mushr_noetic run 'source ~/.bashrc && roslaunch mushr_sim teleop.launch'", + "problemMatcher": [] + }, + { + "label": "Start Foxglove Studio server", + "type": "shell", + "command": "docker run --rm -p \"8080:8080\" ghcr.io/foxglove/studio:latest", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/mushr_utils/install/mushr_install.bash b/mushr_utils/install/mushr_install.bash index c5e4f08..7b8bf8d 100755 --- a/mushr_utils/install/mushr_install.bash +++ b/mushr_utils/install/mushr_install.bash @@ -1,6 +1,15 @@ #!/bin/bash pushd `dirname $0` +# Parse args +TRIVIAL_ONLY=0 +for arg in "$@"; do + if [ "$arg" == "--trivial-only" ]; then + TRIVIAL_ONLY=1 + break + fi +done + # Detect OS export MUSHR_OS_TYPE="$(uname -s)" @@ -12,8 +21,12 @@ fi export MUSHR_INSTALL_PATH=$(pwd) # Real robot or on a laptop? -read -p "Are you installing on robot and need all the sensor drivers? (y/n) " -r -echo +if [[ $TRIVIAL_ONLY == 0 ]]; then + read -p "Are you installing on robot and need all the sensor drivers? (y/n) " -r + echo +else + REPLY="n" +fi if [[ $REPLY =~ ^[Yy]$ ]]; then export MUSHR_REAL_ROBOT=1 export MUSHR_COMPOSE_FILE=docker-compose-robot.yml @@ -23,8 +36,12 @@ else fi # Build from scratch -read -p "Build from scratch? (Not recommended, takes much longer than pulling ready-made image) (y/n) " -r -echo +if [[ $TRIVIAL_ONLY == 0 ]]; then + read -p "Build from scratch? (Not recommended, takes much longer than pulling ready-made image) (y/n) " -r + echo +else + REPLY="n" +fi export BUILD_FROM_SCRATCH=0 if [[ $REPLY =~ ^[Yy]$ ]]; then export BUILD_FROM_SCRATCH=1 @@ -102,15 +119,21 @@ cd $MUSHR_WS_PATH/catkin_ws/src/ && vcs import < mushr/base-repos.yaml && vcs im # Make custom mushr_noetic script cat <<- EOF > ${MUSHR_INSTALL_PATH}/mushr_noetic +#!/bin/bash export MUSHR_INSTALL_PATH=${MUSHR_INSTALL_PATH} export MUSHR_REAL_ROBOT=${MUSHR_REAL_ROBOT} export MUSHR_WS_PATH=${MUSHR_WS_PATH} export MUSHR_COMPOSE_FILE=${MUSHR_COMPOSE_FILE} export MUSHR_OS_TYPE=${MUSHR_OS_TYPE} -if [ \$# == 0 ] || [ \$1 == "run" ]; +if [[ \$# == 0 ]] || [[ \$1 == "run" ]]; then - docker-compose -f \$MUSHR_INSTALL_PATH/\$MUSHR_COMPOSE_FILE run -p 9090:9090 mushr_noetic bash -elif [ \$1 == "build" ]; + if [[ \$# == 2 ]]; + then + docker-compose -f \$MUSHR_INSTALL_PATH/\$MUSHR_COMPOSE_FILE run -p 9090:9090 mushr_noetic bash -ic "\${2}" + else + docker-compose -f \$MUSHR_INSTALL_PATH/\$MUSHR_COMPOSE_FILE run -p 9090:9090 mushr_noetic bash + fi +elif [[ \$1 == "build" ]]; then docker-compose -f $MUSHR_INSTALL_PATH/$MUSHR_COMPOSE_FILE build --no-cache mushr_noetic else