Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: 'Version (e.g., 1.0.0)'
required: true
type: string

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ${{ inputs.version }}
steps:
- name: Validate version format
run: |
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "Error: Invalid version format. Use semver (e.g., 1.0.0 or 1.0.0-beta.1)"
exit 1
fi

- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '18'

- name: Update version
working-directory: packages/canton-devenv-start
run: jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json

- name: Create tarball
working-directory: packages/canton-devenv-start
run: |
npm pack
mv canton-devenv-start-*.tgz devenv-$VERSION.tgz

- name: Create release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ inputs.version }}
name: v${{ inputs.version }}
files: packages/canton-devenv-start/devenv-${{ inputs.version }}.tgz
generate_release_notes: true
25 changes: 25 additions & 0 deletions .github/workflows/test-devcontainers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Test DevContainers

on: [pull_request]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
variant: [stable, latest]
steps:
- uses: actions/checkout@v4
- name: Extract SDK version
id: sdk
run: |
VERSION=$(jq -r '.build.args.DAML_SDK_VERSION' \
packages/canton-devenv-start/templates/.devcontainer/${{ matrix.variant }}/devcontainer.json)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build ${{ matrix.variant }}
run: |
docker build \
--build-arg DAML_SDK_VERSION=${{ steps.sdk.outputs.version }} \
-f packages/canton-devenv-start/templates/.devcontainer/Dockerfile \
packages/canton-devenv-start/templates/.devcontainer
11 changes: 8 additions & 3 deletions packages/canton-devenv-start/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ bunx devenv-init [options]
- `--force` overwrite existing files

## Output
- `.devcontainer/` with Dockerfile, devcontainer.json, `install-daml-vsix.sh`
- `.devcontainer/` with:
- `Dockerfile` (shared)
- `latest/devcontainer.json` (SDK 3.x)
- `stable/devcontainer.json` (SDK 2.x)
- `install-daml-vsix.sh`
- `.gitignore` tuned for DAML artifacts
- Empty workspace ready for `dpm new .`

Open the folder in VS Code/Cursor, “Reopen in Container,” and run `dpm build` once to warm the language server.
## Getting Started
1. Open folder in VS Code/Cursor
2. "Reopen in Container" → choose **latest** or **stable**

## Local development
1. From repo root: `bunx --bun link` (aka `bun link`).
Expand Down
35 changes: 26 additions & 9 deletions packages/canton-devenv-start/bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,19 @@ function parseArgs(argv) {

// Security: Prevent path traversal attacks
// Allow absolute paths but validate they're not system directories
const forbiddenPaths = ['/etc', '/usr', '/bin', '/sbin', '/var', '/sys', '/proc'];
const isSystemPath = forbiddenPaths.some(fp => normalizedPath === fp || normalizedPath.startsWith(fp + '/'));
const forbiddenPaths = [
'/etc', '/usr', '/bin', '/sbin', '/var', '/sys', '/proc',
'/root', '/boot', '/lib', '/lib64', '/opt',
'C:\\Windows', 'C:\\Program Files', 'C:\\Program Files (x86)',
'C:\\ProgramData', 'C:\\System32'
];
const isSystemPath = forbiddenPaths.some(fp => {
const normalizedFp = path.normalize(fp);
return normalizedPath === normalizedFp ||
normalizedPath.startsWith(normalizedFp + path.sep) ||
normalizedPath.toLowerCase() === normalizedFp.toLowerCase() ||
normalizedPath.toLowerCase().startsWith(normalizedFp.toLowerCase() + path.sep);
});

if (isSystemPath) {
console.error(`Error: Cannot write to system directory: ${normalizedPath}`);
Expand Down Expand Up @@ -108,16 +119,22 @@ function copyEntry(src, dest, opts, results) {

try {
fs.copyFileSync(src, dest);
// Only copy file permissions if not on Windows
// Only set file permissions if not on Windows
// Windows doesn't support Unix-style permissions
if (process.platform !== 'win32') {
// Sanitize permissions: remove execute bit unless source is explicitly executable
const sanitizedMode = stats.mode & 0o666; // Only keep read/write permissions
if (stats.mode & 0o111) { // If any execute bit is set in source
// Only preserve execute for owner, not group/others
fs.chmodSync(dest, sanitizedMode | 0o100);
} else {
// Check if this is a shell script by extension
const isShellScript = /\.(sh|bash)$/i.test(src);

if (isShellScript) {
// Shell scripts should always be executable (owner execute + read/write)
fs.chmodSync(dest, 0o755);
} else if (stats.mode & 0o111) {
// If source has any execute bit, preserve owner execute only
const sanitizedMode = (stats.mode & 0o666) | 0o100;
fs.chmodSync(dest, sanitizedMode);
} else {
// Regular files: read/write only
fs.chmodSync(dest, stats.mode & 0o666);
}
}
results.copied.push(dest);
Expand Down
22 changes: 18 additions & 4 deletions packages/canton-devenv-start/templates/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
FROM eclipse-temurin:17-jdk

# Build argument for SDK version - MUST be set by devcontainer.json
ARG DAML_SDK_VERSION

# Validate that DAML_SDK_VERSION is provided
RUN if [ -z "$DAML_SDK_VERSION" ]; then \
echo "ERROR: DAML_SDK_VERSION build argument is required" && exit 1; \
fi

# Persist DAML_SDK_VERSION as ENV for runtime use
ENV DAML_SDK_VERSION=${DAML_SDK_VERSION}

ENV DEBIAN_FRONTEND=noninteractive \
TERM=dumb \
NODE_VERSION=18
Expand Down Expand Up @@ -30,9 +41,12 @@ RUN useradd -m -u 1001 -s /bin/bash daml || true && \
USER daml
WORKDIR /home/daml

# Install DAML SDK via dpm
ENV DPM_HOME=/home/daml/.dpm
ENV PATH="${DPM_HOME}/bin:/home/daml/.daml/bin:${PATH}"
RUN curl -sSL https://get.digitalasset.com/install/install.sh | sh
# Install DAML SDK from GitHub releases
RUN echo "Installing DAML SDK version ${DAML_SDK_VERSION} from GitHub..." && \
curl -sSL "https://github.com/digital-asset/daml/releases/download/v${DAML_SDK_VERSION}/daml-sdk-${DAML_SDK_VERSION}-linux.tar.gz" -o daml-sdk.tar.gz && \
mkdir -p /home/daml/.daml/sdk/${DAML_SDK_VERSION} && \
tar -xzf daml-sdk.tar.gz -C /home/daml/.daml/sdk/${DAML_SDK_VERSION} --strip-components=1 && \
rm daml-sdk.tar.gz && \
/home/daml/.daml/sdk/${DAML_SDK_VERSION}/daml/daml install /home/daml/.daml/sdk/${DAML_SDK_VERSION} --install-with-custom-version=${DAML_SDK_VERSION}

WORKDIR /workspaces
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail

echo "Stopping any running DAML LSP processes..."
pkill -f "damlc (multi-ide|ide)" >/dev/null 2>&1 || true

echo "Starting DAML LSP in multi-ide mode..."
/home/daml/.dpm/bin/dpm damlc multi-ide --optOutTelemetry --log-level=Debug >/tmp/daml-lsp.log 2>&1 &

echo "DAML LSP restarted. Logs redirected to /tmp/daml-lsp.log"
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

DAML_SDK_VERSION="${DAML_SDK_VERSION:-3.4.7}"
# DAML_SDK_VERSION is set by the Dockerfile as an ENV variable
if [[ -z "${DAML_SDK_VERSION:-}" ]]; then
echo "[daml-vsix] Error: DAML_SDK_VERSION environment variable not set"
exit 1
fi

find_vsix() {
local cache_path
Expand Down Expand Up @@ -30,8 +34,10 @@ find_code_server() {
)
for base in "${roots[@]}"; do
[[ -d "$base" ]] || continue
local first
first=$(ls -d "$base"/* 2>/dev/null | head -n1 || true)
local first=""
for entry in "$base"/*; do
[[ -e "$entry" ]] && first="$entry" && break
done
[[ -n "$first" ]] || continue
if [[ -x "$first/bin/code-server" ]]; then
CODE_SERVER_BIN="$first/bin/code-server"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "Canton DAML Dev (latest)",
"build": {
"dockerfile": "../Dockerfile",
"context": "../..",
"args": {
"DAML_SDK_VERSION": "3.4.8"
}
},
"remoteUser": "daml",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"runArgs": [
"--init"
],
"mounts": [
"source=vscode-server-extensions,target=/home/daml/.vscode-server/extensions,type=volume"
],
"containerEnv": {
"DPM_HOME": "/home/daml/.dpm"
},
"remoteEnv": {
"PATH": "${containerEnv:PATH}:/home/daml/.dpm/bin:/home/daml/.daml/bin"
},
"postAttachCommand": "/workspaces/${localWorkspaceFolderBasename}/.devcontainer/install-daml-vsix.sh",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode-remote.remote-containers",
"mkhl.direnv"
],
"settings": {
"editor.formatOnSave": true,
"terminal.integrated.defaultProfile.osx": "bash",
"editor.codeLens": true,
"daml-language-server.trace.server": "verbose"
}
}
},
"forwardPorts": [
5011,
5012,
5021,
5022,
5018,
5019,
7500
]
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"name": "Canton DAML Dev",
"name": "Canton DAML Dev (stable)",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
"dockerfile": "../Dockerfile",
"context": "../..",
"args": {
"DAML_SDK_VERSION": "2.10.2"
}
},
"remoteUser": "daml",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
Expand All @@ -12,13 +15,9 @@
"mounts": [
"source=vscode-server-extensions,target=/home/daml/.vscode-server/extensions,type=volume"
],
"containerEnv": {
"DAML_SDK_VERSION": "3.4.7"
},
"remoteEnv": {
"PATH": "${containerEnv:PATH}:/home/daml/.daml/bin"
},
"postCreateCommand": "dpm --version && echo 'DAML SDK installed successfully' || echo 'Warning: DAML not found'",
"postAttachCommand": "/workspaces/${localWorkspaceFolderBasename}/.devcontainer/install-daml-vsix.sh",
"customizations": {
"vscode": {
Expand Down