diff --git a/.deepsource.toml b/.deepsource.toml index 3305ce5ad..abc9d2fcb 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -20,4 +20,4 @@ environment = [ name = "docker" [analyzers.meta] -dockerfile_paths = [".devcontainer/experimental/Dockerfile"] +dockerfile_paths = [".devcontainer/Dockerfile"] diff --git a/.devcontainer/.dockerignore b/.devcontainer/.dockerignore deleted file mode 100755 index 93f136199..000000000 --- a/.devcontainer/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -npm-debug.log diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..63b398596 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,21 @@ +# Everything else comes from features. This exists only because fish does not +# need a feature: Debian trixie ships fish 4.0.2, so installing it is one apt +# call, and doing it here bakes it into a cached image layer. +# +# The alternative considered was ghcr.io/devcontainers-extra/features/fish-apt-get, +# which downloads the 20 MB nanolayer binary and runs it as root to invoke a +# second feature that runs exactly the apt call below -- more than twice the +# size of the package it installs, for a package the distribution already has. +FROM mcr.microsoft.com/devcontainers/typescript-node:4-24-trixie + +# DL3008 wants `fish=4.0.2-1`. Pinning an apt package is not the same bargain +# as pinning an npm one: Debian drops superseded versions from the archive, so +# the first security update to fish would leave this asking for a version that +# no longer exists and fail the build outright. Nothing tracks apt pins to +# raise a PR the way renovate does for everything else here. The previous +# Dockerfile suppressed this same rule for the same reason. +# skipcq: DOK-DL3008 +RUN apt-get update \ + && apt-get install -y --no-install-recommends fish \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 000000000..63e042dec --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,19 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/pnpm:2": { + "version": "2.0.5", + "resolved": "ghcr.io/devcontainers-extra/features/pnpm@sha256:694c2b6182435c9e9c06f6071728b087c1181b38c18cecf0defe2ab8c11cddd6", + "integrity": "sha256:694c2b6182435c9e9c06f6071728b087c1181b38c18cecf0defe2ab8c11cddd6" + }, + "ghcr.io/devcontainers/features/git-lfs:1": { + "version": "1.2.5", + "resolved": "ghcr.io/devcontainers/features/git-lfs@sha256:71c2b371cf12ab7fcec47cf17369c6f59156100dad9abf9e4c593049d789de72", + "integrity": "sha256:71c2b371cf12ab7fcec47cf17369c6f59156100dad9abf9e4c593049d789de72" + }, + "ghcr.io/devcontainers/features/java:1": { + "version": "1.8.1", + "resolved": "ghcr.io/devcontainers/features/java@sha256:8157bab2d8d71e40b2f3128c162fab763e2b11038fdd33784549290a5d386b48", + "integrity": "sha256:8157bab2d8d71e40b2f3128c162fab763e2b11038fdd33784549290a5d386b48" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8eee0f1f5..f5634d058 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,57 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, -// see documentation located at the following URL: -// https://github.com/OpenINF/docker-fisher +// see https://containers.dev. { - "name": "🦞 OpenINF GrimesAI Salvage Tex: Lunar (OpenINF Community)", + "name": "OpenINF portal", + + // A stock image plus features. The Dockerfile beside this file adds exactly + // one thing on top of that image -- fish, which trixie packages itself -- and + // is not a return to maintaining a base image. "build": { - "dockerfile": "./experimental/Dockerfile" + "dockerfile": "Dockerfile" + }, + + // No Node version appears in this file on purpose. It lives in package.json's + // `engines.node` and in .nvmrc -- the first is what pnpm enforces, the second + // is what nvm.fish reads for a bare `nvm use`. Both files are needed by their + // own tool, so post-create.sh checks they agree rather than adding a third + // copy here. + "features": { + // pnpm self-manages to the version in `packageManager`, so any recent + // release is enough to bootstrap. + "ghcr.io/devcontainers-extra/features/pnpm:2": {}, + // Required by vnu-jar, which backs `verify.svg` and + // `verify.htmlValidForVNU`. Without it both tasks fail with + // `command not found: java`. + "ghcr.io/devcontainers/features/java:1": { + "version": "21", + "installMaven": false, + "installGradle": false + }, + // .gitattributes routes archives, images and fonts through LFS. + "ghcr.io/devcontainers/features/git-lfs:1": {} + }, + + // browser-sync, started by `pnpm start`, serves on 3000 with its UI on 3001. + "forwardPorts": [3000, 3001], + + // So `dprint`, `biome`, `nps`, `eleventy` and friends work when typed + // directly, not only under `pnpm exec`. The old setup did this with + // `fish_add_path (pnpm bin)` in config.fish, which left every non-fish + // caller -- CI, editor tasks, agents -- without them. Here it applies to the + // whole container, which is also how the workflows do it (they append + // node_modules/.bin to GITHUB_PATH). + "remoteEnv": { + "PATH": "${containerWorkspaceFolder}/node_modules/.bin:${containerEnv:PATH}" }, - "runArgs": [], - // Configure tool-specific properties. + + "postCreateCommand": "bash .devcontainer/post-create.sh", + "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Set *default* container-specific settings.json values on container - // create. "settings": { + // Declared, not just selected. VS Code can usually discover fish from + // /etc/shells on its own, but the previous configuration named it + // explicitly and there is no reason to depend on the discovery. "terminal.integrated.profiles.linux": { "fish": { "path": "fish" @@ -21,7 +59,6 @@ }, "terminal.integrated.defaultProfile.linux": "fish" }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ // https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments "aaron-bond.better-comments", @@ -41,8 +78,6 @@ "KatjanaKosic.vscode-json5", // https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml "redhat.vscode-xml", - // https://marketplace.visualstudio.com/items?itemName=rubocop.vscode-rubocop - // "rubocop.vscode-rubocop", // https://marketplace.visualstudio.com/items?itemName=Shopify.theme-check-vscode "Shopify.theme-check-vscode", // https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker @@ -54,26 +89,6 @@ ] } }, - // Use 'forwardPorts' to make a list of ports inside the container available - // locally. - "forwardPorts": [ - 4000, // Jekyll server - 35729, // Live reload server - 2222 // OpenSSH server - ], - "overrideCommand": false, - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", - "workspaceFolder": "/workspace", - - // Use 'postCreateCommand' to run commands after creating the container. - // https://code.visualstudio.com/docs/devcontainers/create-dev-container#_rebuild - "postCreateCommand": "fish -i '${containerWorkspaceFolder}/tools/devcontainer/post-create.fish'", - "postStartCommand": "fish -i '${containerWorkspaceFolder}/tools/devcontainer/post-start.fish'", - "remoteEnv": { - "PATH": "${containerEnv:PATH}:~/.rbenv/shims/" - }, - // Comment out to connect as root instead. - // More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "vscode" + "remoteUser": "node" } diff --git a/.devcontainer/experimental/Dockerfile b/.devcontainer/experimental/Dockerfile deleted file mode 100644 index e00cc16cf..000000000 --- a/.devcontainer/experimental/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM openinf/grimesai-salvage-tex:lunar - -ARG USERNAME=vscode -ARG USER_UID=1000 -ARG USER_GID=$USER_UID -ARG UPGRADE_PACKAGES="true" - -USER 0 - -# COPY library-scripts/*.sh /tmp/library-scripts/ -# skipcq: DOK-DL3008 -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && /bin/bash /tmp/library-scripts/common-debian.sh "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \ -# && /bin/bash /tmp/library-scripts/fish-debian.sh "${USERNAME}" \ -# && /bin/bash /tmp/library-scripts/sshd-debian.sh "2222" "${USERNAME}" "true" "root" \ -# -# **************************************************************************** -# * TODO: Add any additional OS packages you want included in the definition * -# * here. We want to do this before cleanup to keep the "layer" small. * -# **************************************************************************** - && apt-get -y install --no-install-recommends build-essential default-jre ruby-dev git-lfs \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -USER ${USERNAME} - -# ENV Variables required by Jekyll. -ENV LANG=en_US.UTF-8 \ - LANGUAGE=en_US:en \ - TZ=Etc/UTC \ - LC_ALL=en_US.UTF-8 \ - LANG=en_US.UTF-8 \ - LANGUAGE=en_US - -ENTRYPOINT ["/usr/local/share/ssh-init.sh"] -CMD ["sleep", "infinity"] diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 000000000..bb271de79 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# Copyright (c) The OpenINF Authors & Friends. All rights reserved. +# License: MIT OR Apache-2.0 OR BlueOak-1.0.0 +# ------------------------------------------------------------------------------ +# +# Runs once, after the container is created. +# +# Two Node version managers are set up here on purpose, because they do +# different jobs and neither can do the other's: +# +# nvm.fish - what you use. `nvm use` with no arguments reads .nvmrc, and it +# switches the version for your fish session only, which is what +# you want from an interactive version switch. +# the image's nvm - sets the container-wide baseline. The image puts +# /usr/local/share/nvm/current/bin on the container's PATH and +# sets NVM_SYMLINK_CURRENT, so `nvm use` here repoints a symlink +# that every non-fish process follows: editor tasks, CI, agents. +# +# nvm.fish cannot serve that second role -- it prepends to fish's own PATH and +# keeps its versions under $XDG_DATA_HOME/nvm, so nothing outside fish sees it. +# The image's nvm cannot serve the first -- it is not a fish program. + +set -euo pipefail + +readonly FISHER_VERSION=4.4.8 +readonly NVM_FISH_VERSION=2.2.17 + +# .nvmrc is what nvm.fish reads; engines.node is what pnpm enforces. Both files +# have to exist for their own tool, so the only thing to guard is that they say +# the same thing -- an exact version, since a floating alias like lts/krypton +# resolves to whatever the newest release in that line happens to be and will +# eventually stop matching the exact pin in package.json. +required="$(node -p 'require("./package.json").engines.node')" +declared="$(tr -d '[:space:]' < .nvmrc)" + +if [ "${declared}" != "${required}" ]; then + cat >&2 < Node ${required} (image ships $(node -v))" + +# Sourcing nvm.sh is allowed to fail. It auto-detects .nvmrc in the working +# directory and tries to activate that version as it loads; in a container +# where nothing is installed yet, that resolves to N/A and returns 3. Under +# `set -e` that killed this script before it reached the install below, with no +# message, because the explanation goes through nvm_echo to stdout. +# shellcheck source=/dev/null +. "${NVM_DIR}/nvm.sh" || true + +if ! nvm install "${required}"; then + echo "Failed to install Node ${required} via ${NVM_DIR}." >&2 + exit 1 +fi + +nvm alias default "${required}" +nvm use "${required}" + +if [ "$(node -v)" != "v${required}" ]; then + echo "Expected v${required} after nvm use, got $(node -v)." >&2 + exit 1 +fi + +echo "==> Dependencies" + +# Corepack prompts before fetching the pinned pnpm, which fails where there is +# no terminal to answer it. +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +corepack enable +pnpm install + +# ------------------------------------------------------------------------------ +# fish tooling +# ------------------------------------------------------------------------------ + +echo "==> fish tooling" + +# The old base image shipped fisher; a stock image does not. The curl is +# fisher's own bootstrap: it defines the function in memory, which is then used +# to install fisher properly so it survives into later sessions. Both are +# pinned rather than tracking a branch, since this is remote code being sourced. +# +# nvm.fish comes from upstream. The OpenINF fork it used to come from is +# byte-identical to it -- zero commits ahead or behind -- so the fork bought +# nothing and cost a second source to keep watching. +# +# One `fisher install` for both, rather than one each, so this is a single pass. +fish -c " + curl -fsSL https://raw.githubusercontent.com/jorgebucaran/fisher/${FISHER_VERSION}/functions/fisher.fish | source + and fisher install jorgebucaran/fisher@${FISHER_VERSION} jorgebucaran/nvm.fish@${NVM_FISH_VERSION} +" + +# Install the pinned version into nvm.fish's own store so a bare `nvm use` +# works immediately, and set nvm_default_version, which is what nvm.fish's +# conf.d reads to activate a version in new interactive sessions. `nvm use` +# alone would not do it: it sets a session variable, so it would apply to this +# one throwaway shell and nothing else. +fish -c " + nvm install (cat .nvmrc) + and set --universal nvm_default_version (cat .nvmrc) +" >/dev/null 2>&1 || true + +# gpg needs its keyring to exist before commit signing works. VS Code forwards +# the host's gpg-agent, so no key material is created or copied here. +gpg --list-keys >/dev/null 2>&1 || true + +cat <. That package is an optionalDependency of +# dprint, so pnpm keeps it in the virtual store where the extension cannot see +# it -- which is why the extension used to need a separately installed dprint. +publicHoistPattern: + - '@dprint/*' diff --git a/tools/devcontainer/post-create.fish b/tools/devcontainer/post-create.fish deleted file mode 100644 index e4baf82b1..000000000 --- a/tools/devcontainer/post-create.fish +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env fish -# ------------------------------------------------------------------------------ -# Copyright (c) The OpenINF Authors & Friends. All rights reserved. -# License: MIT OR Apache-2.0 OR BlueOak-1.0.0 -# ------------------------------------------------------------------------------ - -# Set the SHELL environment variable to our active shell. - -set -gx SHELL fish -echo 'set -gx SHELL fish' >> $HOME/.config/fish/config.fish - -# ********************* -# Subroutines -# ********************* - -# function - -# ------------------------------------------------------------------------------ - -source $HOME/.config/fish/config.fish - diff --git a/tools/devcontainer/post-start.fish b/tools/devcontainer/post-start.fish deleted file mode 100644 index 1da3c3390..000000000 --- a/tools/devcontainer/post-start.fish +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env fish -# ------------------------------------------------------------------------------ -# Copyright (c) The OpenINF Authors & Friends. All rights reserved. -# License: MIT OR Apache-2.0 OR BlueOak-1.0.0 -# ------------------------------------------------------------------------------ - -# ********************* -# Requirements -# ********************* - -set -gx SHELL fish -set -gx COREPACK_ENABLE_DOWNLOAD_PROMPT 0 - -# ********************* -# Fisher Plugins -# ********************* - -fisher install OpenINF/openinf-nvm.fish -fisher install OpenINF/openinf-bass - -######################################################################## - -# If there's a `.nvmrc`, then run `nvm install`. -if test -e .nvmrc - # Install the specified version of Node.js. - nvm install -end - -# If there's a package.json, then run `pnpm install`. -if test -e package.json - corepack enable - corepack prepare pnpm@latest --activate - bass pnpm setup >> /dev/null - source $HOME/.config/fish/config.fish - pnpm add -g pnpm - pnpm install -end - -echo 'rbenv rehash && nvm use' >> $HOME/.config/fish/config.fish - -# Installs the dprint utility, and adds it to the user's $PATH. -# It makes the tool available to the user when one opens a new terminal window. - -# Install dprint -bass curl -fsSL https://dprint.dev/install.sh | sh >> /dev/null -set -gx DPRINT_INSTALL $HOME/.dprint -echo 'set -gx DPRINT_INSTALL $HOME/.dprint' >> $HOME/.config/fish/config.fish -fish_add_path -g $DPRINT_INSTALL -echo 'fish_add_path -g $DPRINT_INSTALL' >> $HOME/.config/fish/config.fish - -set -gx DPRINT_HOME $DPRINT_INSTALL/bin -echo 'set -gx DPRINT_HOME $DPRINT_INSTALL/bin' >> $HOME/.config/fish/config.fish -fish_add_path -g $DPRINT_HOME -echo 'fish_add_path -g $DPRINT_HOME' >> $HOME/.config/fish/config.fish - -# Add dir in which executables of local Node deps are linked to the user's $PATH. -echo 'fish_add_path -g (pnpm bin)' >> $HOME/.config/fish/config.fish - -######################################################################## - -# Populate user's $HOME/.gnupg directory with empty keyring files -# it will create the $HOME/.gnupg directory if it does not already exist (expected) -gpg --list-keys - -# If there's a .gnupg directory, then perform the following setup tasks. -if test -e $HOME/.gnupg/ - # Configure Git to use `gpg2`. - echo 'git config --global gpg.program gpg2' >> $HOME/.config/fish/config.fish - - echo 'set -gx GPG_TTY (tty)' >> $HOME/.config/fish/config.fish - - # To fix the " gpg: WARNING: unsafe permissions on homedir - # '/home/path/to/user/.gnupg' " error, make sure that the .gnupg directory and - # its contents is accessibile by your user. - # chown -R (whoami) $HOME/.gnupg/ - - # Also correct the permissions and access rights on the directory. - # chmod 600 $HOME/.gnupg/* - # chmod 700 $HOME/.gnupg - - echo no-autostart >> $HOME/.gnupg/gpg.conf - - # Remove an existing Unix-domain socket file for remote port forwarding before - # creating a new one when gpgtunnel connection is made. - # rm $HOME/.gnupg/S.gpg-agent - - printf '\n%s\n\n\t%s\n\n' 'Enable commit signing:' 'git config --global commit.gpgsign true' -end