diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..0d62f54 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,35 @@ +# Find the Dockerfile for mcr.microsoft.com/azure-functions/dotnet:3.0-dotnet3-core-tools at this URL +# https://github.com/Azure/azure-functions-docker/blob/main/host/3.0/buster/amd64/dotnet/dotnet-core-tools.Dockerfile +FROM mcr.microsoft.com/azure-functions/dotnet:3.0-dotnet3-core-tools + +# Uncomment following lines If you want to enable Development Container Script +# For more details https://github.com/microsoft/vscode-dev-containers/tree/main/script-library + +# Avoid warnings by switching to noninteractive +# ENV DEBIAN_FRONTEND=noninteractive + +# # Comment out these lines if you want to use zsh. + +ARG INSTALL_ZSH=false +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +RUN apt-get update && curl -ssL https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh -o /tmp/common-script.sh \ + && /bin/bash /tmp/common-script.sh "$INSTALL_ZSH" "$USERNAME" "$USER_UID" "$USER_GID" \ + && rm /tmp/common-script.sh + +# Setup .NET 5 & 6 SDKs +RUN wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +RUN dpkg -i packages-microsoft-prod.deb +RUN rm packages-microsoft-prod.deb +RUN apt-get update \ + && apt-get install -y apt-transport-https \ + && apt-get update \ + && apt-get install -y dotnet-sdk-5.0 \ + && apt-get install -y dotnet-sdk-6.0 + +# Install Chrome for Desktop Testing through VNC +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && curl -sSL https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o /tmp/chrome.deb \ + && apt-get -y install /tmp/chrome.deb \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f34aad8 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,42 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/azure-functions-dotnetcore-3.1 +{ + "name": "TwoWeeksReady", + "dockerFile": "Dockerfile", + "forwardPorts": [ 7071, 6080, 5901 ], + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp", + "azurite.azurite", + //"ms-azuretools.vscode-cosmosdb" + // unable to get preview extension working through devcontainer emulator due to certificate error, but works with deprecated feature in Azure Storage Explorer + ], + + "features": { + "docker-in-docker": { + "version": "latest", + "moby": true + }, + "node": { + "version": "lts", + "nodeGypDependencies": true + }, + "desktop-lite": { + "password": "vscode", + "webPort": "6080", + "vncPort": "5901" + }, + "github-cli": "latest" + }, + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bash -i tools/first-build.sh", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.devcontainer/library-scripts/docker-in-docker.sh b/.devcontainer/library-scripts/docker-in-docker.sh new file mode 100644 index 0000000..904fe0d --- /dev/null +++ b/.devcontainer/library-scripts/docker-in-docker.sh @@ -0,0 +1,324 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./docker-in-docker-debian.sh [enable non-root docker access flag] [non-root user] [use moby] [Engine/CLI Version] + +ENABLE_NONROOT_DOCKER=${1:-"true"} +USERNAME=${2:-"automatic"} +USE_MOBY=${3:-"true"} +DOCKER_VERSION=${4:-"latest"} # The Docker/Moby Engine + CLI should match in version +MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" +DOCKER_DASH_COMPOSE_VERSION="1" + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +# Get central common setting +get_common_setting() { + if [ "${common_settings_file_loaded}" != "true" ]; then + curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." + common_settings_file_loaded=true + fi + if [ -f "/tmp/vsdc-settings.env" ]; then + local multi_line="" + if [ "$2" = "true" ]; then multi_line="-z"; fi + local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" + if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi + fi + echo "$1=${!1}" +} + +# Function to run apt-get if needed +apt_get_update_if_needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install --no-install-recommends "$@" + fi +} + +# Figure out correct version of a three part version number is not passed +find_version_from_git_tags() { + local variable_name=$1 + local requested_version=${!variable_name} + if [ "${requested_version}" = "none" ]; then return; fi + local repository=$2 + local prefix=${3:-"tags/v"} + local separator=${4:-"."} + local last_part_optional=${5:-"false"} + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then + local escaped_separator=${separator//./\\.} + local last_part + if [ "${last_part_optional}" = "true" ]; then + last_part="(${escaped_separator}[0-9]+)?" + else + last_part="${escaped_separator}[0-9]+" + fi + local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" + else + set +e + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" + set -e + fi + fi + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 + exit 1 + fi + echo "${variable_name}=${!variable_name}" +} + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install dependencies +check_packages apt-transport-https curl ca-certificates lxc pigz iptables gnupg2 dirmngr +if ! type git > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install git +fi + +# Swap to legacy iptables for compatibility +if type iptables-legacy > /dev/null 2>&1; then + update-alternatives --set iptables /usr/sbin/iptables-legacy + update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy +fi + +# Source /etc/os-release to get OS info +. /etc/os-release +# Fetch host/container arch. +architecture="$(dpkg --print-architecture)" + +# Set up the necessary apt repos (either Microsoft's or Docker's) +if [ "${USE_MOBY}" = "true" ]; then + + # Name of open source engine/cli + engine_package_name="moby-engine" + cli_package_name="moby-cli" + + # Import key safely and import Microsoft apt repo + get_common_setting MICROSOFT_GPG_KEYS_URI + curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg + echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list +else + # Name of licensed engine/cli + engine_package_name="docker-ce" + cli_package_name="docker-ce-cli" + + # Import key safely and import Docker apt repo + curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list +fi + +# Refresh apt lists +apt-get update + +# Soft version matching +if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then + # Empty, meaning grab whatever "latest" is in apt repo + engine_version_suffix="" + cli_version_suffix="" +else + # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) + docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" + docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" + # Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/ + docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" + set +e # Don't exit if finding version fails - will handle gracefully + cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" + engine_version_suffix="=$(apt-cache madison ${engine_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" + set -e + if [ -z "${engine_version_suffix}" ] || [ "${engine_version_suffix}" = "=" ] || [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ] ; then + echo "(!) No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" + apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' + exit 1 + fi + echo "engine_version_suffix ${engine_version_suffix}" + echo "cli_version_suffix ${cli_version_suffix}" +fi + +# Install Docker / Moby CLI if not already installed +if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then + echo "Docker / Moby CLI and Engine already installed." +else + if [ "${USE_MOBY}" = "true" ]; then + apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx moby-engine${engine_version_suffix} + apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." + else + apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} docker-ce${engine_version_suffix} + fi +fi + +echo "Finished installing docker / moby!" + +# Install Docker Compose if not already installed and is on a supported architecture +if type docker-compose > /dev/null 2>&1; then + echo "Docker Compose already installed." +else + target_compose_arch="${architecture}" + if [ "${target_compose_arch}" = "amd64" ]; then + target_compose_arch="x86_64" + fi + if [ "${target_compose_arch}" != "x86_64" ]; then + # Use pip to get a version that runns on this architecture + if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install python3-minimal python3-pip libffi-dev python3-venv + fi + export PIPX_HOME=/usr/local/pipx + mkdir -p ${PIPX_HOME} + export PIPX_BIN_DIR=/usr/local/bin + export PYTHONUSERBASE=/tmp/pip-tmp + export PIP_CACHE_DIR=/tmp/pip-tmp/cache + pipx_bin=pipx + if ! type pipx > /dev/null 2>&1; then + pip3 install --disable-pip-version-check --no-cache-dir --user pipx + pipx_bin=/tmp/pip-tmp/bin/pipx + fi + ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose + rm -rf /tmp/pip-tmp + else + # Only supports docker-compose v1 + find_version_from_git_tags DOCKER_DASH_COMPOSE_VERSION "https://github.com/docker/compose" "tags/" + echo "(*) Installing docker-compose ${DOCKER_DASH_COMPOSE_VERSION}..." + curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_DASH_COMPOSE_VERSION}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + fi +fi + +# If init file already exists, exit +if [ -f "/usr/local/share/docker-init.sh" ]; then + echo "/usr/local/share/docker-init.sh already exists, so exiting." + exit 0 +fi +echo "docker-init doesnt exist, adding..." + +# Add user to the docker group +if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then + if ! getent group docker > /dev/null 2>&1; then + groupadd docker + fi + + usermod -aG docker ${USERNAME} +fi + +tee /usr/local/share/docker-init.sh > /dev/null \ +<< 'EOF' +#!/bin/sh +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +set -e + +dockerd_start="$(cat << 'INNEREOF' + # explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly + # ie: docker kill + find /run /var/run -iname 'docker*.pid' -delete || : + find /run /var/run -iname 'container*.pid' -delete || : + + ## Dind wrapper script from docker team, adapted to a function + # Maintained: https://github.com/moby/moby/blob/master/hack/dind + + export container=docker + + if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then + mount -t securityfs none /sys/kernel/security || { + echo >&2 'Could not mount /sys/kernel/security.' + echo >&2 'AppArmor detection and --privileged mode might break.' + } + fi + + # Mount /tmp (conditionally) + if ! mountpoint -q /tmp; then + mount -t tmpfs none /tmp + fi + + # cgroup v2: enable nesting + if [ -f /sys/fs/cgroup/cgroup.controllers ]; then + # move the processes from the root group to the /init group, + # otherwise writing subtree_control fails with EBUSY. + # An error during moving non-existent process (i.e., "cat") is ignored. + mkdir -p /sys/fs/cgroup/init + xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : + # enable controllers + sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ + > /sys/fs/cgroup/cgroup.subtree_control + fi + ## Dind wrapper over. + + # Handle DNS + set +e + cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' + if [ $? -eq 0 ] + then + echo "Setting dockerd Azure DNS." + CUSTOMDNS="--dns 168.63.129.16" + else + echo "Not setting dockerd DNS manually." + CUSTOMDNS="" + fi + set -e + + # Start docker/moby engine + ( dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) & +INNEREOF +)" + +# Start using sudo if not invoked as root +if [ "$(id -u)" -ne 0 ]; then + sudo /bin/sh -c "${dockerd_start}" +else + eval "${dockerd_start}" +fi + +set +e + +# Execute whatever commands were passed in (if any). This allows us +# to set this script to ENTRYPOINT while still executing the default CMD. +exec "$@" +EOF + +chmod +x /usr/local/share/docker-init.sh +chown ${USERNAME}:root /usr/local/share/docker-init.sh \ No newline at end of file diff --git a/.devcontainer/library-scripts/node-debian.sh b/.devcontainer/library-scripts/node-debian.sh new file mode 100644 index 0000000..5394a8f --- /dev/null +++ b/.devcontainer/library-scripts/node-debian.sh @@ -0,0 +1,169 @@ +#!/bin/bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/node.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./node-debian.sh [directory to install nvm] [node version to install (use "none" to skip)] [non-root user] [Update rc files flag] [install node-gyp deps] + +export NVM_DIR=${1:-"/usr/local/share/nvm"} +export NODE_VERSION=${2:-"lts"} +USERNAME=${3:-"automatic"} +UPDATE_RC=${4:-"true"} +INSTALL_TOOLS_FOR_NODE_GYP="${5:-true}" +export NVM_VERSION="0.38.0" + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Ensure that login shells get the correct path if the user updated the PATH using ENV. +rm -f /etc/profile.d/00-restore-env.sh +echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh +chmod +x /etc/profile.d/00-restore-env.sh + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +updaterc() { + if [ "${UPDATE_RC}" = "true" ]; then + echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..." + if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then + echo -e "$1" >> /etc/bash.bashrc + fi + if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then + echo -e "$1" >> /etc/zsh/zshrc + fi + fi +} + +# Function to run apt-get if needed +apt_get_update_if_needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install --no-install-recommends "$@" + fi +} + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install dependencies +check_packages apt-transport-https curl ca-certificates tar gnupg2 dirmngr + +# Install yarn +if type yarn > /dev/null 2>&1; then + echo "Yarn already installed." +else + # Import key safely (new method rather than deprecated apt-key approach) and install + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor > /usr/share/keyrings/yarn-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list + apt-get update + apt-get -y install --no-install-recommends yarn +fi + +# Adjust node version if required +if [ "${NODE_VERSION}" = "none" ]; then + export NODE_VERSION= +elif [ "${NODE_VERSION}" = "lts" ]; then + export NODE_VERSION="lts/*" +fi + +# Create a symlink to the installed version for use in Dockerfile PATH statements +export NVM_SYMLINK_CURRENT=true + +# Install the specified node version if NVM directory already exists, then exit +if [ -d "${NVM_DIR}" ]; then + echo "NVM already installed." + if [ "${NODE_VERSION}" != "" ]; then + su ${USERNAME} -c ". $NVM_DIR/nvm.sh && nvm install ${NODE_VERSION} && nvm clear-cache" + fi + exit 0 +fi + +# Create nvm group, nvm dir, and set sticky bit +if ! cat /etc/group | grep -e "^nvm:" > /dev/null 2>&1; then + groupadd -r nvm +fi +umask 0002 +usermod -a -G nvm ${USERNAME} +mkdir -p ${NVM_DIR} +chown :nvm ${NVM_DIR} +chmod g+s ${NVM_DIR} +su ${USERNAME} -c "$(cat << EOF + set -e + umask 0002 + # Do not update profile - we'll do this manually + export PROFILE=/dev/null + curl -so- https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash + source ${NVM_DIR}/nvm.sh + if [ "${NODE_VERSION}" != "" ]; then + nvm alias default ${NODE_VERSION} + fi + nvm clear-cache +EOF +)" 2>&1 +# Update rc files +if [ "${UPDATE_RC}" = "true" ]; then +updaterc "$(cat < /dev/null 2>&1; then + to_install="${to_install} make" + fi + if ! type gcc > /dev/null 2>&1; then + to_install="${to_install} gcc" + fi + if ! type g++ > /dev/null 2>&1; then + to_install="${to_install} g++" + fi + if ! type python3 > /dev/null 2>&1; then + to_install="${to_install} python3-minimal" + fi + if [ ! -z "${to_install}" ]; then + apt_get_update_if_needed + apt-get -y install ${to_install} + fi +fi + +echo "Done!" \ No newline at end of file diff --git a/.gitignore b/.gitignore index cf346a0..0683a89 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ *.userosscache *.sln.docstates +# Azurite files +__azurite_*.json + # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eb91ab2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "azureFunctions.projectSubpath": "api/TwoWeeksReady", + "azureFunctions.deploySubpath": "api/TwoWeeksReady/bin/Release/netcoreapp3.1/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~3", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9660683..7b83460 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,6 +1,7 @@ { "version": "2.0.0", "tasks": [ + // TwoWeeksReady.Admin tasks { "label": "build", "command": "dotnet", @@ -37,6 +38,72 @@ "/consoleloggerparameters:NoSummary" ], "problemMatcher": "$msCompile" + }, + // CosmoDB Emulator tasks + { + "label": "Start CosmoDB Emulator (Docker)", + "command": ". ./tools/CosmosEmulator/start-emulator.sh", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Stop CosmoDB Emulator (Docker)", + "command": ". ./tools/CosmosEmulator/stop-emulator.sh", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "isBackground": true, + "problemMatcher": [] + }, + // Azure Functions (API) Tasks + { + "label": "API (Clean)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api/TwoWeeksReady" + } + }, + { + "label": "API (Build)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "API (Clean)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api/TwoWeeksReady" + } + }, + { + "label": "API (Run)", + "type": "func", + "dependsOn": "API (Build)", + "options": { + "cwd": "${workspaceFolder}/api/TwoWeeksReady/bin/Debug/netcoreapp3.1", + "languageWorkers__node__arguments": "--inspect=5858" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" } ] -} \ No newline at end of file +} diff --git a/2wr-app/package-lock.json b/2wr-app/package-lock.json index 272891f..c137732 100644 --- a/2wr-app/package-lock.json +++ b/2wr-app/package-lock.json @@ -7649,9 +7649,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "dev": true, "funding": [ { @@ -23549,9 +23549,9 @@ } }, "follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "dev": true }, "for-in": { diff --git a/tools/CosmosEmulator/start-emulator.sh b/tools/CosmosEmulator/start-emulator.sh new file mode 100644 index 0000000..887a97b --- /dev/null +++ b/tools/CosmosEmulator/start-emulator.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Setup CosmosDB Docker Emulator +ipaddr="`ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}' | head -n 1`" +docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator + +# start emulator +docker run --detach --rm -p 8081:8081 -p 10251:10251 -p 10252:10252 -p 10253:10253 -p 10254:10254 -m 3g --cpus=2.0 --name=test-linux-emulator -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10 -e AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=true -e AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE=$ipaddr -it mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator & + +# wait for process to complete and server to start +wait +sleep 120s # necessary wait time for the cosmosdb server to start up and start responding to web requests + +# install the certificate +curl --insecure --url https://localhost:8081/_explorer/emulator.pem -o ~/emulatorcert.crt +sudo cp ~/emulatorcert.crt /usr/local/share/ca-certificates/emulatorcert.crt +sudo update-ca-certificates + +# setup database tables +pushd ./tools/CosmosEmulator +dotnet run /setup +popd diff --git a/tools/CosmosEmulator/stop-emulator.sh b/tools/CosmosEmulator/stop-emulator.sh new file mode 100644 index 0000000..95d777d --- /dev/null +++ b/tools/CosmosEmulator/stop-emulator.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# stop docker process +docker stop test-linux-emulator + +# remove SSL certificate +sudo rm /usr/local/share/ca-certificates/emulatorcert.crt +sudo update-ca-certificates \ No newline at end of file diff --git a/tools/first-build.sh b/tools/first-build.sh new file mode 100644 index 0000000..9926a2c --- /dev/null +++ b/tools/first-build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# restore and build dotnet projects (api & admin) +dotnet restore ./TwoWeeksReady.Common +dotnet restore ./api/api.sln +dotnet restore ./admin/admin.sln +dotnet build ./TwoWeeksReady.Common +dotnet build ./api/api.sln +dotnet build ./admin/admin.sln + +# restore Vue app (2wr-app) +pushd ./2wr-app +npm install +npm install -g azurite +npx playwright install +npm run build +popd + +# setup default configuration files +bash ./tools/setup-config.sh > /dev/null \ No newline at end of file diff --git a/tools/samples/local.settings.sample.json b/tools/samples/local.settings.sample.json new file mode 100644 index 0000000..322b1e8 --- /dev/null +++ b/tools/samples/local.settings.sample.json @@ -0,0 +1,15 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "OidcApiAuthSettings:Audience": "https://2wrdev.azurewebsites.net", + "OidcApiAuthSettings:IssuerUrl": "https://login.2wr.org/" + }, + "ConnectionStrings": { + "CosmosDBConnection": "AccountEndpoint=https://localhost:8081/;AccountKey=YOURLOCALACCOUNTKEY", + "StorageConnection": "UseDevelopmentStorage=true" + }, + "Host":{ + "CORS": "*" + } +} \ No newline at end of file diff --git a/tools/setup-config.sh b/tools/setup-config.sh new file mode 100644 index 0000000..847150b --- /dev/null +++ b/tools/setup-config.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# navigate to script directory +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +pushd $SCRIPT_DIR + +# create api settings file from sample +API_SETTINGS_FILE=../api/TwoWeeksReady/local.settings.json +if [[ ! -f $API_SETTINGS_FILE ]] +then +cp ./samples/local.settings.sample.json $API_SETTINGS_FILE +fi + +# create 2wr-app env from sample +APP_SETTINGS_FILE=../2wr-app/.env +if [[ ! -f $APP_SETTINGS_FILE ]] +then +cp ../2wr-app/.env-sample $APP_SETTINGS_FILE +fi +popd \ No newline at end of file