Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
6715282
feat: create github ci template for lenscore accessibility testing
Caknoooo Oct 30, 2025
ac9cec9
style: run formatter
Caknoooo Oct 30, 2025
3eb1b42
ci: optimizing implementation for lens core ci
Caknoooo Oct 30, 2025
98ad085
style: run formatter
Caknoooo Oct 30, 2025
9eae81a
feat: create scalable ci lens-core for accessibility open source
Caknoooo Nov 2, 2025
59d2342
docs: update url path readme
Caknoooo Nov 2, 2025
e78e428
feat: splitting and handle vercel token required
Caknoooo Nov 4, 2025
c488ce4
feat: add cli output file json and adjust something
Caknoooo Nov 8, 2025
61362e9
docs: update readme
Caknoooo Nov 8, 2025
d6ece88
ci: add fallback install package without lock and support npm yarn an…
Caknoooo Nov 10, 2025
df17729
fix: change approach for download package
Caknoooo Nov 10, 2025
4d86aac
ci: enable corepack before install dep
Caknoooo Nov 10, 2025
7404eea
ci: testing for nextjs
Caknoooo Nov 11, 2025
d5a7553
ci: fix next js
Caknoooo Nov 12, 2025
95a1ec3
feat: add install docker compose
Caknoooo Nov 12, 2025
1b7ae6e
ci: fix something
Caknoooo Nov 12, 2025
e98c12c
style: run formatter
Caknoooo Nov 12, 2025
f9f81c7
ci: add skip cache
Caknoooo Nov 12, 2025
e583fd3
fix: remove unused verifiied
Caknoooo Nov 12, 2025
4ab181b
ci: change approach
Caknoooo Nov 12, 2025
2be970e
ci: change approach
Caknoooo Nov 12, 2025
c740341
ci: change approach
Caknoooo Nov 12, 2025
1744a1d
ci: change approach
Caknoooo Nov 12, 2025
898b426
ci: change approach
Caknoooo Nov 12, 2025
e9a09d7
ci: change approach
Caknoooo Nov 12, 2025
8a82b81
ci: change approach
Caknoooo Nov 12, 2025
ec6ebd1
ci: change approach
Caknoooo Nov 12, 2025
4d00fd1
feat: change approach of lens-core
Caknoooo Nov 14, 2025
b0c2a39
feat: change approach of lens-core
Caknoooo Nov 14, 2025
711935d
feat: change approach of lens-core
Caknoooo Nov 14, 2025
deeba9d
feat: change approach of lens-core
Caknoooo Nov 14, 2025
e939ed5
feat: change approach of lens-core
Caknoooo Nov 14, 2025
e996a5a
feat: change approach of lens-core
Caknoooo Nov 14, 2025
1bb3e19
feat: change approach of lens-core
Caknoooo Nov 14, 2025
2e4f326
feat: change approach of lens-core
Caknoooo Nov 14, 2025
0d02333
feat: change approach of lens-core
Caknoooo Nov 14, 2025
13f617e
feat: change approach of lens-core
Caknoooo Nov 14, 2025
8bcd4da
ci: change approach
Caknoooo Nov 14, 2025
b6b2823
ci: change approach
Caknoooo Nov 14, 2025
4d96c05
ci: change yarn version
Caknoooo Nov 14, 2025
e66f33d
ci: change approach for yarn
Caknoooo Nov 17, 2025
dcae4ae
ci: change approach for yarn
Caknoooo Nov 17, 2025
0bc337b
ci: fix yarn install package
Caknoooo Nov 17, 2025
19eb3ae
ci: fix yarn install package
Caknoooo Nov 17, 2025
1fcd1d7
ci: fix yarn install package
Caknoooo Nov 17, 2025
16c1459
ci: cleaning implementation scan template
Caknoooo Nov 19, 2025
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
273 changes: 273 additions & 0 deletions .github/workflows/lens-core-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
name: LensCore Accessibility CI

on:
workflow_call:
inputs:
url:
description: 'Application URL to scan'
required: false
type: string
default: 'http://localhost:3000'
port:
description: 'Application port'
required: false
type: number
default: 3000
max_urls:
description: 'Maximum number of URLs to scan'
required: false
type: number
default: 10
scan_depth:
description: 'Depth of crawling'
required: false
type: number
default: 2
timeout:
description: 'Timeout for each page scan (ms)'
required: false
type: number
default: 15000

jobs:
accessibility:
name: Run LensCore Accessibility Scan
runs-on: ubuntu-latest
timeout-minutes: 30

permissions:
contents: read
pull-requests: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Detect package manager
id: detect_pm
run: |
if [ -f package-lock.json ] || [ -f npm-shrinkwrap.json ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "lockfile=package-lock.json" >> $GITHUB_OUTPUT
elif [ -f yarn.lock ]; then
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "lockfile=yarn.lock" >> $GITHUB_OUTPUT
elif [ -f pnpm-lock.yaml ]; then
echo "manager=pnpm" >> $GITHUB_OUTPUT
echo "lockfile=pnpm-lock.yaml" >> $GITHUB_OUTPUT
else
echo "manager=npm" >> $GITHUB_OUTPUT
echo "lockfile=" >> $GITHUB_OUTPUT
fi
- name: Install system packages
run: |
sudo apt-get update
sudo apt-get install -y jq curl
- name: Install docker-compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
- name: Extract port and get Docker host IP
id: extract_port
run: |
URL="${{ inputs.url }}"
PORT="${{ inputs.port }}"
if [ -z "$PORT" ] || [ "$PORT" = "0" ]; then
if echo "$URL" | grep -qE ':[0-9]+'; then
PORT=$(echo "$URL" | sed -E 's/.*:([0-9]+).*/\1/')
else
PORT=3000
fi
fi
DOCKER_HOST_IP=$(docker network inspect bridge --format '{{ (index .IPAM.Config 0).Gateway }}' 2>/dev/null || echo "")
if [ -z "$DOCKER_HOST_IP" ]; then
DOCKER_HOST_IP=$(docker network inspect bridge --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}' 2>/dev/null | head -1 || echo "")
fi
if [ -z "$DOCKER_HOST_IP" ]; then
DOCKER_HOST_IP="172.17.0.1"
echo "::warning::Could not get Docker host IP from bridge network, using default: $DOCKER_HOST_IP"
fi
echo "port=$PORT" >> $GITHUB_OUTPUT
echo "docker_host_ip=$DOCKER_HOST_IP" >> $GITHUB_OUTPUT
echo "app_url=http://app:$PORT" >> $GITHUB_OUTPUT
echo "localhost_url=http://localhost:$PORT" >> $GITHUB_OUTPUT
echo "host_docker_internal_url=http://host.docker.internal:$PORT" >> $GITHUB_OUTPUT
echo "docker_host_url=http://${DOCKER_HOST_IP}:${PORT}" >> $GITHUB_OUTPUT
echo "Docker host IP: $DOCKER_HOST_IP"
echo "Scan URL will be: http://${DOCKER_HOST_IP}:${PORT}"
- name: Setup LensCore
run: |
npm install -g @accesstime/lenscore@latest
mkdir -p ~/.lenscore
cat > ~/.lenscore/config.json << 'CONFIG_EOF'
{
"mode": "local",
"docker": {
"image": "lenscore:latest",
"port": 3001
},
"remote": {
"baseUrl": "http://localhost:3001"
},
"openai": {
"apiKey": "",
"model": "gpt-3.5-turbo",
"enabled": false
}
}
CONFIG_EOF
lens-core build
- name: Prepare Dockerfile
run: |
PM="${{ steps.detect_pm.outputs.manager }}"
PORT="${{ steps.extract_port.outputs.port }}"
if [ ! -f Dockerfile ]; then
echo "Dockerfile not found. Creating a basic Dockerfile for $PM..."

if [ "$PM" = "npm" ]; then
{
echo "FROM node:20-alpine"
echo "WORKDIR /app"
echo "COPY package*.json ./"
echo "RUN npm ci"
echo "COPY . ."
echo "RUN npm run build || true"
echo "EXPOSE ${PORT}"
echo "CMD [\"npm\", \"start\"]"
} > Dockerfile
elif [ "$PM" = "yarn" ]; then
{
echo "FROM node:20-alpine"
echo "WORKDIR /app"
echo "RUN corepack enable && corepack prepare yarn@stable --activate"
echo "COPY package.json yarn.lock* .yarnrc* ./"
echo "COPY . ."
echo "RUN yarn install"
echo "RUN yarn build || true"
echo "EXPOSE ${PORT}"
echo "CMD [\"yarn\", \"start\"]"
} > Dockerfile
elif [ "$PM" = "pnpm" ]; then
{
echo "FROM node:20-alpine"
echo "WORKDIR /app"
echo "RUN corepack enable && corepack prepare pnpm@latest --activate"
echo "COPY package.json pnpm-lock.yaml ./"
echo "RUN pnpm install --frozen-lockfile"
echo "COPY . ."
echo "RUN pnpm build || true"
echo "EXPOSE ${PORT}"
echo "CMD [\"pnpm\", \"start\"]"
} > Dockerfile
else
echo "::warning::Unknown package manager: $PM. Creating generic Dockerfile..."
{
echo "FROM node:20-alpine"
echo "WORKDIR /app"
echo "COPY package*.json ./"
echo "RUN npm install --production || true"
echo "COPY . ."
echo "EXPOSE ${PORT}"
echo "CMD [\"node\", \"index.js\"]"
} > Dockerfile
fi
echo "✓ Created basic Dockerfile for $PM"
else
echo "✓ Using existing Dockerfile"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh, this is unnecessary, we should assume that user will have their own Dockerfile already, and if they not, then they should create one. We should not create template dockerfile for them because this most likely won't even work for their infrastructure and they will be forced to create their own Dockerfile.

I will just remove this and leave it with checking for Dockerfile file in the repo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey cee 👍

fi
- name: Start application
run: |
PORT="${{ steps.extract_port.outputs.port }}"
if [ -f docker-compose.yml ]; then
echo "✓ Found docker-compose.yml, using it to start application"
docker-compose up -d --build
else
echo "No docker-compose.yml found. Building and running application directly..."
docker build -t app-image .
docker run -d --rm \
--name app \
-p "${PORT}:${PORT}" \
-e NODE_ENV=production \
-e PORT=${PORT} \
app-image
echo "✓ Application container started"
fi
- name: Wait for LensCore
run: |
timeout=120
elapsed=0
while [ $elapsed -lt $timeout ]; do
if curl -sf "http://localhost:3001/api/health" > /dev/null 2>&1; then
echo "✓ LensCore is ready"
exit 0
fi
sleep 5
elapsed=$((elapsed + 5))
done
echo "::error::LensCore did not become available within timeout"
docker ps -a | grep lenscore || true
exit 1
- name: Wait for application
id: wait_app
run: |
DOCKER_HOST_IP="${{ steps.extract_port.outputs.docker_host_ip }}"
PORT="${{ steps.extract_port.outputs.port }}"
SCAN_BASE_URL="http://${DOCKER_HOST_IP}:${PORT}"
echo "Docker host IP: ${DOCKER_HOST_IP}"
echo "Waiting for app on ${SCAN_BASE_URL}..."
curl --retry 30 --retry-delay 3 --retry-connrefused \
"${SCAN_BASE_URL}" > /dev/null
echo "✓ Application is accessible at ${SCAN_BASE_URL}"
echo "localhost_url=${SCAN_BASE_URL}" >> $GITHUB_OUTPUT
echo "Previewing application response..."
RESPONSE=$(curl -sS "$SCAN_BASE_URL" 2>/dev/null || echo "")
if [ -n "$RESPONSE" ]; then
echo "Response preview (first 5 lines):"
echo "$RESPONSE" | head -n 5
fi
echo "scan_url=${SCAN_BASE_URL}" >> $GITHUB_ENV
- name: Run scan
run: |
SCAN_URL="${{ steps.wait_app.outputs.localhost_url }}"
if [ -z "$SCAN_URL" ]; then
DOCKER_HOST_IP="${{ steps.extract_port.outputs.docker_host_ip }}"
PORT="${{ steps.extract_port.outputs.port }}"
SCAN_URL="http://${DOCKER_HOST_IP}:${PORT}"
fi
lens-core scan "$SCAN_URL" \
-u ${{ inputs.max_urls }} \
-d ${{ inputs.scan_depth }} \
-t ${{ inputs.timeout }} \
--skip-cache \
--ci \
-o report.json
- name: Generate HTML report
run: |
SCAN_URL="${{ steps.wait_app.outputs.localhost_url }}"
if [ -z "$SCAN_URL" ]; then
DOCKER_HOST_IP="${{ steps.extract_port.outputs.docker_host_ip }}"
PORT="${{ steps.extract_port.outputs.port }}"
SCAN_URL="http://${DOCKER_HOST_IP}:${PORT}"
fi
lens-core scan "$SCAN_URL" -u ${{ inputs.max_urls }} -d ${{ inputs.scan_depth }} -t ${{ inputs.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 services
if: always()
run: |
if [ -f docker-compose.yml ]; then
docker-compose down -v || true
else
docker stop app || true
docker rm app || true
fi
lens-core down || true
Loading