diff --git a/.claude/skills/iris-dev/skill.md b/.claude/skills/iris-dev/skill.md new file mode 100644 index 00000000..53297a35 --- /dev/null +++ b/.claude/skills/iris-dev/skill.md @@ -0,0 +1,208 @@ +--- +name: iris-dev +description: Develop and test Embedded Git for IRIS. Use when you need to connect to the IRIS container (SSH, ObjectScript), interact with the Management Portal or Git Web UI (Playwright CLI), or test Embedded Git behavior (initialize repos, create branches, reload modules). +--- + +# IRIS Development Skill + +Skill for developing and testing the Embedded Git extension against an IRIS instance running in a dev container. + +## Architecture + +Claude Code runs in the **workspace** container. IRIS runs in a separate **iris** container. They share the repository via a volume mount: + +| Container | Mount path | +|-----------|-----------| +| workspace | `/workspace` | +| iris | `/home/irisowner/dev/git-source-control/` | + +You do **NOT** have sudo permissions on either container. + +--- + +## 1. Connecting to the IRIS Container (SSH) + +The IRIS container is accessible via SSH using the hostname `iris`. + +### Running ObjectScript Commands + +```bash +ssh iris << 'EOF' +iris session iris -UUSER << 'IRIS' +write $zversion, ! +halt +IRIS +EOF +``` + +**Key rules:** +- Always quote heredoc delimiters (`'EOF'` and `'IRIS'`) to prevent shell expansion +- Always end the IRIS session with `halt` +- Use `-U` to select the namespace (e.g., `-U%SYS`, `-UUSER`) + +### Running Shell Commands on the IRIS Container + +```bash +ssh iris "ls -la /usr/irissys/mgr/" +ssh iris "cd /home/irisowner/dev/git-source-control && git log --oneline -5" +``` + +### Container Details + +- **Hostname:** `iris` +- **Port mapping:** 52774 (host) -> 52773 (container) +- **IRIS instance name:** IRIS +- **Default OS user:** irisowner (no sudo) + +--- + +## 2. Browser Automation (Playwright CLI) + +Use the `playwright-cli` command **from the workspace container** to interact with the IRIS Management Portal and Git Web UI via headless Chrome. + +### Authentication + +Every new browser session requires login: +- **Username:** superuser +- **Password:** SYS + +### Login Pattern + +```bash +playwright-cli open http://iris:52773/csp/sys/UtilHome.csp +playwright-cli snapshot +# Read the snapshot YAML to find refs for username, password, and login button +playwright-cli fill superuser +playwright-cli fill SYS +playwright-cli click +playwright-cli snapshot # verify title changed to "IRIS - Home" +``` + +Session state cannot be saved/restored across sessions. You must log in every time. + +### Snapshot vs Screenshot + +Prefer `snapshot` over `screenshot`. Snapshot returns structured YAML with element refs you can use for interaction. Use `screenshot` only when visual layout matters. + +```bash +playwright-cli snapshot +playwright-cli screenshot --filename=.playwright-cli/page.png +``` + +### Interacting with Pages + +Always take a snapshot first to discover element refs, then use the refs: + +```bash +playwright-cli fill value +playwright-cli fill value --submit +playwright-cli click +playwright-cli select value +playwright-cli goto +``` + +### Always Close When Done + +```bash +playwright-cli close +``` + +### Command Reference + +| Command | Purpose | +|---------|---------| +| `open ` | Start headless browser, navigate to URL | +| `goto ` | Navigate within existing session | +| `snapshot` | Structured YAML with element refs | +| `screenshot --filename=` | Visual screenshot | +| `fill ` | Fill a form field | +| `fill --submit` | Fill and press Enter | +| `click ` | Click an element | +| `select ` | Select dropdown option | +| `close` | Close the browser session | + +### Common Portal URLs + +| Page | URL | +|------|-----| +| Home / Dashboard | `http://iris:52773/csp/sys/UtilHome.csp` | +| Git Web UI (USER) | `http://iris:52773/isc/studio/usertemplates/gitsourcecontrol/webuidriver.csp/USER/` | +| Git Pull (USER) | `http://iris:52773/isc/studio/usertemplates/gitsourcecontrol/pull.csp?$NAMESPACE=USER` | +| Class list (USER) | `http://iris:52773/csp/sys/mgr/%25CSP.UI.Portal.ClassList.zen?$NAMESPACE=USER` | +| Production config | `http://iris:52773/csp/user/EnsPortal.ProductionConfig.zen?$NAMESPACE=USER&` | + +--- + +## 3. Embedded Git Development and Testing + +### Rebuilding After Code Changes + +After editing source files (ObjectScript classes, web UI JavaScript, CSP pages), reload the module in IRIS: + +```bash +ssh iris << 'EOF' +iris session iris -UUSER << 'IRIS' +zpm "load /home/irisowner/dev/git-source-control -dev" +halt +IRIS +EOF +``` + +This runs `npm ci && npm run build` (grunt release) for the web UI, compiles ObjectScript classes, copies CSP/web files to the IRIS installation directory, and re-configures web applications. + +**Important:** zpm must be run in the USER namespace (not %SYS). The path is the IRIS container's mount path (`/home/irisowner/dev/git-source-control`), not the workspace path (`/workspace`). + +### Setting Up a Test Repository + +**Never use the mounted git-source-control repository for testing Embedded Git behavior.** Instead, initialize a fresh repository on the IRIS container: + +```bash +ssh iris << 'EOF' +iris session iris -UUSER << 'IRIS' +// Configure Embedded Git settings +set settings = ##class(SourceControl.Git.Settings).%New() +set settings.namespaceTemp = "/usr/irissys/mgr/repo/USER/" +set settings.gitUserName = "Test User" +set settings.gitUserEmail = "test@example.com" +do settings.%Save() + +// Initialize the repository +do ##class(SourceControl.Git.Utils).Init() +halt +IRIS +EOF +``` + +Then create an initial commit and test branches via SSH shell commands: + +```bash +ssh iris "cd /usr/irissys/mgr/repo/USER && git config user.name 'Test User' && git config user.email 'test@example.com' && echo '# Test' > README.md && git add . && git commit -m 'Initial commit'" +ssh iris "cd /usr/irissys/mgr/repo/USER && git branch 'feature/test-branch'" +``` + +### Verifying in the Git Web UI + +Open the Git Web UI in a browser session, log in, and check that your test branches appear: + +```bash +playwright-cli open http://iris:52773/isc/studio/usertemplates/gitsourcecontrol/webuidriver.csp/USER/ +playwright-cli snapshot +# Log in (see Authentication section above) +playwright-cli snapshot # verify branches appear under "Local Branches" +playwright-cli close +``` + +### Key Settings Class Properties + +The `SourceControl.Git.Settings` class uses these properties: + +| Property | Purpose | +|----------|---------| +| `namespaceTemp` | Local git repo root directory | +| `gitBinPath` | Path to git executable (usually auto-detected) | +| `gitUserName` | Git attribution name for current user | +| `gitUserEmail` | Git attribution email for current user | +| `pullEventClass` | Event handler class for git pull | +| `decomposeProductions` | Source-control productions as separate files | + +Save settings with `do settings.%Save()` or `do settings.SaveWithSourceControl()` (the latter also exports the config file to the repo). diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..803aa194 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,58 @@ +{ + "name": "git-source-control", + "dockerComposeFile": "docker-compose.yml", + "service": "workspace", + "workspaceFolder": "/workspace", + "remoteUser": "node", + "postStartCommand": "git config --global --replace-all safe.directory /workspace", + "waitFor": "postStartCommand", + "customizations": { + "vscode": { + "extensions": [ + "anthropic.claude-code", + "intersystems-community.vscode-objectscript", + "intersystems.language-server", + "intersystems-community.servermanager" + ], + "settings": { + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { "path": "bash", "icon": "terminal-bash" }, + "zsh": { "path": "zsh" } + }, + "intersystems.servers": { + "iris-dev": { + "webServer": { + "scheme": "http", + "host": "iris", + "port": 52773 + }, + "username": "superuser" + } + }, + "objectscript.conn": { + "server": "iris-dev", + "ns": "USER", + "active": true + }, + "debug.javascript.autoAttachFilter": "disabled" + } + } + }, + "mounts": [ + "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume", + "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume" + ], + "containerEnv": { + "CLAUDE_CONFIG_DIR": "/home/node/.claude", + "POWERLEVEL9K_DISABLE_GITSTATUS": "true", + "AWS_BEARER_TOKEN_BEDROCK": "${localEnv:AWS_BEARER_TOKEN_BEDROCK}", + "AWS_DEFAULT_REGION": "${localEnv:AWS_DEFAULT_REGION}" + }, + "portsAttributes": { + "52774": { + "label": "IRIS Management Portal", + "onAutoForward": "ignore" + } + } +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..31ba8982 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,37 @@ +services: + workspace: + build: + context: . + dockerfile: workspace/Dockerfile + args: + TZ: ${TZ:-America/New_York} + CLAUDE_CODE_VERSION: latest + GIT_DELTA_VERSION: 0.18.2 + ZSH_IN_DOCKER_VERSION: 1.2.0 + cap_add: + - NET_ADMIN + - NET_RAW + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + volumes: + - ../:/workspace + command: sleep infinity + + iris: + build: + context: .. + dockerfile: .devcontainer/iris/Dockerfile + cap_add: + - NET_ADMIN + - NET_RAW + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + volumes: + - ../:/home/irisowner/dev/git-source-control/ + - ~/iris.key:/usr/irissys/mgr/iris.key + ports: + - 52774:52773 + restart: always + command: + - -a + - iris session iris -U%SYS '##class(Security.Users).UnExpireUserPasswords("*")' diff --git a/.devcontainer/init-firewall.sh b/.devcontainer/init-firewall.sh new file mode 100644 index 00000000..4453aed8 --- /dev/null +++ b/.devcontainer/init-firewall.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +# Reset policies to ACCEPT first so that on container restarts (where policies may already +# be DROP from a prior run) the script can still make outbound connections (e.g. curl GitHub). +iptables -P INPUT ACCEPT +iptables -P FORWARD ACCEPT +iptables -P OUTPUT ACCEPT + +# 1. Extract Docker DNS info BEFORE any flushing +DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true) + +# Flush all existing rules and ipsets +iptables -F; iptables -X +iptables -t nat -F; iptables -t nat -X +iptables -t mangle -F; iptables -t mangle -X +ipset destroy allowed-domains 2>/dev/null || ipset flush allowed-domains 2>/dev/null || true + +# 2. Selectively restore ONLY internal Docker DNS resolution +if [ -n "$DOCKER_DNS_RULES" ]; then + iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true + iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true + echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat +fi + +# Allow DNS (outbound UDP 53 + inbound responses) +iptables -A OUTPUT -p udp --dport 53 -j ACCEPT +iptables -A INPUT -p udp --sport 53 -j ACCEPT +# Allow inter-container traffic (SSH and IRIS web server) by resolving +# peer container IPs. Rules that don't match a local listener are no-ops. +for peer in iris workspace; do + PEER_IP=$(getent hosts "$peer" | awk '{print $1}') || continue + iptables -A OUTPUT -p tcp -d "$PEER_IP" --dport 22 -j ACCEPT + iptables -A OUTPUT -p tcp -d "$PEER_IP" --dport 52773 -j ACCEPT + iptables -A INPUT -p tcp -s "$PEER_IP" --dport 22 -j ACCEPT +done +# Allow localhost +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT + +# Create ipset (hash:net supports CIDRs) +ipset create --exist allowed-domains hash:net + +# Add GitHub IP ranges (from api.github.com/meta, .web + .api + .git, aggregated) +gh_ranges=$(curl -s https://api.github.com/meta) +echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q | while read -r cidr; do + ipset add --exist allowed-domains "$cidr" +done + +# Resolve and add specific allowed domains +# AWS Bedrock endpoints — read region from environment, fall back to us-east-1 +AWS_REGION="${AWS_DEFAULT_REGION:-${AWS_REGION:-us-east-1}}" +for domain in "registry.npmjs.org" "api.anthropic.com" "sentry.io" \ + "statsig.anthropic.com" "statsig.com" \ + "marketplace.visualstudio.com" "vscode.blob.core.windows.net" \ + "update.code.visualstudio.com" \ + "docs.intersystems.com" \ + "bedrock-runtime.${AWS_REGION}.amazonaws.com" \ + "bedrock.${AWS_REGION}.amazonaws.com" \ + "sts.amazonaws.com" "sts.${AWS_REGION}.amazonaws.com"; do + ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}') + while read -r ip; do + [ -n "$ip" ] || continue + ipset add --exist allowed-domains "$ip" + done < <(echo "$ips") +done + +# Default-deny policies +iptables -P INPUT DROP +iptables -P FORWARD DROP +iptables -P OUTPUT DROP + +# Allow established/related first +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT +iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + +# Allow outbound to whitelisted IPs only +iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT + +# Reject everything else with immediate feedback +iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited + +# Verification: must NOT reach example.com, MUST reach api.github.com +curl --connect-timeout 5 https://example.com >/dev/null 2>&1 && exit 1 # should fail +curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1 || exit 1 # must succeed +echo "Firewall configuration complete" diff --git a/.devcontainer/iris/Dockerfile b/.devcontainer/iris/Dockerfile new file mode 100644 index 00000000..b7a02cd5 --- /dev/null +++ b/.devcontainer/iris/Dockerfile @@ -0,0 +1,29 @@ +ARG BASE=containers.intersystems.com/intersystems/iris-community:2025.1 + +FROM ${BASE} + +USER root +RUN apt-get update && apt-get install -y --no-install-recommends \ + git nodejs npm \ + iptables ipset iproute2 dnsutils aggregate curl jq \ + openssh-server \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN ssh-keygen -A && \ + mkdir -p /run/sshd && \ + sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && \ + sed -i 's/^#\?PermitEmptyPasswords.*/PermitEmptyPasswords no/' /etc/ssh/sshd_config && \ + sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config + +RUN --mount=type=bind,src=.,dst=/home/irisowner/dev/git-source-control/,rw \ + chown -R irisowner:irisowner /home/irisowner/dev/git-source-control/ && \ + su - irisowner -c "iris start iris" && \ + su - irisowner -c "iris session IRIS < /home/irisowner/dev/git-source-control/iris.script" && \ + su - irisowner -c "iris stop iris quietly" + +COPY .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.sh +COPY .devcontainer/iris/iris-entrypoint.sh /iris-entrypoint.sh +RUN chmod +x /usr/local/bin/init-firewall.sh /iris-entrypoint.sh + +# Entrypoint runs as root to set up firewall and SSH, then drops to irisowner. +ENTRYPOINT ["/iris-entrypoint.sh"] diff --git a/.devcontainer/iris/iris-entrypoint.sh b/.devcontainer/iris/iris-entrypoint.sh new file mode 100644 index 00000000..db16092d --- /dev/null +++ b/.devcontainer/iris/iris-entrypoint.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -euo pipefail + +# This entrypoint runs as root. It sets up the firewall and SSH, then drops +# to irisowner with NET_ADMIN/NET_RAW stripped from the bounding set. + +# Apply outbound firewall before IRIS starts making network connections. +/usr/local/bin/init-firewall.sh + +# Allow incoming connections to the IRIS web server. This port is published in +# docker-compose.yml for the management portal. On Docker Desktop for Windows the +# forwarded traffic can arrive from an IP outside the Docker bridge subnet, so +# this rule accepts from any source. +iptables -I INPUT 1 -p tcp --dport 52773 -j ACCEPT + +# --- SSH setup: generate keypair and start sshd --- + +SSH_DIR="/home/irisowner/dev/git-source-control/.devcontainer/.ssh" +PRIV_KEY="${SSH_DIR}/dev_key" +PUB_KEY="${SSH_DIR}/dev_key.pub" +AUTH_KEYS="/home/irisowner/.ssh/authorized_keys" + +# Generate keypair on the shared volume if it doesn't already exist. +if [ ! -f "$PRIV_KEY" ]; then + mkdir -p "$SSH_DIR" + ssh-keygen -t ed25519 -f "$PRIV_KEY" -N "" -C "devcontainer-inter-container" + chown -R irisowner:irisowner "$SSH_DIR" +fi + +# Install the public key for irisowner. +mkdir -p /home/irisowner/.ssh +cp "$PUB_KEY" "$AUTH_KEYS" +chown -R irisowner:irisowner /home/irisowner/.ssh +chmod 700 /home/irisowner/.ssh +chmod 600 "$AUTH_KEYS" + +# Start sshd as a background daemon. +/usr/sbin/sshd + +# Drop to irisowner and strip NET_ADMIN/NET_RAW from the bounding set so +# IRIS processes cannot modify iptables rules or craft raw packets. +exec setpriv --reuid=irisowner --regid=irisowner --init-groups \ + --bounding-set=-net_admin,-net_raw \ + --ambient-caps=-net_admin,-net_raw \ + -- /tini -- /iris-main "$@" diff --git a/.devcontainer/workspace/Dockerfile b/.devcontainer/workspace/Dockerfile new file mode 100644 index 00000000..90b46d46 --- /dev/null +++ b/.devcontainer/workspace/Dockerfile @@ -0,0 +1,71 @@ +FROM node:20 + +ARG TZ +ENV TZ="$TZ" + +ARG CLAUDE_CODE_VERSION=latest + +RUN apt-get update && apt-get install -y --no-install-recommends \ + less git procps sudo fzf zsh man-db unzip gnupg2 gh \ + iptables ipset iproute2 dnsutils aggregate jq nano vim \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /usr/local/share/npm-global && \ + chown -R node:node /usr/local/share + +ARG USERNAME=node + +RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \ + && mkdir /commandhistory \ + && touch /commandhistory/.bash_history \ + && chown -R $USERNAME /commandhistory + +ENV DEVCONTAINER=true + +RUN mkdir -p /workspace /home/node/.claude && \ + chown -R node:node /workspace /home/node/.claude + +WORKDIR /workspace + +ARG GIT_DELTA_VERSION=0.18.2 +RUN ARCH=$(dpkg --print-architecture) && \ + wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \ + sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \ + rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" + +USER node + +ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global +ENV PATH=$PATH:/usr/local/share/npm-global/bin +ENV SHELL=/bin/zsh +ENV EDITOR=nano +ENV VISUAL=nano + +ARG ZSH_IN_DOCKER_VERSION=1.2.0 +RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \ + -p git \ + -p fzf \ + -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \ + -a "source /usr/share/doc/fzf/examples/completion.zsh" \ + -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \ + -x + +RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} + +# Playwright CLI for headless browser automation against the IRIS portal. +# Chromium is installed at build time because the Playwright CDN is not +# on the runtime firewall allowlist. +RUN npm install -g @playwright/cli@latest playwright +ENV PLAYWRIGHT_BROWSERS_PATH=/usr/local/share/playwright-browsers +USER root +RUN npx playwright install --with-deps chrome && \ + chmod -R o+rx /usr/local/share/playwright-browsers +USER node + +# Firewall and entrypoint scripts (build context is .devcontainer/) +COPY --chown=root:root init-firewall.sh /usr/local/bin/init-firewall.sh +COPY --chown=root:root workspace/workspace-entrypoint.sh /workspace-entrypoint.sh +USER root +RUN chmod +x /usr/local/bin/init-firewall.sh /workspace-entrypoint.sh + +ENTRYPOINT ["/workspace-entrypoint.sh"] diff --git a/.devcontainer/workspace/workspace-entrypoint.sh b/.devcontainer/workspace/workspace-entrypoint.sh new file mode 100644 index 00000000..f27b8611 --- /dev/null +++ b/.devcontainer/workspace/workspace-entrypoint.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -euo pipefail + +# Set up firewall as root (requires NET_ADMIN capability). +# This runs at every container start, before any user processes. +/usr/local/bin/init-firewall.sh + +# --- SSH client setup: wait for key from iris container --- + +SHARED_KEY="/workspace/.devcontainer/.ssh/dev_key" +NODE_SSH_DIR="/home/node/.ssh" +NODE_KEY="${NODE_SSH_DIR}/dev_key" +NODE_CONFIG="${NODE_SSH_DIR}/config" + +echo "Waiting for SSH key from iris container..." +while [ ! -f "$SHARED_KEY" ]; do + sleep 1 +done + +mkdir -p "$NODE_SSH_DIR" +cp "$SHARED_KEY" "$NODE_KEY" +chown node:node "$NODE_SSH_DIR" "$NODE_KEY" +chmod 700 "$NODE_SSH_DIR" +chmod 600 "$NODE_KEY" + +cat > "$NODE_CONFIG" <<'SSHEOF' +Host iris + User irisowner + IdentityFile ~/.ssh/dev_key + StrictHostKeyChecking no + UserKnownHostsFile /dev/null +SSHEOF +chown node:node "$NODE_CONFIG" +chmod 600 "$NODE_CONFIG" + +echo "SSH client configured." + +# Drop to the node user and exec the container command (e.g., "sleep infinity"). +# This ensures user processes never run as root. +# Strip NET_ADMIN and NET_RAW from the bounding set so user processes cannot +# modify iptables rules or craft raw packets to bypass the firewall. +exec setpriv --reuid=node --regid=node --init-groups \ + --bounding-set=-net_admin,-net_raw \ + --ambient-caps=-net_admin,-net_raw \ + -- "$@" diff --git a/.gitattributes b/.gitattributes index dfe07704..d7c026eb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ # Auto detect text files and perform LF normalization * text=auto + +# Shell scripts must always use LF, even on Windows checkouts, +# because they run inside Linux containers. +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index eedcf4e4..77671ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .vscode/ .gitattributes -*.code-workspace \ No newline at end of file +*.code-workspace +.worktrees/ +.devcontainer/.ssh/ +.playwright-cli/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae2c3825..6354028b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,17 +19,19 @@ The easiest way to get a working development environment is with Docker. This gi git clone https://github.com/intersystems/git-source-control cd git-source-control git checkout -b -docker compose up -d --build +docker compose -f .devcontainer/docker-compose.yml up -d --build ``` -This spins up a single container: -- **git-source-control-iris-1**: an IRIS instance with git-source-control loaded in dev mode in the USER namespace. The management portal is published to the host at port 52774. +This spins up two containers: +- **iris**: an IRIS instance with git-source-control loaded in dev mode in the USER namespace. The management portal is published to the host at port 52774. +- **workspace**: a Claude Code development container with git-source-control repository mounted. #### Important Notes -- The repository is mounted at `/home/irisowner/dev/git-source-control/` inside the container. -- If port 52774 is already in use, edit the port mapping in `docker-compose.yml`. -- If you have an InterSystems license key at `~/iris.key`, it will be mounted into the container automatically. +- The repository is mounted at `/home/irisowner/dev/git-source-control/` inside the IRIS container. +- If port 52774 is already in use, edit the port mapping in `.devcontainer/docker-compose.yml`. +- If you have an InterSystems license key at `~/iris.key`, it will be mounted into the IRIS container automatically. +- Claude Code may be used with the authentication options [documented here](https://code.claude.com/docs/en/devcontainer#add-claude-code-to-your-dev-container). If you have an AWS Bedrock key set in environment variables on the host machine it will be used by default. #### Development diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index e4e31642..00000000 --- a/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -ARG BASE=containers.intersystems.com/intersystems/iris-community:2025.1 - -FROM ${BASE} - -USER root -RUN apt-get update && apt-get install -y git nodejs npm - -RUN --mount=type=bind,src=.,dst=/home/irisowner/dev/git-source-control/,rw \ - chown -R irisowner:irisowner /home/irisowner/dev/git-source-control/ && \ - su - irisowner -c "iris start iris" && \ - su - irisowner -c "iris session IRIS < /home/irisowner/dev/git-source-control/iris.script" && \ - su - irisowner -c "iris stop iris quietly" - -USER irisowner - - diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 1bae21bc..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - iris: - build: . - restart: always - ports: - - 52774:52773 - volumes: - - ~/iris.key:/usr/irissys/mgr/iris.key - - ./:/home/irisowner/dev/git-source-control/ - command: - - -a - - iris session iris -U%SYS '##class(Security.Users).UnExpireUserPasswords("*")'