add laserweb service (#239)

* add laserweb to workflows
* add laserweb dockerfile and compose service
* add laserweb readme and profile instructions
* build: fall back to version extracted from dockerfile if upstream has no tags and limit backfill if upstream has less tags than backfill requests
This commit is contained in:
Markus Küffner
2025-11-29 20:23:26 +01:00
committed by GitHub
parent 777db25958
commit eddc958390
10 changed files with 141 additions and 10 deletions

View File

@@ -30,6 +30,8 @@ jobs:
- docker/moonraker/**
ustreamer:
- docker/ustreamer/**
laserweb:
- docker/laserweb/**
build:
needs: changes
if: ${{ needs.changes.outputs.apps != '' && toJson(fromJson(needs.changes.outputs.apps)) != '[]' }}

View File

@@ -12,6 +12,7 @@ jobs:
- klipperscreen
- moonraker
- ustreamer
- laserweb
runs-on: ubuntu-24.04
steps:
- name: "[prind] checkout"

View File

@@ -42,6 +42,11 @@ jobs:
- .github/workflows/image-build-review.yaml
- scripts/build/**
- docker/ustreamer/**
laserweb:
- .github/actions/**
- .github/workflows/image-build-review.yaml
- scripts/build/**
- docker/laserweb/**
build:
needs: changes
if: ${{ needs.changes.outputs.apps != '' && toJson(fromJson(needs.changes.outputs.apps)) != '[]' }}

View File

@@ -35,6 +35,9 @@ jobs:
ustreamer:
- .github/workflows/image-docs-publish.yaml
- docker/moonraker/README.md
laserweb:
- .github/workflows/image-docs-publish.yaml
- docker/laserweb/README.md
dockerhub-description:
needs: changes
if: ${{ needs.changes.outputs.apps != '' && toJson(fromJson(needs.changes.outputs.apps)) != '[]' }}

View File

@@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
<!--
## [Unreleased]
### Added
* **New Service:** LaserWeb4 #239
### Fixed
### Changed
### Removed

View File

@@ -26,6 +26,7 @@ With a single command, you can start up Klipper and its accompanying application
|<img src="https://raw.githubusercontent.com/Donkie/Spoolman/master/client/icons/spoolman.svg" width=30px>|[Spoolman](https://github.com/Donkie/Spoolman)|`upstream`|[Additional Profiles](#spoolman)|
|<img src="https://avatars.githubusercontent.com/u/41749659?s=200&v=4" width=30px>|[µStreamer](https://github.com/pikvm/ustreamer)|prind @ [docker/ustreamer](docker/ustreamer)|[Add your Configuration](#add-your-configuration-to-docker-composeoverrideyaml)<br>[Multiple Webcams](https://github.com/mkuf/prind?tab=readme-ov-file#multiple-webcams)|
|<img src="https://octoeverywhere.com/img/logo/logo_maskable.svg" width=30px>|[OctoEverywhere](https://octoeverywhere.com)|`upstream`|[Additional Profiles](#octoeverywhere)|
|<img src="https://raw.githubusercontent.com/ssendev/LaserWeb4/refs/heads/v4.1/src/favicon.ico" width=30px>|[LaserWeb4](https://laserweb.yurl.ch/)|prind @ [docker/laserweb](docker/laserweb)|[Additional Profiles](#laserweb)|
</details>
@@ -245,6 +246,17 @@ After the stack has started, get the logs of the octoeverywhere service to retri
docker compose logs octoeverywhere
```
#### laserweb
[LaserWeb4](https://laserweb.yurl.ch/) can be enabled via the `laserweb` Profile. The image is based on [a fork](https://github.com/ssendev/LaserWeb4) which includes support for connecting to Moonraker.
```
docker compose --profile fluidd --profile laserweb up -d
```
After the stack has started, navigate to `http://<yourprinter>/laserweb/`.
In the `Comms` tab, select `SERVER: Moonraker`, add your Printers IP Address as `SERVER IP` and click `Connect`
## Updating
Images are built daily and tagged with `latest` and the [git description](https://git-scm.com/docs/git-describe#_examples) of the remote repo.
Example:

View File

@@ -204,6 +204,20 @@ services:
labels:
org.prind.service: octoeverywhere
laserweb:
image: mkuf/laserweb:latest
restart: unless-stopped
profiles:
- laserweb
labels:
org.prind.service: laserweb
traefik.enable: true
traefik.http.services.laserweb.loadbalancer.server.port: 80
traefik.http.routers.laserweb.rule: PathPrefix(`/laserweb`)
traefik.http.routers.laserweb.entrypoints: web
traefik.http.routers.laserweb.middlewares: strip-laserweb@docker
traefik.http.middlewares.strip-laserweb.stripprefix.prefixes: /laserweb
## Accompanying Services/Infra
##

View File

@@ -0,0 +1,20 @@
FROM --platform=$BUILDPLATFORM node:22-bookworm AS build
ARG REPO=https://github.com/ssendev/LaserWeb4
ARG VERSION=v4.1
WORKDIR /opt
RUN git clone ${REPO} laserweb \
&& cd laserweb \
&& git checkout ${VERSION} \
&& git submodule init \
&& git submodule update
WORKDIR /opt/laserweb
RUN npm ci --force
RUN npm run bundle-prod
FROM nginx:alpine AS run
COPY --from=build /opt/laserweb/dist /usr/share/nginx/html
RUN chown -R nginx:nginx /usr/share/nginx/html

52
docker/laserweb/README.md Normal file
View File

@@ -0,0 +1,52 @@
This Image is built and used by [prind](.).
# Laserweb4 packaged in Docker
## What is Laserweb?
> LaserWeb / CNCWeb is a full CAM & Machine Control Program for Laser/CNC/Plotter/Plasma applications.
_via https://laserweb.yurl.ch/_
This image contains the frontend from https://github.com/ssendev/LaserWeb4.
It still requires a compatible backend like [moonraker](https://github.com/Arksine/moonraker) or the [lw.comm-server](https://github.com/LaserWeb/lw.comm-server) if machine control is desired.
## Usage
#### Run
```bash
docker run -p 80:80 mkuf/laserweb:latest
```
#### Compose
```yaml
services:
laserweb:
image: mkuf/laserweb:latest
ports:
- "80:80"
```
## Defaults
|Entity|Description|
|---|---|
|User| `nginx (101:101)` |
|Workdir||
|Entrypoint|`/docker-entrypoint.sh`|
|Cmd|`nginx -g daemon off;`|
## Ports
|Port|Description|
|---|---|
|`80/tcp`|Default HTTP Port|
## Volumes
none
## Tags
|Tag|Description|Static|
|---|---|---|
|`latest`|Refers to the most recent runtime Image.|May point to a new build within 24h, depending on code changes in the upstream repository.|
|`<git description>` <br>eg: `v4.1-1-g3c7564d`|Refers to a specific git description in the upstream repository. |Yes|
## Targets
|Target|Description|Pushed|
|---|---|---|
|`build`|Pull Upstream Codebase and build application|No|
|`run`|Default runtime Image based on `library/nginx`|Yes|

View File

@@ -57,7 +57,10 @@ with open(args.config,"r") as file:
context = "docker/" + args.app
dockerfile = context + "/Dockerfile"
build = {
"upstream": None,
"upstream": {
"url": None,
"ref": None
},
"targets": [],
"versions": {},
"summary": {
@@ -85,7 +88,12 @@ with open(dockerfile) as file:
# upstream repository url
repo = re.findall(r'ARG REPO.*', line)
if repo:
build["upstream"] = repo[0].split('=')[1]
build["upstream"]["url"] = repo[0].split('=')[1]
# upstream version
ref = re.findall(r'ARG VERSION.*', line)
if ref:
build["upstream"]["ref"] = ref[0].split('=')[1]
# build targets
target = re.findall(r'FROM .* AS .*', line)
@@ -95,9 +103,9 @@ with open(dockerfile) as file:
if args.upstream:
logger.warning("Upstream Repo has been overwritten to: " + args.upstream )
build["upstream"] = args.upstream
build["upstream"]["url"] = args.upstream
else:
logger.info("Found upstream repository: " + build["upstream"])
logger.info("Found upstream repository: " + build["upstream"]["url"])
if len(build["targets"]) < 1:
logger.error("No targets found. Nothing to build")
@@ -115,16 +123,29 @@ else:
# extract info from upstream
logger.info("Cloning Upstream repository")
tmp = tempfile.TemporaryDirectory()
upstream_repo = git.Repo.clone_from(build["upstream"], tmp.name)
upstream_repo = git.Repo.clone_from(build["upstream"]["url"], tmp.name)
logger.info("Generating Versions from Upstream repository")
## latest
latest_version = upstream_repo.git.describe("--tags")
try:
## latest
latest_version = upstream_repo.git.describe("--tags")
except:
## if latest does not exist, use VERSION from dockerfile
logger.warning("Upstream has no tags, using " + build["upstream"]["ref"] + " as latest")
latest_version = build["upstream"]["ref"]
build["versions"][latest_version] = { "latest": True }
## tags
upstream_repo_sorted_tags = upstream_repo.git.tag("-l", "--sort=v:refname").split('\n')
for i in range(1,args.backfill+1):
upstream_repo_sorted_tags = upstream_repo.git.tag("-l", "--sort=v:refname").splitlines()
upstream_repo_number_of_tags = len(upstream_repo_sorted_tags)
if upstream_repo_number_of_tags < args.backfill:
logger.warning("Requested backfill is higher than the number of upstream tags. Limiting backfill to " + str(upstream_repo_number_of_tags))
backfill = upstream_repo_number_of_tags
else:
backfill = args.backfill
for i in range(1,backfill+1):
tag = upstream_repo_sorted_tags[-abs(i)]
if tag not in build["versions"].keys():
build["versions"][tag] = { "latest": False }
@@ -171,7 +192,7 @@ for version in build["versions"].keys():
docker.buildx.build(
# Build specific
context_path = context,
build_args = {"REPO": build["upstream"], "VERSION": version},
build_args = {"REPO": build["upstream"]["url"], "VERSION": version},
platforms = args.platform,
target = target,
push = args.push,