- Jun 2024
-
www.kenmuse.com www.kenmuse.com
-
Sample
.devcontainer/devcontainer.json
:json { "name": "Global", "build": { "context": "..", "dockerfile": "Dockerfile" }, "containerEnv": { "PYTHONPATH": "." }, "customizations": { "vscode": { "settings": { "extensions.verifySignature": false }, "extensions": [ "GitHub.copilot", "ms-python.vscode-pylance", "ms-python.python", "eamodio.gitlens" ] } }, "initializeCommand": "/bin/bash -c '[[ -d ${HOME}/.aws ]] || { echo \"Error: ${HOME}/.aws directory not found.\"; exit 1; }; [[ -f ${HOME}/.netrc ]] || { echo \"Error: ${HOME}/.netrc file not found.\"; exit 1; }; [[ -d ${HOME}/.ssh ]] || { echo \"Error: ${HOME}/.ssh directory not found.\"; exit 1; }; echo \"\n> All required mounts found on the host machine.\"'", "onCreateCommand": { "hadolint": "apt-get update && apt-get install wget -y && wget -O /bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 && chmod u+x /usr/bin/hadolint", "precommit": "pip install pre-commit" }, "updateContentCommand": "/bin/bash -c 'if grep -A 2 \"machine gitlab.com\" ~/.netrc | grep -q \"password\" && GITLAB_TOKEN=$(grep -A 2 \"machine gitlab.com\" ~/.netrc | grep -oP \"(?<=password ).*\" | tr -d \"\\n\") && [ -n \"$GITLAB_TOKEN\" ]; then echo \"\n> Token found in ~/.netrc\"; else read -sp \"\n> Enter your GitLab token: \" GITLAB_TOKEN && echo; fi; echo \"export GITLAB_TOKEN=$GITLAB_TOKEN\" >> ~/.bashrc && . ~/.bashrc && poetry config http-basic.abc __token__ $GITLAB_TOKEN'", "postCreateCommand": ". ~/.bashrc && curl -s --location 'https://gitlab.com/api/v4/projects/12345/repository/files/.pre-commit-config.yaml/raw?ref=main' --header \"PRIVATE-TOKEN: $GITLAB_TOKEN\" -o .pre-commit-config.yaml", "postAttachCommand": "/bin/bash -c '. ~/.bashrc && read -p \"\n> Do you want to update the content of devcontainer.json? (y/n): \" response; if [[ \"$response\" == \"y\" ]]; then curl -s --location \"https://gitlab.com/api/v4/projects/12345/repository/files/devcontainer.json/raw?ref=main\" --header \"PRIVATE-TOKEN: $GITLAB_TOKEN\" -o .devcontainer/devcontainer.json; else echo \"\n> Skipping update of devcontainer.json\"; fi'", "mounts": [ "source=${localEnv:HOME}/.aws/,target=/root/.aws/,type=bind,readonly", "source=${localEnv:HOME}/.netrc,target=/root/.netrc,type=bind,readonly", "source=${localEnv:HOME}/.ssh/,target=/root/.ssh/,type=bind,readonly" ] }
-
Some more of my recent learning with
devcontainer.json
(its Dev Container metadata):- Interactive commands (those waiting for user input like
read
) do not display the input request in (at leastonCreateCommand
andpostCreateCommand
sections), so it is better to keep them inupdateContentCommand
orpostAttachCommand
. - If there are 2
read
commands in a single section, likeupdateContentCommand
, only the 1st one is displayed to the user, and the 2nd one is ignored. - When I put a
read
command within a dictionary (with at lest 2 key/values) ofpostAttachCommand
, the interactive command wasn't being displayed. - We need to use
/bin/bash -c
to be able to useread -s
(the-s
flag) which allows for securely passing the password so that it does not stay in the VS Code console. Also, I had trouble with interactive commands andif
statements without it. - Using
"GITLAB_TOKEN": "${localEnv:GITLAB_TOKEN}"
does not easily work as it is looking forGITLAB_TOKEN
env variable set locally on our host computers, and I believe no one does it. - The dictionary seems to be executing its scripts in parallel; therefore, it is not easily possible to break down long lines which have to execute in a chronological sequence.
- JSON does not allow for human-readable line breaks; therefore, indeed, it seems impossible to improve the long one-liners.
- The files/folders mentioned within
mounts
need to exist locally (otherwise, Docker container build fails). They are mounted before any other section. Technically, we can protect ourselves with the following command to find an extra message in VS Code container logs:
json "initializeCommand": "/bin/bash -c '[[ -d ${HOME}/.aws ]] || { echo \"Error: ${HOME}/.aws directory not found.\"; exit 1; }; [[ -f ${HOME}/.netrc ]] || { echo \"Error: ${HOME}/.netrc file not found.\"; exit 1; }; [[ -d ${HOME}/.ssh ]] || { echo \"Error: ${HOME}/.ssh directory not found.\"; exit 1; }'",
Other option is to get rid of the error completely, but this creates files on the host machine; therefore, it is not an ideal solution:
json "initializeCommand": "mkdir -p ~/.ssh ~/.aws && touch ~/.netrc",
- Interactive commands (those waiting for user input like
-
["bash", "-i", "-c", "read -p 'Type a message: ' -t 10 && echo Attach $REPLY"],
I would also simply put the following:
bash /bin/bash -c 'read -p 'Type a message: ' -t 10 && echo Attach $REPLY'
-
Consequently, it’s one of the only commands that consistently allows interactions with users.
I also found that
updateContentCommand
allows for the user interaction (it displays interactive command in the VS Code console). -
There are six available lifecycle script hooks
Explanation of 6 available
devcontainer.json
(Dev Container in VS Code) hooks.
-