Skip to content
Open
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
178 changes: 178 additions & 0 deletions .github/workflows/accessibility-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
name: LensCore Accessibility Docs Check

on:
push:
branches: ['main']
pull_request:

jobs:
accessibility-docs:
runs-on: ubuntu-latest
env:
LENSCORE_CI_HOME: /home/runner/.lenscore
VITEPRESS_PORT: 5173
VITEPRESS_BASE_PATH: /LensCore/en/
LENSCORE_PORT: 3001
MAX_URLS: 20
SCAN_DEPTH: 2
TIMEOUT: 10000
steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq

- name: Install docker-compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.33.1/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose version

- name: Run VitePress docs container
run: |
docker run -d --rm \
--name lenscore-docs \
-p "${VITEPRESS_PORT}:${VITEPRESS_PORT}" \
-v "${{ github.workspace }}:/app" \
-w /app \
-e HOST=0.0.0.0 \
node:20 \
sh -c "npm ci && npm run docs:dev -- --host 0.0.0.0 --port ${VITEPRESS_PORT} --strictPort"

- name: Setup LensCore
run: |
npm install -g @accesstime/lenscore@latest

CONFIG_BASE="${LENSCORE_CI_HOME:-$HOME/.lenscore-ci}"
rm -rf "$CONFIG_BASE"
mkdir -p "$CONFIG_BASE"

cat > "$CONFIG_BASE/config.json" <<EOF
{
"mode": "local",
"docker": {
"image": "lenscore:latest",
"port": $LENSCORE_PORT
},
"remote": {
"baseUrl": "http://localhost:$LENSCORE_PORT"
},
"openai": {
"apiKey": "",
"model": "gpt-3.5-turbo",
"enabled": false
}
}
EOF

TMP_DIR=$(mktemp -d)
HOME="$CONFIG_BASE" bash -c "cd '$TMP_DIR' && lens-core build"

- name: Wait for docs & expose URL
id: extract_port
run: |
echo "Waiting for VitePress container to be ready..."
sleep 5

# Get container IP address
CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lenscore-docs)
DOCKER_HOST_IP=$(docker network inspect bridge --format '{{ (index .IPAM.Config 0).Gateway }}')

SCAN_BASE_URL=""
# Try container IP first, fallback to gateway IP, then localhost
for BASE_IP in "${CONTAINER_IP}" "${DOCKER_HOST_IP}" "localhost"; do
TEST_URL="http://${BASE_IP}:${VITEPRESS_PORT}${VITEPRESS_BASE_PATH}"
echo "Testing URL: ${TEST_URL}"
if curl -sSf "${TEST_URL}" > /dev/null 2>&1; then
SCAN_BASE_URL="${TEST_URL}"
echo "✅ Found working URL: ${SCAN_BASE_URL}"
break
fi
done

if [ -z "${SCAN_BASE_URL}" ]; then
echo "Container IP: ${CONTAINER_IP}"
echo "Gateway IP: ${DOCKER_HOST_IP}"
echo "VitePress port: ${VITEPRESS_PORT}"
echo "VitePress base path: ${VITEPRESS_BASE_PATH}"
echo "Waiting for docs..."

# Check container logs
docker logs lenscore-docs 2>&1 | tail -20 || true

# Wait for VitePress to be ready with container IP and base path
SCAN_BASE_URL="http://${CONTAINER_IP}:${VITEPRESS_PORT}${VITEPRESS_BASE_PATH}"
for i in {1..30}; do
if curl -sSf "${SCAN_BASE_URL}" > /dev/null 2>&1; then
echo "✅ VitePress is ready!"
break
fi
echo "Attempt $i/30: Waiting for VitePress on ${SCAN_BASE_URL}..."
sleep 2
done

# Final check
if ! curl -sSf "${SCAN_BASE_URL}" > /dev/null 2>&1; then
echo "::error::VitePress failed to start"
docker logs lenscore-docs 2>&1 | tail -50
exit 1
fi
fi

echo "localhost_url=${SCAN_BASE_URL}" >> "$GITHUB_OUTPUT"

- name: Preview VitePress response
run: |
SCAN_URL="${scan_url:-${{ steps.extract_port.outputs.localhost_url }}}"
echo "Fetching homepage from: $SCAN_URL"
RESPONSE=$(curl -sS "$SCAN_URL")
echo "----- Begin VitePress response (first 40 lines) -----"
echo "$RESPONSE" | head -n 40
echo "----- End VitePress response -----"
if ! echo "$RESPONSE" | grep -q "LensCore"; then
echo "::warning::Expected marker text not found in VitePress response."
fi

- name: Run accessibility scan
run: |
SCAN_URL="${scan_url:-${{ steps.extract_port.outputs.localhost_url }}}"
HOME="$LENSCORE_CI_HOME" lens-core scan "$SCAN_URL" \
-u "${MAX_URLS}" \
-d "${SCAN_DEPTH}" \
-t "${TIMEOUT}" \
--skip-cache \
--ci \
-o report.json

- name: Generate HTML report
run: |
SCAN_URL="${scan_url:-${{ steps.extract_port.outputs.localhost_url }}}"
HOME="$LENSCORE_CI_HOME" lens-core scan "$SCAN_URL" \
-u "${MAX_URLS}" \
-d "${SCAN_DEPTH}" \
-t "${TIMEOUT}" \
--skip-cache \
--web || true

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: lenscore-accessibility-report
path: |
report.json
retention-days: 7
if-no-files-found: warn

- name: Stop docs container
if: always()
run: |
docker stop lenscore-docs || true
142 changes: 142 additions & 0 deletions docs/en/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ Test accessibility of a single page.
- `-r, --rules <rules>`: Specific rules to test (comma-separated)
- `-g, --tags <tags>`: WCAG tags to test (e.g., "wcag2a,wcag2aa")
- `--no-screenshot`: Skip screenshot capture
- `--ci`: CI mode: formatted output for continuous integration
- `-o, --output <file>`: Output file for JSON report (default: report.json when --ci is used)
- `--no-exit-on-violations`: Do not exit with error code when violations are found (CI mode)
- `--no-show-details`: Hide detailed violation information (CI mode)
- `--skip-cache`: Bypass cache and force a fresh test

**Examples:**
Expand Down Expand Up @@ -181,6 +185,22 @@ lens-core crawl https://example.com --web

Crawl website and test accessibility (combined operation).

**Options:**

- `--enable-ai`: Enable AI-powered analysis
- `-k, --openai-key <key>`: OpenAI API key
- `-c, --project-context <context>`: Project context (e.g., "react,tailwind")
- `-w, --web`: Generate HTML report
- `-u, --max-urls <number>`: Maximum URLs to crawl (default: 10)
- `-d, --max-depth <number>`: Maximum crawl depth (default: 2)
- `-t, --timeout <ms>`: Request timeout in milliseconds (default: 15000)
- `-j, --concurrency <number>`: Number of concurrent requests (default: 3)
- `--skip-cache`: Bypass cache and force fresh scan
- `--ci`: CI mode: formatted output for continuous integration
- `-o, --output <file>`: Output file for JSON report (default: report.json when --ci is used)
- `--no-exit-on-violations`: Do not exit with error code when violations are found (CI mode)
- `--no-show-details`: Hide detailed violation information (CI mode)

**Examples:**

```bash
Expand All @@ -189,6 +209,8 @@ lens-core scan https://example.com --enable-ai
lens-core scan https://example.com --max-urls 50 --max-depth 3
lens-core scan https://example.com --project-context "react,tailwind"
lens-core scan https://example.com --web
lens-core scan https://example.com --ci
lens-core scan https://example.com --ci --no-exit-on-violations
```

**Options (summary):**
Expand Down Expand Up @@ -294,6 +316,84 @@ Generates an HTML report saved to `web/output/` directory with:
- Code snippets
- Recommendations

### CI Output (Continuous Integration)

The `--ci` flag provides formatted output optimized for CI/CD pipelines:

```bash
lens-core scan https://example.com --ci
lens-core test https://example.com --ci
```

**Features:**

- ✅ **Formatted output**: Clean, structured output for CI logs
- ✅ **Automatic exit codes**: Exit code 1 if violations found, 0 if none
- ✅ **Detailed violations**: Shows violation details with help URLs
- ✅ **JSON report**: Automatically saves report to `report.json` (or custom file with `-o`)
- ✅ **Fallback handling**: Automatically falls back to test command if scan fails
- ✅ **GitHub Actions integration**: Includes `::error::` annotations for violations

**CI Mode Options:**

- `--ci`: Enable CI mode with formatted output
- `-o, --output <file>`: Specify output file for JSON report (default: `report.json`)
- `--no-exit-on-violations`: Don't fail workflow on violations (useful for monitoring)
- `--no-show-details`: Hide detailed violation information (summary only)

**Example CI Output:**

```
==========================================
ACCESSIBILITY VIOLATIONS DETECTED
==========================================

Pages scanned: 3
Total violations: 5

--- Violation Details ---

Page: https://example.com
❌ color-contrast [serious]
Description: Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds
Help: Elements must have sufficient color contrast
Help URL: https://dequeuniversity.com/rules/axe/4.8/color-contrast
- Element: button.submit-btn
HTML: <button class="submit-btn">Submit</button>
Summary: Element has insufficient color contrast

==========================================
How to fix:
1. Review each violation above
2. Check the Help URL for detailed guidance
3. Fix the issues in your code
4. Re-run the workflow to verify fixes
==========================================

::error::Accessibility violations detected: 5
```

**Use Cases:**

1. **Enforcing Quality** (default behavior):

```bash
lens-core scan https://example.com --ci
# Workflow fails if violations found
```

2. **Monitoring Only** (non-blocking):

```bash
lens-core scan https://example.com --ci --no-exit-on-violations
# Workflow succeeds but violations are reported
```

3. **Custom Output File**:
```bash
lens-core scan https://example.com --ci -o custom-report.json
```

## Configuration File

LensCore configuration is stored in `~/.lenscore/config.json`:
Expand Down Expand Up @@ -366,6 +466,48 @@ lens-core crawl https://example.com \

### CI/CD Integration

LensCore provides dedicated CI mode for seamless integration with CI/CD pipelines:

**Basic CI Integration:**

```bash
lens-core setup --port 3001
lens-core build
lens-core scan https://example.com --ci
```

**GitHub Actions Example:**

```yaml
- name: Run accessibility scan
run: |
lens-core scan "$SCAN_URL" \
-u 20 \
-d 2 \
-t 10000 \
--skip-cache \
--ci \
-o report.json
```

**Advanced CI Integration:**

```bash
# With AI analysis
lens-core scan https://example.com --ci --enable-ai

# Non-blocking (monitoring only)
lens-core scan https://example.com --ci --no-exit-on-violations

# Custom output file
lens-core scan https://example.com --ci -o accessibility-report.json

# Minimal output (no details)
lens-core scan https://example.com --ci --no-show-details
```

**Legacy Integration (without --ci):**

```bash
lens-core setup --port 3001 --ai --openai-key $OPENAI_API_KEY
lens-core build
Expand Down
12 changes: 4 additions & 8 deletions docs/en/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,21 +246,17 @@ curl http://localhost:3001/api/health

## Project Structure

When you run LensCore, it creates the following structure:
**Runtime Structure** (created when running LensCore):

```
~/.lenscore/
├── config.json # Configuration file
├── logs/ # Application logs
│ ├── combined.log
│ └── error.log
└── cache/ # Cache directory

./
├── storage/ # Screenshot storage
│ └── screenshots/
└── web/ # HTML reports
└── output/
├── cache/ # Cache directory
└── web/ # Web templates (copied from package)
└── output/ # Generated HTML reports
```

## Configuration Options
Expand Down
Loading