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
3 changes: 0 additions & 3 deletions .devcontainer/Dockerfile

This file was deleted.

1 change: 0 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"DavidAnson.vscode-markdownlint",
"ms-vscode-remote.remote-containers",
"ms-azuretools.vscode-docker",
"cweijan.vscode-redis-client",
"bradlc.vscode-tailwindcss",
"GitHub.vscode-pull-request-github",
"eamodio.gitlens",
Expand Down
12 changes: 2 additions & 10 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
services:
# Update this to the name of the service you want to work with in your docker-compose.yml file
lab-framework-core:
# Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
# folder. Note that the path of the Dockerfile and context is relative to the *primary*
# docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
# array). The sample below assumes your primary file is in the root of your project.
#
build:
context: .
dockerfile: ./Dockerfile
# image: mcr.microsoft.com/devcontainers/javascript-node:20
image: mcr.microsoft.com/devcontainers/javascript-node:24-bookworm
volumes:
# Update this to wherever you want VS Code to mount the folder of your project
- ..:/workspace:cached
Expand All @@ -28,7 +20,7 @@ services:
image: mockserver/mockserver
ports:
- "80:1080" # for UDF mocks
- "5123:1080" # for petname service mocks
- "5123:1080" # for deployment identifier service mocks

volumes:
cache:
Expand Down
4 changes: 2 additions & 2 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ curl --retry 12 --retry-all-errors -X PUT 'http://metadata.udf:80/mockserver/exp
{
"id": "0cd74e44-90fc-4220-96fe-4c1b1cf1502e",
"name": "Runner",
"osName": "Ubuntu 22.04 LTS Server",
"osName": "Ubuntu 24.04 LTS Server",
"mgmtIp": "10.1.1.5",
"trafficIps": [],
"accessMethods": {
Expand Down Expand Up @@ -172,7 +172,7 @@ curl -X PUT 'http://metadata.udf:80/mockserver/expectation' \
{
"id": "0cd74e44-90fc-4220-96fe-4c1b1cf1502e",
"name": "Runner",
"osName": "Ubuntu 22.04 LTS Server",
"osName": "Ubuntu 24.04 LTS Server",
"mgmtIp": "10.1.1.5",
"trafficIps": [],
"accessMethods": {
Expand Down
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Framework ENV Vars
# DOCKER_API_URL=tcp://host.docker.internal:2375 # Set to use the Docker API (instead of docker.sock) when the framework makes docker calls.
DEPLOYMENT_IDENTIFIER_RESPONSE_FIELD=petname
REMOTE_DOCS_REPO_SERVER="https://raw.githubusercontent.com"
REMOTE_DOCS_REPO_API_SERVER="https://api.github.com"
REMOTE_DOCS_REPO_OWNER=f5devcentral
Expand All @@ -10,6 +11,6 @@ REMOTE_DOCS_REPO_MEDIA_PATH=docs/media
REMOTE_DOCS_REPO_CACHE_SECONDS=300
# SIMULATE_LOAD_DELAY=1 # Adds artificial delay to key document loading functions. Used to test the loading animation page.
# NODE_TLS_REJECT_UNAUTHORIZED=0 # Allows API Check components to test TLS connections with untrusted certs

# NODE_NO_WARNINGS=1 # Suppresses Node.js warnings, such as from the above setting
# Custom Content-based ENV Vars
CONTAINER_MOUNT_BASE_DIR=/Users/d.edgar/Documents/workspaces/lab-framework
32 changes: 32 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copilot Instructions

## Test Execution Rules

- Never run tests in watch mode for this repository.
- Do not use `npm test` because the script enables Jest watch mode.
- Always run tests in CI-style non-watch mode.

Approved examples:

- `CI=1 npx jest --runInBand --watchAll=false`
- `CI=1 npx jest --runInBand --watchAll=false src/lib/variables.test.ts`
- `CI=1 npx jest --runInBand --watchAll=false src/app/components/set-variable.test.tsx`

## Implementation Rules

- Never write legacy conversion code.
- Never write migration compatibility code paths.
- Implement clean forward-only behavior and update callers/tests accordingly.
- Always run relevant tests after making code changes.
- Always update or add tests when behavior, interfaces, or outputs change.
- Treat component and context state transitions as first-class behavior: when changing rendering or refactoring stateful code, add or update tests that assert the transition path, not just the final markup, and review any test edits for hidden state regressions before accepting them.
- Use browser localStorage as the source of truth for UI state where applicable.
- Ensure UI updates immediately when localStorage-backed variables change.
- For client-side polling, background status sync, or repeated variable resolution, prefer lightweight API endpoints plus targeted client state updates over server actions or page-route roundtrips that trigger full route rendering.
- When a polling or refresh loop is needed, scope it to the smallest data surface possible, avoid re-rendering the whole page tree, and pause or reduce polling for hidden/inactive tabs when practical.
- If a change may deviate from any of these rules, ask for explicit user approval before proceeding.
- Refactor static/hardcoded values into named constants or environment variables.
- If it is unclear whether a value should be a constant or an environment variable, ask the user before implementing.
- Do not implement readability-reducing compatibility alias patterns that accept multiple names for the same input.
- For any given input/option, accept a single canonical name and update callers/tests/docs to match it instead of adding fallback aliases or normalization logic.
- Do not introduce duplicate code; extract shared logic into centralized utilities/modules and update call sites instead of copy-pasting implementations.
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ updates:
directory: "/"
schedule:
interval: weekly
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: weekly
31 changes: 18 additions & 13 deletions .github/workflows/build-containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize, reopened]

env:
REGISTRY: ghcr.io
Expand All @@ -18,30 +19,34 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v7

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to the Container registry
uses: docker/login-action@v3.3.0
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for lab-framework container
if: github.ref == 'refs/heads/main'
id: meta-lab-framework
uses: docker/metadata-action@v5.5.1
- name: Extract metadata (tags, labels) for core container
id: meta-core
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.REPO_NAME }}-lab-framework
images: ${{ env.REGISTRY }}/${{ env.REPO_NAME }}-core
tags: |
type=sha
type=raw,value=latest
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push lab-framework image
uses: docker/build-push-action@v6.7.0
- name: Build and push lab-framework core image
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
push: ${{ github.ref == 'refs/heads/main' }}
tags: ${{ steps.meta-lab-framework.outputs.tags }}
labels: ${{ steps.meta-lab-framework.outputs.labels }}
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
tags: ${{ steps.meta-core.outputs.tags }}
labels: ${{ steps.meta-core.outputs.labels }}

8 changes: 4 additions & 4 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 22.x
uses: actions/setup-node@v4
- uses: actions/checkout@v7
- name: Use Node.js 24.x
uses: actions/setup-node@v7
with:
node-version: 22.x
node-version: 24.x
- name: Check Node.js version
run: node -pe process.versions
- name: Install dependencies
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: PR Tests

on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24.x
cache: npm

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm run test:ci
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ next-env.d.ts

.env
report.json

# Used in lab content
nginx-api.conf
ssl
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
"settings.json",
"jsonconfig.json"
],
"typescript.validate.enable": true,
"js/ts.validate.enabled": true,
"eslint.useFlatConfig": true,
"jest.runMode": "on-demand",
"chat.tools.terminal.autoApprove": {
"npx jest": true
},
}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

FROM node:22-alpine AS base
FROM node:24-alpine AS base

# Install dependencies only when needed
FROM base AS deps
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ You can start editing the lab markdown page by modifying `app/docs/nginx-one.mdx

The lab framework runs in Docker, so the it must be installed in the host system. Additionally, the lab framework container will use the host's docker API to manage containers. The host system needs to expose the Docker API over a network so that the lab framework container can connect to it. This approach involves some setup and is more secure when properly configured, especially when using TLS for encrypted communication.

Assuming a host system of Ubuntu 22.04, docker can be installed using the following:
Assuming a host system of Ubuntu 24.04, docker can be installed using the following:

```shell
sudo apt install -y ca-certificates curl gnupg lsb-release
Expand Down Expand Up @@ -87,7 +87,7 @@ sudo chmod 0666 /var/run/docker.sock

```

A `docker-compose.yaml` file has been provided to orchestrate the lab framework and redis containers. In order for the lab framework to create additional containers required by the lab author, the docker calls need to use the host's docker daemon. The above steps enable mounting docker.sock as a volume into the container.
A `docker-compose.yaml` file has been provided to orchestrate the lab framework containers. In order for the lab framework to create additional containers required by the lab author, the docker calls need to use the host's docker daemon. The above steps enable mounting docker.sock as a volume into the container.

If this isn't preferred (or will not work), the host's docker daemon has been configured to allow remote hosts to call the docker API. To use this, you will specify a custom host via the `-H` parameter in order to connect to the hosts Docker API. Example:

Expand Down
18 changes: 18 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,21 @@ import "@testing-library/jest-dom";

import { enableFetchMocks } from 'jest-fetch-mock';
enableFetchMocks();

jest.mock("next/image", () => ({
__esModule: true,
default: (props: {
src?: string;
alt?: string;
[key: string]: unknown;
}) => {
const React = require("react");
const { src, alt, ...rest } = props;

return React.createElement("img", {
...rest,
alt: alt ?? "",
src: typeof src === "string" ? src : "",
});
},
}));
11 changes: 11 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ const nextConfig = {
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
reactStrictMode: true,
output: "standalone",
allowedDevOrigins: ["[::1]", "localhost", "127.0.0.1"],
serverExternalPackages: ["dockerode"],
images: {
domains: ["raw.githubusercontent.com"],
remotePatterns: [
{
protocol: "https",
hostname: "raw.githubusercontent.com",
pathname: "/**",
},
],
},
};

export default nextConfig;
Loading