Docker Community Forums

Share and learn in the Docker community.

Visual Studio Code

Tell me your Development Storys with Visual Studio Code.
I Love VS-Code to build Container and use the Docker on the Terminal.

Actualy VS Code is my prefered ide for many use cases. On my latop, I use it with the kelvin.vscode-sshfs plugin in combination with the KeeAgent plugin in KeePass that serves the certificates for keyauth.

I love it that much, that I have code-server running all the time :slight_smile: I added two helper scripts to a) add git credentials and b) define which plugins I want to have installed in the code-server. The beauty: everything can be used from a remote browser :slight_smile:

my docker-compose.yml for a docker stack deployment:
The value for {CODER_IMAGE} is linuxserver/code-server:latest and the value for {CODER_DATA_DIR} is /config. Everything else are personal informations. The GIT_* environment parameters are specific to a helper script I wrote (see below). Everything else can be found in the dockerhub description of the image.

I do render this “template” compose.yml with envsubst, which replaces the variables with the actual values, and pipe everything to docker stack deploy -c -, which then deploys the rendered docker-compose.yml

---
version: '3.7'

x-deploy:
  &default-deploy
  mode: replicated
  replicas: 1
  restart_policy:
    condition: any

services:

  app:
    image: ${CODER_IMAGE}
    deploy:
      <<: *default-deploy
      labels:
        - traefik.enable=true
        - traefik.docker.network=web
        - traefik.backend=code
        - traefik.web.frontend.rule=Host:${CODER_FQDN}
        - traefik.web.port=8443
        - traefik.wss.protocol=https
        - traefik.wss.frontend.rule=Host:${CODER_FQDN}
        - traefik.wss.protocol=ws
        - traefik.wss.port=8443
        - traefik.frontend.headers.forceSTSHeader=true
        - traefik.frontend.headers.STSSeconds=315360000
        - traefik.frontend.headers.STSPreload=true
        - traefik.frontend.headers.STSIncludeSubdomains=tr
        - traefik.frontend.headers.browserXSSFilter=true
        - traefik.frontend.headers.contentTypeNosniff=true
    networks:
      web: {}
    environment:
      PUID: ${CODER_ENV_PUID}
      PGID: ${CODER_ENV_PGID}
      TZ: ${CODER_ENV_TZ}
      PASSWORD: '${CODER_ENV_PASSWORD}'
      SUDO_PASSWORD: '${CODER_ENV_SUDO_PASSWORD}'
      GIT_USERNAME_1: '${CODER_ENV_GIT_USERNAME_1}'
      GIT_PASSWORD_1: '${CODER_ENV_GIT_PASSWORD_1}'
      GIT_URL_1: '${CODER_ENV_GIT_URL_1}'
      EXTENSIONS: >
        ms-azuretools.vscode-docker
        dotjoshjohnson.xml
        ms-python.python
        redhat.java
        rebornix.ruby
        samuelcolvin.jinjahtml
        eamodio.gitlens
        oderwat.indent-rainbow
        coenraads.bracket-pair-colorizer
        vincaslt.highlight-matching-tag
        ivory-lab.jenkinsfile-support
        vscoss.vscode-ansible
    volumes:
      - type: volume
        source: data
        target: '${CODER_DATA_DIR}'
    configs:
      - source: vs-extensions
        target: ${CODER_DATA_DIR}/custom-cont-init.d/vs-extensions.sh
        uid: '10001'
        gid: '0'
        mode: 444
      - source: git-https-credentials
        target: ${CODER_DATA_DIR}/custom-cont-init.d/vs-git-credentials.sh
        uid: '10001'
        gid: '0'
        mode: 444

configs:
  vs-extensions:
    file: ./vs-extensions.sh
  git-https-credentials:
    file: ./vs-git-credentials.sh

volumes:
  data:
    ...

networks:
  web:
    external: true

vs-extension.sh:

#!/bin/bash
if [ -z "${EXTENSIONS}" ];then
    echo "Environment EXTENSIONS not provided, skipping "
    exit 0
fi
extensions_dir="${HOME}/extensions"
# install missing extensions
for extension in ${EXTENSIONS}; do
    if [ $(ls -1 ${extensions_dir} | grep -wc ${extension}) -eq 0 ];then 
        code-server --install-extension ${extension} --extensions-dir ${extensions_dir}
        echo "extension installed: ${extension}"
    else
        echo "extension present: ${extension}"
    fi
done

# cleanup old version of extension and oprhaned extension.
for installed_extension in $(code-server --extensions-dir ${extensions_dir} --list-extensions); do
    uninstall_extension=true
    for extension in ${EXTENSIONS}; do
        if [ "${installed_extension,,}" == "${extension}" ]; then
            uninstall_extension=false
            # only keep latest versions
            installed_versions=$(ls -1 ${extensions_dir} | grep ${extension})
            latest_version=$(echo "${installed_versions}" | sort | tail -n 1 )
            for version in ${installed_versions}; do
                if [ "${version}" != "${latest_version}" ]; then
                    echo "deleting old extension version: ${version}"
                    rm -rf "${extensions_dir}/${version}"
                fi
            done
            break;
        fi
    done
    if [ "${uninstall_extension}" == "true" ]; then
        code-server --uninstall-extension ${installed_extension} --extensions-dir ${extensions_dir}
        echo "extension deleted: ${installed_extension}"
    fi
done

vs-git-credentials.sh:

#!/bin/bash
rawurlencode() {
    local string="${1}"
    local strlen=${#string}
    local encoded=""
    local pos c o

    for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
            [-_.~a-zA-Z0-9] )
                                o="${c}"
                                ;;
            * )
                                printf -v o '%%%02x' "'$c"
        esac
        encoded+="${o}"
    done
    echo "${encoded}"
}

if [ $(git config --global credential.helper | grep "store" | wc -l) -eq 0 ]; then
    git config --global credential.helper store
fi
rm ~/.git-credentials && touch ~/.git-credentials
for git_url in $(env | grep GIT_URL ); do
    git_var=${git_url%=*}      # extract key
    i=${git_var##*_}  # extract number from key
    if [ "${i}" == "URL" ]; then 
        echo "https://$(rawurlencode ${GIT_USERNAME}):$(rawurlencode ${GIT_PASSWORD})@${GIT_URL}" >> ~/.git-credentials
    elif [ ${i} -gt 0 ]; then
        #inner eval build variable names, outter eval gets the value for the build variable names 
        USERNAME=$(eval echo \${$(eval echo GIT_USERNAME_\${i})})
        PASSWORD=$(eval echo \${$(eval echo GIT_PASSWORD_\${i})})
        URL=$(eval echo \${$(eval echo GIT_URL_\${i})})
        echo "https://$(rawurlencode ${USERNAME}):$(rawurlencode ${PASSWORD})@${URL}" >> ~/.git-credentials
    fi
done

The 2nd helper script parses all GIT_URL_{n} environment entries and fetches GIT_USERNAME_{n} and GIT_PASSWORD_{n} (where {n} is a number) to create as many entries in the ~/.git-credentials file as entries where defined .Of couse if your git server allows keyauth, then you would mount the key instead and do not use this script at all.