Compare commits

..

35 Commits

Author SHA1 Message Date
95b6480323 Revert "feat: add ssh key into memory"
This reverts commit 2b19cccdbc.
2025-05-23 15:14:08 +00:00
2b19cccdbc feat: add ssh key into memory 2025-05-23 12:12:35 +00:00
5483cf71bd feat: remove secproc group and copy start.sh separately 2025-05-23 11:59:44 +00:00
b0c6750585 feat: remove default key paths 2025-05-23 11:59:16 +00:00
fce5d72b19 feat: set mode, remove secproc group and remove start.sh from tarball 2025-05-23 11:59:00 +00:00
bdac248845 chore: add task to update start.sh script in $HOME/ 2025-05-23 09:50:48 +00:00
1c7bc517dd feat: modify UID:GID to 1000:1000 for container 2025-05-22 16:49:48 +00:00
1e8bb0521c Revert "feat: add devuser to group appuser 1003"
This reverts commit d15950f86c.
2025-05-22 16:10:49 +00:00
9577707dd5 fix: add comma between groups 2025-05-21 15:13:20 +00:00
d15950f86c feat: add devuser to group appuser 1003 2025-05-21 15:07:04 +00:00
50907b9519 feat: remove sshd from openssh package 2025-05-21 10:36:38 +00:00
62d6833cdc feat: supress podman-compose warning 2025-05-20 20:59:24 +00:00
e5dc78049a chore: supress podman-compose warning 2025-05-20 20:53:45 +00:00
a3f393b844 feat: add packages podman-compose and podman-docker 2025-05-20 20:42:50 +00:00
fd7e83ccff feat: add environment var CONTAINER_CONNECTION to start up script 2025-05-20 19:54:19 +00:00
bc7d65a24b fix: run podman as user and correct default command 2025-05-20 18:37:17 +00:00
ed3dc4bb8f feat: add podman package and connection settings 2025-05-20 18:31:24 +00:00
9d0c18f94b Revert "feat: add package python-pip"
This reverts commit 1a9023b25f.
2025-05-20 09:14:08 +00:00
7f4913f942 feat: separate out .config installations into separate script 2025-05-20 09:10:09 +00:00
3c25755a27 chore: ignore home.tar.gz 2025-05-20 09:04:25 +00:00
1a9023b25f feat: add package python-pip 2025-05-20 09:03:47 +00:00
1652edccd0 Remove LFS-tracked file home.tar.gz 2025-05-20 09:01:59 +00:00
aa9dfca6c1 feat: add package rust-analyzer 2025-05-20 08:52:50 +00:00
2448ce6ab3 chore: build home.tar.gz
add rust lazy extra
2025-05-20 07:43:04 +00:00
c6503fa9dd feat: add lang.rust lazy extra plugin 2025-05-20 07:39:03 +00:00
1ced08a364 feat: add fish configuration files 2025-05-20 07:38:24 +00:00
44eff8fc7f feat: add continuum plugin to tmux 2025-05-20 06:53:06 +00:00
bfcb9175ec chore: build home.tar.gz 2025-05-20 06:52:29 +00:00
e319884921 feat: add packages rust startship and fortune-mod 2025-05-20 05:53:13 +00:00
7e4aaa2dec chore: correct task command to remove images 2025-05-17 19:33:18 +00:00
f0559a240f chore: update home.tar.gz 2025-05-17 19:30:21 +00:00
a2ea4e6344 feat: remove podman support 2025-05-17 19:27:01 +00:00
bc3db0abae chore: add task to remove images on remote 2025-05-17 18:41:11 +00:00
77435d3748 chore: remove redundant tasks 2025-05-17 16:11:11 +00:00
b7b09db4c5 chore: remove files related to workspace pipelines 2025-05-17 16:10:00 +00:00
19 changed files with 57 additions and 599 deletions

View File

@ -14,5 +14,5 @@ replace_home() {
find .config -type d -exec chmod g+x {} +
replace_home "$PWD" "/home/devuser"
tar -czf home.tar.gz --owner root:0 --group secproc:1002 --xform "s,$PWD,/home/devuser," .config .local .ssh start.sh
tar --mode=a=r,u+w,a+x -czf home.tar.gz --owner root:0 --group root:0 --xform "s,$PWD,/home/devuser," .config .local .ssh
replace_home "/home/devuser" "$PWD"

View File

@ -1,39 +0,0 @@
#!/bin/bash
set -euo pipefail
YAML_FILE="access.yml"
TEMPLATE_FILE="gitconfig.template"
USER="$1"
# Extract user fields from YAML
GIT_NAME=$(yq ".\"$USER\".name" "$YAML_FILE")
GIT_EMAIL=$(yq ".\"$USER\".email" "$YAML_FILE")
# Ensure fields are not empty
if [[ -z "$GIT_NAME" || -z "$GIT_EMAIL" ]]; then
echo "❌ Error: User '$USER' not found or missing name/email in $YAML_FILE"
exit 1
fi
# Create output directory
USER_DIR="files/$USER"
mkdir -p "$USER_DIR"
# Generate .gitconfig
env GIT_NAME="$GIT_NAME" GIT_EMAIL="$GIT_EMAIL" \
envsubst <"$TEMPLATE_FILE" >"$USER_DIR/gitconfig"
echo "✅ .gitconfig created at $USER_DIR/gitconfig"
# Generate SSH keypair if it doesn't exist
KEYFILE="$USER_DIR/id_ed25519"
if [[ -f "$KEYFILE" ]]; then
echo "🔑 SSH key already exists for $USER at $KEYFILE"
else
ssh-keygen -t ed25519 -N "" -C "$GIT_EMAIL" -f "$KEYFILE"
echo "✅ SSH keypair generated at:"
echo " 🔐 Private: $KEYFILE"
echo " 🔓 Public : $KEYFILE.pub"
fi

5
.bin/install-config.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env fish
fisher install patrickf1/fzf.fish jorgebucaran/autopair.fish gazorby/fish-abbreviation-tips jethrokuan/z
echo 'function fish_greeting; fortune; end' >"$HOME"/.config/fish/functions/fish_greeting.fish
echo 'starship init fish | source' >"$HOME"/.config/fish/config.fish

View File

@ -24,7 +24,5 @@ if ! tmux has-session -t "$SESSION" 2>/dev/null; then
tmux new-session -d -s "$SESSION" -n editor 'HOME='"$HOME"' XDG_STATE_HOME='"$HOME/.state"' /usr/bin/fish'
fi
tmux send-keys -t $SESSION:editor 'fisher install jorgebucaran/fisher pure-fish/pure patrickf1/fzf.fish jorgebucaran/autopair.fish gazorby/fish-abbreviation-tips jethrokuan/z' Enter
# Attach to it
exec tmux attach -t "$SESSION"

1
.config/fish/config.fish Normal file
View File

@ -0,0 +1 @@
starship init fish | source

View File

@ -0,0 +1,4 @@
patrickf1/fzf.fish
jorgebucaran/autopair.fish
gazorby/fish-abbreviation-tips
jethrokuan/z

View File

@ -0,0 +1 @@
function fish_greeting; fortune; end

View File

@ -21,6 +21,7 @@
"lazyvim.plugins.extras.lang.json",
"lazyvim.plugins.extras.lang.markdown",
"lazyvim.plugins.extras.lang.python",
"lazyvim.plugins.extras.lang.rust",
"lazyvim.plugins.extras.lang.scala",
"lazyvim.plugins.extras.lang.sql",
"lazyvim.plugins.extras.lang.toml",
@ -35,4 +36,4 @@
"NEWS.md": "10960"
},
"version": 8
}
}

View File

@ -431,9 +431,9 @@ tmux_conf_uninstall_plugins_on_reload=true
# visit https://github.com/tmux-plugins for available plugins
#set -g @plugin 'tmux-plugins/tmux-copycat'
#set -g @plugin 'tmux-plugins/tmux-cpu'
#set -g @plugin 'tmux-plugins/tmux-resurrect'
#set -g @plugin 'tmux-plugins/tmux-continuum'
#set -g @continuum-restore 'on'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
set -g @continuum-restore 'on'
set -g @plugin 'catppuccin/tmux'

7
.gitignore vendored
View File

@ -2,6 +2,11 @@ logs
.local
.cache
.state
.config/fish
.config/fish/*/*
.config/fish/fish_variables
!.config/fish/config.fish
!.config/fish/fish_plugins
!.config/fish/functions/fish_greeting.fish
.npm
.config/nvim/lazy-lock.json
home.tar.gz

View File

@ -1,7 +1,6 @@
Host alps
HostName 10.88.0.1
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
StrictHostKeyChecking yes
Port 2222
@ -10,7 +9,6 @@ Host alps
Host github
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
StrictHostKeyChecking yes
ProxyCommand none

35
.vscode/tasks.json vendored
View File

@ -40,22 +40,6 @@
"problemMatcher": [],
"detail": "Copy Containerfile to $HOME/"
},
{
"label": "GitOps(Update): ssh_router.sh",
"type": "shell",
"command": ".bin/gitops update ssh_router",
"group": "build",
"problemMatcher": [],
"detail": "Copy ssh_router.sh to $HOME/.local/bin/"
},
{
"label": "GitOps(Update): gitops_router.sh",
"type": "shell",
"command": ".bin/gitops update gitops_router",
"group": "build",
"problemMatcher": [],
"detail": "Copy gitops_router.sh to $HOME/.local/bin"
},
{
"label": "GitOps(Update): home.tar.gz",
"type": "shell",
@ -73,12 +57,12 @@
"detail": "Copy gitconfig.template to $HOME/"
},
{
"label": "GitOps(Update): validate_command_access.sh",
"label": "GitOps(Update): start.sh",
"type": "shell",
"command": ".bin/gitops update validate_command",
"command": ".bin/gitops update start.sh",
"group": "build",
"problemMatcher": [],
"detail": "Copy validate_command_access.sh to $HOME/.local/bin"
"detail": "Copy start.sh to $HOME/"
},
{
"label": "Create home tarball",
@ -110,6 +94,13 @@
"problemMatcher": [],
"detail": "run podman rm $args on remote"
},
{
"label": "GitOps: Remove workspace image",
"type": "shell",
"command": ".bin/gitops rmi ${input:images}",
"problemMatcher": [],
"detail": "run podman rmi $args on remote"
},
{
"label": "Cleanup worktree",
"type": "shell",
@ -125,6 +116,12 @@
"description": "Pick a container",
"options": ["pallav", "palak", "param", "darshan"],
"default": "pallav"
},
{
"id": "images",
"type": "promptString",
"description": "space separated list of images",
"default": ""
}
]
}

View File

@ -4,23 +4,22 @@
FROM archlinux:base-devel-20250511.0.348143 as base
ARG DEV_USER=devuser
ARG DEV_UID=1001
ARG DEV_GID=1001
ARG DEV_UID=1000
ARG DEV_GID=1000
# Install all necessary packages and clean up cache
RUN pacman -Sy --noconfirm && \
pacman -S --noconfirm --needed \
base-devel neovim git git-lfs fish tmux go-yq \
nodejs python podman fzf fd ripgrep jdk-openjdk fisher yazi less \
base-devel neovim git git-lfs fish tmux go-yq rust starship podman \
nodejs python fzf fd ripgrep jdk-openjdk fisher yazi less rust-analyzer \
lazygit luarocks python-pynvim npm bash-completion tree-sitter-cli kitty-terminfo \
lua51 openssh && \
lua51 openssh fortune-mod podman-compose podman-docker && \
pacman -Scc --noconfirm && \
rm -rf /var/cache/pacman/pkg/*
rm -rf /var/cache/pacman/pkg/* /usr/bin/sshd /usr/lib/systemd/system/sshd.service
# Create user/groups as per your script, with -l to avoid system user quirks
RUN groupadd -g $DEV_GID $DEV_USER && \
groupadd -g 1002 secproc && \
useradd -l -ms /bin/fish -G secproc -u $DEV_UID -g $DEV_GID $DEV_USER
useradd -l -ms /bin/fish -u $DEV_UID -g $DEV_GID $DEV_USER
# ────────────────────────
# Stage 2: Workspace Image
@ -28,15 +27,19 @@ RUN groupadd -g $DEV_GID $DEV_USER && \
FROM base as workspace
ARG DEV_USER=devuser
ARG DEV_UID=1001
ARG DEV_GID=1001
ARG DEV_UID=1000
ARG DEV_GID=1000
ARG DEV_HOME=/home/$DEV_USER
ARG POD_USER=mypodmanuser
ARG POD_UID=1002
# Use ADD for extracting archives
ADD home.tar.gz $DEV_HOME
COPY --chmod=755 start.sh $DEV_HOME/
# Prepare .ssh and known_hosts, and fix permissions only if dirs exist
RUN mkdir -p $DEV_HOME/.ssh && \
touch /etc/containers/nodocker && \
ssh-keyscan -p 2222 10.88.0.1 >> $DEV_HOME/.ssh/known_hosts && \
ssh-keyscan -p 22 github.com >> $DEV_HOME/.ssh/known_hosts && \
for d in $DEV_HOME/.local \
@ -54,7 +57,10 @@ RUN mkdir -p $DEV_HOME/.ssh && \
done
WORKDIR /app
ENV CONTAINER_HOST=unix:///run/podman/podman.sock
USER $DEV_USER
RUN podman system connection add my-remote --identity $DEV_HOME/.ssh/id_ed25519 \
ssh://$POD_USER@10.88.0.1/run/user/${POD_UID}/podman/podman.sock && \
podman system connection default my-remote
CMD ["/home/devuser/start.sh"]

View File

@ -1,178 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
PERSON="${1:?Missing PERSON argument}"
HOST="alps:3222"
PROTOCOL="http"
REPO=("babbarc/workspaces" "babbarc/workspaces-sec-alps-infilytics")
BRANCH="master"
LOG_FILE="/tmp/.gitops-router-${PERSON}.log"
# ─────────────────────────────────────────────
# ANSI color codes
readonly C_RESET='\033[0m'
readonly C_INFO='\033[1;34m' # bold blue
readonly C_WARN='\033[1;33m' # bold yellow
readonly C_ERROR='\033[1;31m' # bold red
# ─────────────────────────────────────────────
# log <level> <message...> with emojis
log() {
local lvl="${1^^}"
shift
local icon color
case "$lvl" in
INFO) icon="" color="$C_INFO" ;;
WARN) icon="⚠️" color="$C_WARN" ;;
ERROR) icon="❌" color="$C_ERROR" ;;
*) icon="🔹" color="$C_RESET" ;;
esac
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
printf '%b%s [%s] [%s] %s%b\n' \
"$color" "$icon" "$ts" "$lvl" "$*" "$C_RESET" |
tee -a "$LOG_FILE"
}
# ─────────────────────────────────────────────
# Build the raw URL for fetching files
geturl() {
local repo="$1" type="$2" file="$3"
printf '%s://%s/%s/%s/branch/%s/%s\n' \
"$PROTOCOL" "$HOST" "${REPO[$repo]}" "$type" "$BRANCH" "$file"
}
# ─────────────────────────────────────────────
# Run a local script
run() {
local script="$1"
"$HOME/.local/bin/$script"
}
# ─────────────────────────────────────────────
# Download & install an artifact
# update <repo> <file> <target-dir> <mode> [<type>]
update() {
local repo="$1" file="$2" dir="$3" mode="$4" type="${5:-raw}"
local url out
out="$HOME/$dir/$(basename "$file")"
url="$(geturl "$repo" "$type" "$file")"
[[ -f "$out" ]] && chmod 700 "$out"
if curl -fsSL "$url" -o "$out"; then
log INFO "Downloaded $url$out"
chmod "$mode" "$out"
else
log ERROR "Failed to download $url"
return 1
fi
}
# ─────────────────────────────────────────────
# Clean up dangling podman images
clean_images() {
local dangling
dangling="$(podman images -f dangling=true -q)"
if [[ -z "$dangling" ]]; then
log INFO "No dangling images to remove."
else
log WARN "Removing dangling images..."
echo "$dangling" | xargs podman rmi
log INFO "Dangling images removed."
fi
}
# ─────────────────────────────────────────────
# Remove host podman containers
remove_containers() {
local tokens=("$@")
local flags=() patterns=() containers=()
local valid='^[A-Za-z0-9._-]+$'
# allow unmatched globs to disappear
shopt -s nullglob
# separate flags (-f, etc.) from name patterns
for tok in "${tokens[@]}"; do
if [[ "$tok" == -* ]]; then
flags+=("$tok")
else
patterns+=("$tok")
fi
done
# validate & expand each pattern
for pat in "${patterns[@]}"; do
if [[ ! "$pat" =~ $valid ]]; then
log ERROR "Invalid container name: '$pat'"
shopt -u nullglob
return 1
fi
containers+=("$pat")
done
shopt -u nullglob
if ((${#containers[@]} == 0)); then
log WARN "No containers matched: ${patterns[*]}"
return 0
fi
# pass flags *then* containers to podman rm
podman rm "${flags[@]}" "${containers[@]}"
}
# ─────────────────────────────────────────────
# validate_command <workspace> <cmd> [<tok1> <tok2> …]
source "$HOME"/.local/bin/validate_command_access.sh
# ─────────────────────────────────────────────
# Entry & command parsing
if [[ -z "${SSH_ORIGINAL_COMMAND:-}" ]]; then
log ERROR "No SSH_ORIGINAL_COMMAND provided."
exit 1
fi
log INFO "SSH_ORIGINAL_COMMAND: $SSH_ORIGINAL_COMMAND"
read -ra parts <<<"$SSH_ORIGINAL_COMMAND"
cmd="${parts[0]}"
args=("${parts[@]:1}")
validate_command "$PERSON" "$cmd" "${args[@]}"
# ─────────────────────────────────────────────
# Dispatch
case "$cmd" in
build)
case "${args[0]}" in
base) podman build --target base -t analytics-backend-base . ;;
workspace) podman build --target base -t analytics-backend-base . ;;
all) podman build -t analytics-backend-workspace . ;;
*) log ERROR "build: invalid arg '${args[0]}'" ;;
esac
;;
update)
case "${args[0]}" in
containerfile) update 0 Containerfile . 500 ;;
access) update 1 access.yml . 400 ;;
authorized_keys) update 1 access.yml . 400 ;;
ssh_router) update 0 ssh_router.sh .local/bin 500 ;;
gitops_router) update 0 gitops_router.sh .local/bin 500 ;;
validate_command) update 0 validate_command_access.sh .local/bin 500 ;;
home_tar) update 0 home.tar.gz . 500 media ;;
gitconfig) update 0 gitconfig.template . 500 ;;
*) log ERROR "update: invalid arg '${args[0]}'" ;;
esac
;;
clean) clean_images ;;
status) podman images ;;
remove) remove_containers "${args[@]}" ;;
*)
log ERROR "Unknown command: '$cmd'"
exit 127
;;
esac

BIN
home.tar.gz (Stored with Git LFS)

Binary file not shown.

View File

@ -1,208 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
PERSON="${1:?Usage: $0 <person>}"
WORKSPACE="${SSH_ORIGINAL_COMMAND:-}"
IMAGE="localhost/analytics-backend-workspace:latest"
DEV_USER="devuser"
XDG_RUNTIME_DIR="/run/user/$(id -u)"
LOG_FILE="/tmp/.ssh-router-${PERSON}.log"
# ─────────────────────────────────────────────
# ANSI colors & emojis
readonly C_RESET='\033[0m'
readonly C_INFO='\033[1;34m' # blue
readonly C_WARN='\033[1;33m' # yellow
readonly C_ERROR='\033[1;31m' # red
log() {
local level="${1^^}"
shift
local icon color
case "$level" in
INFO) icon="" color="$C_INFO" ;;
WARN) icon="⚠️" color="$C_WARN" ;;
ERROR) icon="❌" color="$C_ERROR" ;;
*) icon="🔹" color="$C_RESET" ;;
esac
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
printf '%b%s [%s] %s%b\n' \
"$color" "$icon" "$ts" "[$level] $*" "$C_RESET" |
tee -a "$LOG_FILE"
}
# ─────────────────────────────────────────────
# Check for interactive TTY
if [[ ! -t 0 ]]; then
log ERROR "No TTY allocated—refusing to run without an interactive terminal"
echo "Error: No TTY. Use 'ssh -t'" >&2
exit 1
fi
# ─────────────────────────────────────────────
# Default WORKSPACE if empty
if [[ -z "$WORKSPACE" ]]; then
WORKSPACE="$PERSON"
log INFO "Defaulted WORKSPACE → $WORKSPACE"
fi
TMUX_SESSION="${WORKSPACE}|analytics-backend"
# ─────────────────────────────────────────────
# Ensure Podman socket is up
ensure_podman() {
local sock="$XDG_RUNTIME_DIR/podman/podman.sock"
if [[ ! -S "$sock" ]]; then
log INFO "Starting podman.socket for user $(id -un)"
systemctl --user start podman.socket || {
log ERROR "Failed to start podman.socket"
exit 1
}
sleep 1
fi
[[ -S "$sock" ]] || {
log ERROR "Podman socket still missing"
exit 1
}
}
ensure_podman
# ─────────────────────────────────────────────
# Ensure IMAGE is present
ensure_image() {
if ! podman image exists "$IMAGE"; then
log WARN "Image $IMAGE not found—pulling"
podman pull --tls-verify=false "$IMAGE" || {
log ERROR "Failed to pull $IMAGE"
exit 1
}
log INFO "Pulled $IMAGE"
fi
}
ensure_image
# ─────────────────────────────────────────────
# Disallow file transfers
case "$SSH_ORIGINAL_COMMAND" in
*scp* | *sftp* | *rsync* | *tar*)
log ERROR "File transfers are disabled"
exit 1
;;
esac
# ─────────────────────────────────────────────
# Generate per-user gitconfig
generate_gitconfig() {
local access="$HOME/access.yml"
local template="$HOME/gitconfig.template"
local userdir="$HOME/secrets/$PERSON"
local name email
name=$(yq -r ".\"$PERSON\".name" "$access" 2>/dev/null || echo)
email=$(yq -r ".\"$PERSON\".email" "$access" 2>/dev/null || echo)
if [[ -z "$name" || -z "$email" ]]; then
log ERROR "Missing name/email for '$PERSON' in $access"
exit 1
fi
mkdir -p "$userdir"
GIT_NAME="$name" GIT_EMAIL="$email" \
envsubst <"$template" >"$userdir/gitconfig"
log INFO ".gitconfig created → $userdir/gitconfig"
}
# ─────────────────────────────────────────────
# Start container if absent or stopped
start_container_if_needed() {
if ! podman container exists "$WORKSPACE"; then
log INFO "Creating container '$WORKSPACE'"
generate_gitconfig
podman run -dit \
--name "$WORKSPACE" \
--userns=keep-id \
--user "$DEV_USER" \
--hostname "$WORKSPACE" \
--label auto-cleanup=true \
-v "$HOME/data/$WORKSPACE:/app:Z" \
-v "$HOME/secrets/$WORKSPACE/gitconfig:/home/$DEV_USER/.gitconfig:ro,Z" \
-v "$HOME/secrets/$WORKSPACE/id_ed25519:/home/$DEV_USER/.ssh/id_ed25519:ro,Z" \
-v "$HOME/secrets/$WORKSPACE/id_ed25519.pub:/home/$DEV_USER/.ssh/id_ed25519.pub:ro,Z" \
--entrypoint "/home/$DEV_USER/start.sh" \
"$IMAGE" "$TMUX_SESSION"
elif ! podman inspect -f '{{.State.Running}}' "$WORKSPACE" | grep -q true; then
log INFO "Starting existing container '$WORKSPACE'"
podman start "$WORKSPACE" >/dev/null
fi
sleep 1
}
# ─────────────────────────────────────────────
# Detach logic: stop container when devuser has left
check_devuser_attached() {
local clients
clients=$(podman exec "$WORKSPACE" tmux list-clients -t "$TMUX_SESSION" -F "#{client_user}" 2>/dev/null)
if grep -q "^${DEV_USER}\$" <<<"$clients"; then
log INFO "devuser still attached—keeping container running"
else
log INFO "devuser detached—stopping container"
podman stop "$WORKSPACE" >/dev/null
fi
}
# ─────────────────────────────────────────────
# Determine access mode (rw|ro) or exit
get_access_mode() {
local yaml="access.yml" user="$PERSON" ws="$WORKSPACE"
[[ ! "$ws" =~ ^[A-Za-z0-9._-]+$ ]] && {
log ERROR "Invalid workspace name"
exit 1
}
if [[ "$user" == "$ws" ]]; then
echo rw
elif yq -e '.["'"$user"'"].rw[]?' "$yaml" | grep -qx "$ws"; then
echo rw
elif yq -e '.["'"$user"'"].ro[]?' "$yaml" | grep -qx "$ws"; then
echo ro
else
log ERROR "$user has no access to $ws"
exit 1
fi
}
MODE="$(get_access_mode)"
# ─────────────────────────────────────────────
# Main dispatch
case "$MODE" in
rw)
start_container_if_needed
# Ensure tmux session exists
if ! podman exec -it --user "$DEV_USER" "$WORKSPACE" tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
podman exec -it --user "$DEV_USER" "$WORKSPACE" \
tmux new-session -d -s "$TMUX_SESSION"
fi
log INFO "$PERSON attaching to workspace '$WORKSPACE'"
podman exec -it -e TERM="$TERM" --user "$DEV_USER" "$WORKSPACE" \
tmux attach -t "$TMUX_SESSION"
log INFO "$PERSON detached from '$WORKSPACE'"
check_devuser_attached
;;
ro)
if podman inspect -f '{{.State.Running}}' "$WORKSPACE" 2>/dev/null | grep -q true; then
log INFO "$PERSON viewing workspace '$WORKSPACE'"
podman exec -it -e TERM="$TERM" --user "$DEV_USER" "$WORKSPACE" \
tmux attach -r -t "$TMUX_SESSION"
log INFO "$PERSON stopped viewing '$WORKSPACE'"
else
log ERROR "Workspace '$WORKSPACE' is not running"
exit 1
fi
;;
*)
log ERROR "Unknown access mode: '$MODE'"
exit 1
;;
esac

View File

@ -1,2 +1,2 @@
#!/bin/bash
EDITOR=nvim tmux new-session -s "$1"
PODMAN_COMPOSE_WARNING_LOGS=false EDITOR=nvim CONTAINER_CONNECTION=my-remote tmux new-session -s "$1"

View File

@ -1,47 +0,0 @@
#!/usr/bin/env bash
set -e
cat >access.yml <<EOF
pallav:
fixedArgsCommands:
build:
- base
- workspace
- all
clean:
status:
multiArgsCommands:
remove:
- palak
- param
- darshan
EOF
source ./validate_command_access.sh
testcase() {
local desc="$1"
shift
if validate_command pallav "$@"; then
echo "PASS: $desc"
else
echo "FAIL: $desc"
fi
}
testcase "build base (valid)" build base
testcase "build all (valid)" build all
testcase "build base workspace (invalid)" build base workspace || true
testcase "build (no arg, invalid)" build || true
testcase "clean (zero-arg, valid)" clean
testcase "clean with arg (invalid)" clean foo || true
testcase "remove palak (valid)" remove palak
testcase "remove param palak (valid, any order)" remove param palak
testcase "remove palak param darshan (valid, any order)" remove palak param darshan
testcase "remove (no arg, invalid)" remove || true
testcase "remove foo (invalid)" remove foo || true
testcase "remove palak palak (duplicate, invalid)" remove palak palak || true
testcase "status (zero-arg, valid)" status
testcase "status foo (invalid)" status foo || true

View File

@ -1,83 +0,0 @@
#!/usr/bin/env bash
validate_command() {
local PERSON="$1"
local cmd="$2"
shift 2
local tokens=("$@")
local yaml="access.yml"
# Check if fixedArgsCommands.<cmd> exists
local is_fixed
is_fixed="$(yq e ".\"$PERSON\".fixedArgsCommands | has(\"$cmd\")" "$yaml")"
# Check if multiArgsCommands.<cmd> exists
local is_multi
is_multi="$(yq e ".\"$PERSON\".multiArgsCommands | has(\"$cmd\")" "$yaml")"
if [[ "$is_fixed" != "true" && "$is_multi" != "true" ]]; then
echo "ERROR: Command '$cmd' not allowed for $PERSON" >&2
return 1
fi
# Exclude flags from positional args
local args=()
for tok in "${tokens[@]}"; do
[[ "$tok" == -* ]] && continue
args+=("$tok")
done
if [[ "$is_fixed" == "true" ]]; then
mapfile -t allowed < <(yq e ".\"$PERSON\".fixedArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null)
local n_allowed="${#allowed[@]}"
if [[ $n_allowed -eq 0 ]]; then
# zero-arg command
if [[ ${#args[@]} -ne 0 ]]; then
echo "ERROR: Command '$cmd' takes no arguments" >&2
return 1
fi
else
# depth is 1: only one of the allowed choices must be present
if [[ ${#args[@]} -ne 1 ]]; then
echo "ERROR: Command '$cmd' requires exactly 1 argument: (${allowed[*]})" >&2
return 1
fi
local found=0
for want in "${allowed[@]}"; do
[[ "${args[0]}" == "$want" ]] && found=1 && break
done
if [[ $found -eq 0 ]]; then
echo "ERROR: Invalid argument '${args[0]}' for '$cmd'; allowed: (${allowed[*]})" >&2
return 1
fi
fi
return 0
fi
if [[ "$is_multi" == "true" ]]; then
mapfile -t allowed < <(yq e ".\"$PERSON\".multiArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null)
local n_allowed="${#allowed[@]}"
if [[ ${#args[@]} -lt 1 || ${#args[@]} -gt $n_allowed ]]; then
echo "ERROR: Command '$cmd' requires 1 to $n_allowed arguments: (${allowed[*]})" >&2
return 1
fi
# Order doesn't matter, but all must be unique and from allowed.
# Build a set of allowed args.
declare -A allowed_set=()
for want in "${allowed[@]}"; do allowed_set["$want"]=1; done
declare -A seen=()
for a in "${args[@]}"; do
[[ -z "${allowed_set[$a]}" ]] && {
echo "ERROR: Invalid argument '$a' for '$cmd'; allowed: (${allowed[*]})" >&2
return 1
}
[[ -n "${seen[$a]}" ]] && {
echo "ERROR: Duplicate argument '$a' for '$cmd'" >&2
return 1
}
seen["$a"]=1
done
return 0
fi
}