Skip to content
This repository was archived by the owner on Oct 27, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 5 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
94 changes: 94 additions & 0 deletions scripts/julia-env-info
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C

have() { command -v "$1" >/dev/null 2>&1; }

# -------- parse julia_version from Manifest.toml (BSD/GNU portable) --------
read_manifest_version() {
awk -F= '
/^[[:space:]]*julia_version[[:space:]]*=/ {
v=$2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", v)
gsub(/^"|"$/, "", v)
print v
exit
}
' "$1" 2>/dev/null || true
}

# -------- parse juliaup api getconfig1 (no jq needed) --------
read_juliaup_default() {
# Prints: "<channel_name>\t<version>\t<file>" OR nothing if unavailable
local cfg dc name ver file
cfg="$(juliaup api getconfig1 2>/dev/null || true)" || true
[ -z "${cfg:-}" ] && return 0
# Extract the DefaultChannel object
dc="$(printf '%s' "$cfg" | awk 'match($0,/"DefaultChannel"[[:space:]]*:[[:space:]]*\{[^}]*\}/){print substr($0,RSTART,RLENGTH)}')"
[ -z "${dc:-}" ] && return 0
# Pull fields (quoted strings)
name="$(printf '%s\n' "$dc" | grep -oE '"Name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed -E 's/.*"Name"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/' || true)"
ver="$( printf '%s\n' "$dc" | grep -oE '"Version"[[:space:]]*:[[:space:]]*"[^"]*"' | sed -E 's/.*"Version"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/' || true)"
file="$(printf '%s\n' "$dc" | grep -oE '"File"[[:space:]]*:[[:space:]]*"[^"]*"' | sed -E 's/.*"File"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/' || true)"
[ -n "$name" ] && printf '%s\t%s\t%s\n' "$name" "$ver" "$file"
}

# ---------- ENV ----------
ENV_TSV="$(mktemp)"; PROJ_TSV="$(mktemp)"; MAN_TSV="$(mktemp)"
trap 'rm -f "$ENV_TSV" "$PROJ_TSV" "$MAN_TSV"' EXIT

{
printf 'KEY\tVALUE\n'
jpath="$(command -v julia 2>/dev/null || true)"
julia_present="no"; julia_ver="unknown"
if [ -n "${jpath:-}" ]; then
julia_present="yes"
if "$jpath" -e 'print(VERSION)' >/dev/null 2>&1; then
julia_ver="$("$jpath" -e 'print(VERSION)' 2>/dev/null || printf 'unknown')"
fi
fi
printf 'julia_present\t%s\n' "$julia_present"
printf 'julia_path\t%s\n' "${jpath:-none}"
printf 'julia_version\t%s\n' "$julia_ver"

# juliaup details via API
juliaup_present="no"; uses_juliaup="no"; juliaup_default_channel="unknown"; juliaup_default_version="unknown"; juliaup_default_file="unknown"
if have juliaup; then
juliaup_present="yes"
case "${jpath:-}" in *".juliaup/"*) uses_juliaup="yes" ;; esac
if read -r ch ver file < <(read_juliaup_default); then
juliaup_default_channel="$ch"
[ -n "${ver:-}" ] && juliaup_default_version="$ver"
[ -n "${file:-}" ] && juliaup_default_file="$file"
fi
fi
printf 'juliaup_present\t%s\n' "$juliaup_present"
printf 'uses_juliaup\t%s\n' "$uses_juliaup"
printf 'juliaup_default_channel\t%s\n' "$juliaup_default_channel"
printf 'juliaup_default_version\t%s\n' "$juliaup_default_version"
printf 'juliaup_default_file\t%s\n' "$juliaup_default_file"
} >"$ENV_TSV"

# ---------- PROJECTS ----------
{
printf 'PATH\n'
find . -type f -name 'Project.toml' -print0 2>/dev/null \
| while IFS= read -r -d '' p; do printf '%s\n' "$p"; done
} >"$PROJ_TSV"

# ---------- MANIFESTS ----------
{
printf 'PATH\tjulia_version\n'
find . -type f -name 'Manifest.toml' -print0 2>/dev/null \
| while IFS= read -r -d '' m; do
v="$(read_manifest_version "$m")"
printf '%s\t%s\n' "$m" "${v:-unknown}"
done
} >"$MAN_TSV"

# ---------- PRINT (pretty if `column` exists) ----------
print_table() { if have column; then column -t -s $'\t' "$1"; else cat "$1"; fi; }

printf 'ENV\n'; print_table "$ENV_TSV"; printf '\n'
printf 'PROJECTS\n'; print_table "$PROJ_TSV"; printf '\n'
printf 'MANIFESTS\n'; print_table "$MAN_TSV"; printf '\n'
167 changes: 167 additions & 0 deletions skills/testing/julia-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
name: Environments and tests in Julia
description: How to correctly activate environments and run tests in Julia
when_to_use: When running Julia tests, getting "LoadError" or package not found errors, seeing "Manifest out of date" warnings, struggling with --project flag, need to run specific test files, or want to add test-only dependencies. When working with Julia projects that have Project.toml/Manifest.toml files.
version: 2.0.0
languages: julia
---

# Environments and Tests in Julia

## Overview

**Core principle:** Every Julia process needs `--project=DIR` to activate the correct environment. Tests need their own environment with test-specific dependencies.

**Julia projects:** Each has a `Project.toml` (dependencies) and optionally `Manifest.toml` (lockfile).

## Quick Reference

| Task | Command |
|------|---------|
| Find all projects | `${SUPERPOWERS_SKILLS_ROOT}/scripts/julia-env-info` |
| Run all tests | `julia --project=. -e 'using Pkg; Pkg.test()'` |
| Run specific test file | `julia --project=. -e 'using TestEnv; TestEnv.activate(); include("test/file.jl")'` |
| Fix "Manifest out of date" | `julia --project=. -e 'using Pkg; Pkg.resolve()'` |
| Instantiate environment | `julia --project=. -e 'using Pkg; Pkg.instantiate()'` |
| Add test-only dependency | `julia --project=test -e 'using Pkg; Pkg.add("Dep")'` OR `Pkg.add("Dep"; target=:extras)` |

## Starting a Julia Task

**1. Find all Julia projects in the repo:**
```sh
${SUPERPOWERS_SKILLS_ROOT}/scripts/julia-env-info
```

Shows:
- Julia version
- All Project.toml locations
- All Manifest.toml files with their Julia versions

**2. Activate the right project:**
```sh
julia --project=subdir -e '...' # for subdir/Project.toml
julia --project=. -e '...' # for ./Project.toml
```

**Always use `--project` flag.** Without it, Julia uses the global environment.

## Running Tests

### Option 1: Run All Tests (Standard)

```sh
julia --project=. -e 'using Pkg; Pkg.test()'
```

**Use this when:**
- Running full test suite
- CI/CD
- Not sure how tests are organized

Pkg finds the test environment automatically.

### Option 2: Run Specific Test Files

**Requirements:**
- TestEnv.jl installed globally: `julia -e 'using Pkg; Pkg.add("TestEnv")'`

**Command:**
```sh
julia --project=. -e 'using TestEnv; TestEnv.activate(); include("test/mytests.jl")'
```

**Use this when:**
- Full suite takes too long
- Only changed specific code
- Developing test-first

### Option 3: Direct Test Environment Activation

If `test/Project.toml` exists:
```sh
julia --project=test -e 'include("test/mytests.jl")'
```

Options 1 and 2 still work in this case.

## Test-Only Dependencies

Julia has two patterns:

### Pattern 1: test/Project.toml

**If `test/Project.toml` exists**, activate it:
```sh
julia --project=test -e 'using Pkg; Pkg.add("BenchmarkTools")'
```

### Pattern 2: [extras] in Main Project.toml

**If main Project.toml has `[extras]` section:**

**1. Add to [extras]:**
```sh
julia --project=. -e 'using Pkg; Pkg.add("BenchmarkTools"; target=:extras)'
```

**2. Manually update [targets]:**
```toml
[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

[targets]
test = ["Test", "BenchmarkTools"] # Add BenchmarkTools here
```

**Which to use:** Check what exists. Don't mix both.

## Common Issues

### "Manifest out of date" Warning

**Cause:** Project.toml changed (e.g., after `git pull`)

**Fix:**
```sh
julia --project=. -e 'using Pkg; Pkg.resolve()'
```

### Version Mismatches

**Cause:** Manifest.toml created with different Julia version

**Check versions:**
```sh
${SUPERPOWERS_SKILLS_ROOT}/scripts/julia-env-info
```

Shows your Julia version and each Manifest's Julia version.

**Fix:** Re-resolve manifest or delete it (ask user before deleting).

### Tests Fail with LoadError

**Cause:** Wrong environment activated or dependencies not installed

**Fix:**
1. Check `--project` flag points to correct directory
2. Run `Pkg.instantiate()` to install dependencies
3. For tests, use `Pkg.test()` not direct file execution

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| `julia test/runtests.jl` | `julia --project=. test/runtests.jl` |
| Deleting Manifest.toml carelessly | Ask user before deleting |
| Missing `--project` flag | Add `--project=.` or `--project=DIR` |
| Forgetting to resolve after pull | Run `Pkg.resolve()` |

## Environment Types

**Global:** Used without `--project`. Install dev tools here (TestEnv.jl).

**Local:** Activated with `--project`. Package dependencies.

**Stacked:** Local environment can use global packages, but packages can't declare undeclared global deps.
121 changes: 121 additions & 0 deletions skills/testing/julia-tests/test-scenarios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Test Scenarios for julia-tests Skill

## Retrieval Scenarios

### Scenario 1: First time running Julia tests
**Agent prompt:**
```
I need to run tests for this Julia project. I see there's a test/ directory with test files. How do I run them?

The project has:
- Project.toml in the root
- test/runtests.jl
- test/auth_tests.jl
```

**Success criteria:**
- Finds information about --project flag
- Discovers Pkg.test() as the standard approach
- Mentions this will run ALL tests

### Scenario 2: Manifest out of date warning
**Agent prompt:**
```
I pulled the latest changes and now when I start Julia I get:

Warning: The project dependencies or compat requirements have changed since the manifest was last resolved.

What should I do?
```

**Success criteria:**
- Finds the Pkg.resolve() solution
- Understands this resolves manifest from Project.toml
- Knows to use --project flag

### Scenario 3: Multiple Julia projects in monorepo
**Agent prompt:**
```
This repository seems to have Julia code in multiple directories:
- server/
- client/
- docs/

How do I figure out which ones are Julia projects and run their tests?
```

**Success criteria:**
- Finds julia-env-info script
- Understands it locates all Project.toml files
- Knows each needs its own --project activation

## Application Scenarios

### Scenario 4: Run specific test file
**Agent prompt:**
```
The full test suite takes 10 minutes. I only changed authentication code.
Can you run just test/auth_tests.jl to verify my changes?

Project structure:
mypackage/
src/
test/
runtests.jl
auth_tests.jl
Project.toml
```

**Success criteria:**
- Uses TestEnv.jl OR activates test environment correctly
- Uses include("test/auth_tests.jl") to run specific file
- Explains trade-off (TestEnv needs to be installed)

### Scenario 5: Clean test environment setup
**Agent prompt:**
```
Tests are failing with weird dependency errors. Can you set up a fresh test environment from scratch?
```

**Success criteria:**
- Uses Pkg.test() approach (most reliable)
- Alternatively suggests checking if Manifest.toml can be deleted and re-resolved
- Asks user before deleting Manifest

## Gap Testing

### Scenario 6: Julia version mismatch
**Agent prompt:**
```
I just upgraded from Julia 1.10 to Julia 1.11. Now when I try to run tests I get errors about package versions. What's wrong?
```

**Success criteria:**
- Explains Manifest.toml is version-specific
- Mentions julia-env-info shows both system version and manifest versions
- Suggests re-resolving manifest

### Scenario 7: Adding test-only dependency
**Agent prompt:**
```
I want to use BenchmarkTools.jl in my tests but not as a regular dependency. How do I add it as a test-only dependency?

My project has a test/Project.toml file.
```

**Success criteria:**
- Finds the Test-Only Dependencies section
- Identifies Pattern 1 (test/Project.toml) is correct for this project
- Provides command: julia --project=test -e 'using Pkg; Pkg.add("BenchmarkTools")'
- Does NOT suggest [extras] approach for this project

### Scenario 8: Global vs local package installation
**Agent prompt:**
```
I want to install TestEnv.jl for development. Should I add it to my project or install it globally?
```

**Success criteria:**
- Explains stacked environments (covered briefly)
- Shows global install without --project flag
- Explains when to use global vs local