From d162d54f911b737d816cfc8980e7c7de50c587ee Mon Sep 17 00:00:00 2001 From: Vladimir Looze Date: Sat, 18 Jul 2026 17:30:28 +0300 Subject: [PATCH 1/5] dos2unix --- .github/workflows/bundle.yml | 32 +-- .github/workflows/prospector.yml | 30 +-- .github/workflows/release.yml | 64 +++--- apps/demo-segmented-clock/README.md | 3 + apps/demo-segmented-clock/index.html | 75 ++++++ apps/demo-segmented-clock/index.js | 72 ++++++ apps/demo-segmented-clock/minify.json | 6 + apps/demo-segmented-clock/uglify.json | 15 ++ qr.py | 318 +++++++++++++------------- 9 files changed, 393 insertions(+), 222 deletions(-) create mode 100644 apps/demo-segmented-clock/README.md create mode 100644 apps/demo-segmented-clock/index.html create mode 100644 apps/demo-segmented-clock/index.js create mode 100644 apps/demo-segmented-clock/minify.json create mode 100644 apps/demo-segmented-clock/uglify.json diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index a3b512e..c2a61e1 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -1,16 +1,16 @@ -name: Test Bundles - -on: - pull_request: - -jobs: - bundle: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v2 - - name: build ci environment - run: | - docker build --pull --rm -t qrapps:ci . - - name: generate QR - run: | - docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles +name: Test Bundles + +on: + pull_request: + +jobs: + bundle: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v2 + - name: build ci environment + run: | + docker build --pull --rm -t qrapps:ci . + - name: generate QR + run: | + docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles diff --git a/.github/workflows/prospector.yml b/.github/workflows/prospector.yml index 6937ec3..9c1fd14 100644 --- a/.github/workflows/prospector.yml +++ b/.github/workflows/prospector.yml @@ -1,16 +1,16 @@ -name: Prospector - -on: - pull_request: - -jobs: - prospector: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v2 - - name: build ci environment - run: | - docker build --pull --rm -t qrapps:ci . - - name: lint with prospector - run: | +name: Prospector + +on: + pull_request: + +jobs: + prospector: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v2 + - name: build ci environment + run: | + docker build --pull --rm -t qrapps:ci . + - name: lint with prospector + run: | docker run --rm -v .:/src -w /src --entrypoint=prospector qrapps:ci \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27c5cab..91cd9b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,32 +1,32 @@ -name: Release Bundles - -on: - push: - branches: [ main ] - -jobs: - bundle: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v2 - - name: build ci environment - run: | - docker build --pull --rm -t qrapps:ci . - - name: generate QR - run: | - docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles - - name: generate README.md - run: | - rm -rf docs - mv bundles docs - sudo cp -at docs .google/* - sudo git checkout -- docs/_config.yml - docker run --rm -v .:/src -w /src qrapps:ci release.py - - name: release - run: | - git config --global user.email "woldemar@mimas.ru" - git config --global user.name "Vladimir Looze" - git add docs - git commit -m "release new bundles" - git push --force origin main - +name: Release Bundles + +on: + push: + branches: [ main ] + +jobs: + bundle: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v2 + - name: build ci environment + run: | + docker build --pull --rm -t qrapps:ci . + - name: generate QR + run: | + docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles + - name: generate README.md + run: | + rm -rf docs + mv bundles docs + sudo cp -at docs .google/* + sudo git checkout -- docs/_config.yml + docker run --rm -v .:/src -w /src qrapps:ci release.py + - name: release + run: | + git config --global user.email "woldemar@mimas.ru" + git config --global user.name "Vladimir Looze" + git add docs + git commit -m "release new bundles" + git push --force origin main + diff --git a/apps/demo-segmented-clock/README.md b/apps/demo-segmented-clock/README.md new file mode 100644 index 0000000..2a2b55e --- /dev/null +++ b/apps/demo-segmented-clock/README.md @@ -0,0 +1,3 @@ +# Segmented Clock + +A digital clock display using 7-segment display style. Shows hours, minutes, and seconds with blinking colons between them. \ No newline at end of file diff --git a/apps/demo-segmented-clock/index.html b/apps/demo-segmented-clock/index.html new file mode 100644 index 0000000..12bdb35 --- /dev/null +++ b/apps/demo-segmented-clock/index.html @@ -0,0 +1,75 @@ + + + + + + \ No newline at end of file diff --git a/apps/demo-segmented-clock/index.js b/apps/demo-segmented-clock/index.js new file mode 100644 index 0000000..617bd16 --- /dev/null +++ b/apps/demo-segmented-clock/index.js @@ -0,0 +1,72 @@ +const segments = { + 0: ['a','b','c','d','e','f'], + 1: ['b','c'], + 2: ['a','b','g','e','d'], + 3: ['a','b','g','c','d'], + 4: ['f','g','b','c'], + 5: ['a','f','g','c','d'], + 6: ['a','f','g','c','d','e'], + 7: ['a','b','c'], + 8: ['a','b','c','d','e','f','g'], + 9: ['a','b','c','d','f','g'] +}; + +function createDigit() { + const digit = document.createElement('div'); + digit.className = 'digit'; + ['a','b','c','d','e','f','g'].forEach(seg => { + const segment = document.createElement('div'); + segment.className = `segment ${seg}`; + digit.appendChild(segment); + }); + return digit; +} + +function createColon() { + const colon = document.createElement('div'); + colon.className = 'colon'; + const dot1 = document.createElement('div'); + dot1.className = 'dot top'; + const dot2 = document.createElement('div'); + dot2.className = 'dot bottom'; + colon.appendChild(dot1); + colon.appendChild(dot2); + return colon; +} + +function updateDigit(digit, number) { + const activeSegments = segments[number] || []; + digit.querySelectorAll('.segment').forEach(seg => { + seg.style.opacity = activeSegments.includes(seg.className.split(' ')[1]) ? '1' : '0.1'; + }); +} + +function updateClock() { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + + const digits = document.querySelectorAll('.digit'); + updateDigit(digits[1], hours[0]); + updateDigit(digits[2], hours[1]); + updateDigit(digits[4], minutes[0]); + updateDigit(digits[5], minutes[1]); + updateDigit(digits[7], seconds[0]); + updateDigit(digits[8], seconds[1]); +} + +function init() { + const clock = document.getElementById('clock'); + // Create 6 digits (2 for hours, 2 for minutes, 2 for seconds) + for (let i = 0; i < 6; i++) { + clock.appendChild(createDigit()); + if (i === 1 || i === 3) { + clock.appendChild(createColon()); + } + } + updateClock(); + setInterval(updateClock, 1000); +} + +init(); \ No newline at end of file diff --git a/apps/demo-segmented-clock/minify.json b/apps/demo-segmented-clock/minify.json new file mode 100644 index 0000000..a7b6afa --- /dev/null +++ b/apps/demo-segmented-clock/minify.json @@ -0,0 +1,6 @@ +{ + "collapseWhitespace": true, + "minifyJS": true, + "minifyCSS": true, + "removeOptionalTags": true +} \ No newline at end of file diff --git a/apps/demo-segmented-clock/uglify.json b/apps/demo-segmented-clock/uglify.json new file mode 100644 index 0000000..48099a8 --- /dev/null +++ b/apps/demo-segmented-clock/uglify.json @@ -0,0 +1,15 @@ +{ + "mangle": true, + "toplevel": true, + "compress": { + "sequences": true, + "dead_code": true, + "conditionals": true, + "loops": true, + "booleans": true, + "unused": true, + "if_return": true, + "join_vars": true, + "drop_console": true + } +} \ No newline at end of file diff --git a/qr.py b/qr.py index f6d7254..bb32436 100644 --- a/qr.py +++ b/qr.py @@ -1,159 +1,159 @@ -import os -import shutil -import argparse -import subprocess -import qrcode -from qrcode.image.styledpil import StyledPilImage -import qrcode.constants - - -def html_to_url(html_filename, txt_filename): - with open(html_filename, 'rb') as f_html: - html_text = f_html.read().decode('utf-8') - - print(f'HTML src size: {len(html_text)}') - - html_text_escaped = html_text - - # % operator (division reminder) is rarely used in JS - # but can still appear and mess up percent-encoding of Data URL. - html_text_escaped = html_text_escaped.replace('%', '%' + '25') - - # You might think urllib.parse.quote is a better solution, - # but it escapes too much stuff and bloats data, - # significantly reducing effective QR capacity - html_text_escaped = html_text_escaped.replace('#', '%' + '23') - - print(f'HTML rdy size: {len(html_text_escaped)}') - data_url = 'data:text/html,' + html_text_escaped - qr_data_encoded = data_url.encode('ascii') - with open(txt_filename, 'wb') as f_txt: - f_txt.write(qr_data_encoded) - - -def url_to_qr(url_filename, png_filename): - qr_code = qrcode.QRCode( - version=None, - image_factory=StyledPilImage, - border=4, - box_size=2, - error_correction=qrcode.constants.ERROR_CORRECT_L, - ) - with open(url_filename, 'rb') as f_url: - html_text = f_url.read().decode('utf-8') - qr_code.add_data(html_text) - print('QR data dump:') - for i in qr_code.data_list: - print(f'- bytes={len(i.data)} mode={i.mode} content={i.data[:64]}') - print(f'QR code fit size: {qr_code.best_fit()}') - qr_code.make(fit=True) - img = qr_code.make_image() - img.save(png_filename) - - -def inline_scripts(html_filename, out_filename): - subprocess.check_call([ - 'inline-script-tags', - html_filename, - out_filename]) - - -def inline_stylesheets(html_filename, out_filename): - subprocess.check_call([ - 'inline-stylesheets', - html_filename, - out_filename]) - - -def minify(html_filename, out_filename, config_file): - subprocess.check_call([ - 'html-minifier', - '--config-file', config_file, - html_filename, - '--output', out_filename]) - - -def uglify(html_dir, html_tmp_dir): - def js_filter(file_name): - return file_name.endswith('.js') - list_dir = os.listdir(html_dir) - js_files = list( - os.path.join(html_dir, x) for x in filter(js_filter, list_dir)) - js_tmp_files = list( - os.path.join(html_tmp_dir, x) for x in filter(js_filter, list_dir)) - js_pairs = zip(js_files, js_tmp_files) - for js_pair in js_pairs: - subprocess.check_call([ - 'uglifyjs', - '--output', js_pair[1], - '--warn', - '--config-file', os.path.join(html_dir, 'uglify.json'), - '--', js_pair[0]]) - - -def parse_args(): - parser = argparse.ArgumentParser('Convert HTML page into QR code') - parser.add_argument('--htmldir', - help='directory with html page') - parser.add_argument('--builddir', - help='directory to put bundle and QR code into') - return parser.parse_args() - - -def main(): - args = parse_args() - - html_tmp_dir = os.path.basename(args.htmldir) + '.tmp' - app_name = os.path.basename(args.htmldir) - deploy_directory = os.path.join(args.builddir, app_name) - if not os.path.isdir(deploy_directory): - os.makedirs(deploy_directory) - - bundle_filename = os.path.join(html_tmp_dir, 'index.html') - mini_filename = os.path.join(deploy_directory, 'index.html') - url_filename = os.path.join(deploy_directory, 'qr.txt') - png_filename = os.path.join(deploy_directory, 'qr.png') - - if os.path.isdir(html_tmp_dir): - shutil.rmtree(html_tmp_dir) - shutil.copytree(args.htmldir, html_tmp_dir) - uglify(args.htmldir, html_tmp_dir) - - tmp_file = os.path.join(html_tmp_dir, 'index.html.tmp') - inline_scripts( - os.path.join(html_tmp_dir, 'index.html'), - tmp_file - ) - inline_stylesheets( - tmp_file, - bundle_filename - ) - os.remove(tmp_file) - bundle_size = os.stat(bundle_filename).st_size - print(f'Combined size: {bundle_size}') - - minify( - bundle_filename, - mini_filename, - os.path.join(html_tmp_dir, 'minify.json') - ) - mini_size = os.stat(mini_filename).st_size - print(f'Minified size: {mini_size}') - - html_to_url( - mini_filename, - url_filename - ) - - url_size = os.stat(url_filename).st_size - print(f'URL data size: {url_size} of 2953 (QR capacity)') - - if url_size <= 2953: - url_to_qr( - url_filename, - png_filename - ) - - -if __name__ == '__main__': - main() +import os +import shutil +import argparse +import subprocess +import qrcode +from qrcode.image.styledpil import StyledPilImage +import qrcode.constants + + +def html_to_url(html_filename, txt_filename): + with open(html_filename, 'rb') as f_html: + html_text = f_html.read().decode('utf-8') + + print(f'HTML src size: {len(html_text)}') + + html_text_escaped = html_text + + # % operator (division reminder) is rarely used in JS + # but can still appear and mess up percent-encoding of Data URL. + html_text_escaped = html_text_escaped.replace('%', '%' + '25') + + # You might think urllib.parse.quote is a better solution, + # but it escapes too much stuff and bloats data, + # significantly reducing effective QR capacity + html_text_escaped = html_text_escaped.replace('#', '%' + '23') + + print(f'HTML rdy size: {len(html_text_escaped)}') + data_url = 'data:text/html,' + html_text_escaped + qr_data_encoded = data_url.encode('ascii') + with open(txt_filename, 'wb') as f_txt: + f_txt.write(qr_data_encoded) + + +def url_to_qr(url_filename, png_filename): + qr_code = qrcode.QRCode( + version=None, + image_factory=StyledPilImage, + border=4, + box_size=2, + error_correction=qrcode.constants.ERROR_CORRECT_L, + ) + with open(url_filename, 'rb') as f_url: + html_text = f_url.read().decode('utf-8') + qr_code.add_data(html_text) + print('QR data dump:') + for i in qr_code.data_list: + print(f'- bytes={len(i.data)} mode={i.mode} content={i.data[:64]}') + print(f'QR code fit size: {qr_code.best_fit()}') + qr_code.make(fit=True) + img = qr_code.make_image() + img.save(png_filename) + + +def inline_scripts(html_filename, out_filename): + subprocess.check_call([ + 'inline-script-tags', + html_filename, + out_filename]) + + +def inline_stylesheets(html_filename, out_filename): + subprocess.check_call([ + 'inline-stylesheets', + html_filename, + out_filename]) + + +def minify(html_filename, out_filename, config_file): + subprocess.check_call([ + 'html-minifier', + '--config-file', config_file, + html_filename, + '--output', out_filename]) + + +def uglify(html_dir, html_tmp_dir): + def js_filter(file_name): + return file_name.endswith('.js') + list_dir = os.listdir(html_dir) + js_files = list( + os.path.join(html_dir, x) for x in filter(js_filter, list_dir)) + js_tmp_files = list( + os.path.join(html_tmp_dir, x) for x in filter(js_filter, list_dir)) + js_pairs = zip(js_files, js_tmp_files) + for js_pair in js_pairs: + subprocess.check_call([ + 'uglifyjs', + '--output', js_pair[1], + '--warn', + '--config-file', os.path.join(html_dir, 'uglify.json'), + '--', js_pair[0]]) + + +def parse_args(): + parser = argparse.ArgumentParser('Convert HTML page into QR code') + parser.add_argument('--htmldir', + help='directory with html page') + parser.add_argument('--builddir', + help='directory to put bundle and QR code into') + return parser.parse_args() + + +def main(): + args = parse_args() + + html_tmp_dir = os.path.basename(args.htmldir) + '.tmp' + app_name = os.path.basename(args.htmldir) + deploy_directory = os.path.join(args.builddir, app_name) + if not os.path.isdir(deploy_directory): + os.makedirs(deploy_directory) + + bundle_filename = os.path.join(html_tmp_dir, 'index.html') + mini_filename = os.path.join(deploy_directory, 'index.html') + url_filename = os.path.join(deploy_directory, 'qr.txt') + png_filename = os.path.join(deploy_directory, 'qr.png') + + if os.path.isdir(html_tmp_dir): + shutil.rmtree(html_tmp_dir) + shutil.copytree(args.htmldir, html_tmp_dir) + uglify(args.htmldir, html_tmp_dir) + + tmp_file = os.path.join(html_tmp_dir, 'index.html.tmp') + inline_scripts( + os.path.join(html_tmp_dir, 'index.html'), + tmp_file + ) + inline_stylesheets( + tmp_file, + bundle_filename + ) + os.remove(tmp_file) + bundle_size = os.stat(bundle_filename).st_size + print(f'Combined size: {bundle_size}') + + minify( + bundle_filename, + mini_filename, + os.path.join(html_tmp_dir, 'minify.json') + ) + mini_size = os.stat(mini_filename).st_size + print(f'Minified size: {mini_size}') + + html_to_url( + mini_filename, + url_filename + ) + + url_size = os.stat(url_filename).st_size + print(f'URL data size: {url_size} of 2953 (QR capacity)') + + if url_size <= 2953: + url_to_qr( + url_filename, + png_filename + ) + + +if __name__ == '__main__': + main() From 6d6ca1e59038978401469d149435b98981868993 Mon Sep 17 00:00:00 2001 From: Vladimir Looze Date: Sat, 18 Jul 2026 18:28:25 +0300 Subject: [PATCH 2/5] simplify workflow with docker compose --- .github/workflows/bundle.yml | 8 +-- .github/workflows/prospector.yml | 8 +-- .github/workflows/release.yml | 13 ++--- Dockerfile | 39 ++++--------- README.md | 9 +-- docker-compose.yml | 95 ++++++++++++++++++++++++++++++++ docker.md | 72 ++++++++++++++++++++++++ 7 files changed, 197 insertions(+), 47 deletions(-) create mode 100644 docker-compose.yml create mode 100644 docker.md diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index c2a61e1..571413a 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -1,4 +1,4 @@ -name: Test Bundles +name: Test Bundles on: pull_request: @@ -7,10 +7,10 @@ jobs: bundle: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: build ci environment run: | - docker build --pull --rm -t qrapps:ci . + docker-compose build - name: generate QR run: | - docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles + docker-compose run --rm qrs diff --git a/.github/workflows/prospector.yml b/.github/workflows/prospector.yml index 9c1fd14..a98591f 100644 --- a/.github/workflows/prospector.yml +++ b/.github/workflows/prospector.yml @@ -1,4 +1,4 @@ -name: Prospector +name: Prospector on: pull_request: @@ -7,10 +7,10 @@ jobs: prospector: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: build ci environment run: | - docker build --pull --rm -t qrapps:ci . + docker-compose build - name: lint with prospector run: | - docker run --rm -v .:/src -w /src --entrypoint=prospector qrapps:ci \ No newline at end of file + docker-compose run --rm lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91cd9b3..381aa63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release Bundles +name: Release Bundles on: push: @@ -8,25 +8,24 @@ jobs: bundle: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: build ci environment run: | - docker build --pull --rm -t qrapps:ci . + docker-compose build - name: generate QR run: | - docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles + docker-compose run --rm qrs - name: generate README.md run: | rm -rf docs mv bundles docs sudo cp -at docs .google/* sudo git checkout -- docs/_config.yml - docker run --rm -v .:/src -w /src qrapps:ci release.py + docker-compose run --rm release - name: release run: | git config --global user.email "woldemar@mimas.ru" git config --global user.name "Vladimir Looze" git add docs git commit -m "release new bundles" - git push --force origin main - + git push --force origin main diff --git a/Dockerfile b/Dockerfile index 352fed5..33650f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,23 @@ -# Self-contained environment, useful commands: -# -# Build environment image: -# docker build --pull --rm -t qrapps:ci . -# -# After image has been built, you can run: -# -# Interactive shell, useful for debugging: -# docker run --rm -it -v .:/src -w /src --entrypoint=bash qrapps:ci -# -# Code quality check: -# docker run --rm -v .:/src -w /src --entrypoint=prospector qrapps:ci -# -# Convert everything to QR codes: -# docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles -# -# Convert single app to QR code: -# docker run --rm -v .:/src -w /src qrapps:ci qr.py --htmldir=apps/demo-clock --builddir=bundles +# Self-contained environment for QR Apps project -from debian:12 +FROM debian:12 RUN apt-get -y update && apt-get install -y --no-install-recommends \ python3-pip=23.0.1+dfsg-1 \ python3-venv=3.11.2-1+b1 \ npm=9.2.0~ds1-1 \ - && \ - rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* -RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \ - --mount=type=bind,source=requirements-contrib.txt,target=requirements-contrib.txt \ - python3 -m venv /venv && \ +COPY requirements.txt /tmp/requirements.txt +COPY requirements-contrib.txt /tmp/requirements-contrib.txt + +RUN python3 -m venv /venv && \ /venv/bin/python3 -m pip install --upgrade pip && \ - /venv/bin/python3 -m pip install -r requirements.txt && \ - /venv/bin/python3 -m pip install -r requirements-contrib.txt + /venv/bin/python3 -m pip install -r /tmp/requirements.txt && \ + /venv/bin/python3 -m pip install -r /tmp/requirements-contrib.txt RUN npm install -g uglify-js html-minifier inline-scripts -ENV PATH="/venv/bin:${PATH}" - +ENV PATH=/venv/bin:$PATH +WORKDIR /src ENTRYPOINT ["/venv/bin/python3"] \ No newline at end of file diff --git a/README.md b/README.md index 90aff0c..b3b5ab0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CodeQL](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml) +[![CodeQL](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml) [![GutHub Pages](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment) [![prospector](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml) @@ -23,8 +23,9 @@ Since QR code content is [Data URL](https://en.wikipedia.org/wiki/Data_URI_schem - `README.md` - app description (see `demo-*` apps for file content examples) -- Use build instructions from [Dockerfile](Dockerfile) - +- Use build instructions from [Dockerfile](Dockerfile) +- See [docker.md](docker.md) for Docker Compose commands + ## Why would you do that? For fun, challenge and as a demonstration of modern browser capabilities. Fitting entire app in one QR code also keeps them relatively small and simple... until author tries to squeeze code too hard and it becomes unreadeable 😂 @@ -35,4 +36,4 @@ This is also a good source of compact code examples. Some of them were inspired Each app is HTML/JS/CSS webpage contained within own directory in `apps`. Scripts `qr.py` and `qrs.py` will convert each app into [QR code](https://en.wikipedia.org/wiki/QR_code) by 'minifying'/'uglyfying' it and inlining all JS/CSS/whatever into single HTML. This HTML then encoded as [Data URL](https://en.wikipedia.org/wiki/Data_URI_scheme) and QR code of said URL is generated. ## Collection of QR apps! -Generated automatically on push and published to https://worldemar.github.io/qrapps/ +Generated automatically on push and published to https://worldemar.github.io/qrapps/ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e41d838 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,95 @@ +# Docker Compose for QR Apps project + +services: + # Interactive shell for debugging + shell: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: bash + stdin_open: true + tty: true + + # Code quality check with prospector + lint: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: prospector + + # Convert all apps to QR codes + qrs: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + command: ["qrs.py", "--htmldirs=apps", "--builddir=bundles"] + + # Convert single app to QR code + # Usage: docker-compose run --rm qr --htmldir=apps/demo-clock --builddir=bundles + qr: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: ["python3", "qr.py"] + + # Generate release documentation + release: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + command: ["release.py"] + + # Run uglify-js + uglifyjs: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: uglifyjs + + # Run html-minifier + html-minifier: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: html-minifier + + # Run inline-script-tags + inline-script-tags: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: inline-script-tags + + # Run inline-stylesheets + inline-stylesheets: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/src + working_dir: /src + entrypoint: inline-stylesheets diff --git a/docker.md b/docker.md new file mode 100644 index 0000000..a693e7c --- /dev/null +++ b/docker.md @@ -0,0 +1,72 @@ +# Docker Compose Commands + +This project uses Docker Compose to simplify all development and build commands. + +## Prerequisites + +- Docker Desktop installed +- Docker Compose (v2+) installed + +## Build the Environment + +```bash +docker-compose build +``` + +## Interactive Shell for Debugging + +```bash +docker-compose run --rm shell +``` + +Opens an interactive bash shell with all dependencies installed and your local code mounted. + +## Code Quality Check + +```bash +docker-compose run --rm lint +``` + +Runs prospector linting on Python code. + +## Convert Everything to QR Codes + +```bash +docker-compose run --rm qrs +``` + +Converts all apps in `apps/` directory to QR codes in `bundles/`. + +## Convert Single App to QR Code + +```bash +docker-compose run --rm qr --htmldir=apps/demo-clock --builddir=bundles +``` + +Replace `demo-clock` with any app directory name under `apps/`. + +## Release Bundles + +```bash +docker-compose run --rm release +``` + +Generates `docs/README.md` with links and QR codes for all apps. + +## Additional Utility Commands + +### Minification Tools + +```bash +# Uglify JS files +docker-compose run --rm uglifyjs [options] + +# Minify HTML +docker-compose run --rm html-minifier [options] + +# Inline script tags +docker-compose run --rm inline-script-tags + +# Inline stylesheets +docker-compose run --rm inline-stylesheets +``` From a5a1abbc6f6f29c13c422a0cdf92882c48927f65 Mon Sep 17 00:00:00 2001 From: Vladimir Looze Date: Sat, 18 Jul 2026 18:32:50 +0300 Subject: [PATCH 3/5] use ruff instead of prospector --- .github/workflows/{prospector.yml => ruff.yml} | 8 ++++---- README.md | 10 +++++----- docker-compose.yml | 4 ++-- docker.md | 2 +- requirements-contrib.txt | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) rename .github/workflows/{prospector.yml => ruff.yml} (76%) diff --git a/.github/workflows/prospector.yml b/.github/workflows/ruff.yml similarity index 76% rename from .github/workflows/prospector.yml rename to .github/workflows/ruff.yml index a98591f..0519304 100644 --- a/.github/workflows/prospector.yml +++ b/.github/workflows/ruff.yml @@ -1,16 +1,16 @@ -name: Prospector +name: ruff on: pull_request: jobs: - prospector: + ruff: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: build ci environment run: | docker-compose build - - name: lint with prospector + - name: lint with ruff run: | - docker-compose run --rm lint + docker-compose run --rm lint diff --git a/README.md b/README.md index b3b5ab0..1c25edc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![CodeQL](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml) [![GutHub Pages](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment) -[![prospector](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml) +[![ruff](https://github.com/worldemar/qrapps/actions/workflows/ruff.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/ruff.yml) # QR Code Apps @@ -23,9 +23,9 @@ Since QR code content is [Data URL](https://en.wikipedia.org/wiki/Data_URI_schem - `README.md` - app description (see `demo-*` apps for file content examples) -- Use build instructions from [Dockerfile](Dockerfile) -- See [docker.md](docker.md) for Docker Compose commands - +- Use build instructions from [Dockerfile](Dockerfile) +- See [docker.md](docker.md) for Docker Compose commands + ## Why would you do that? For fun, challenge and as a demonstration of modern browser capabilities. Fitting entire app in one QR code also keeps them relatively small and simple... until author tries to squeeze code too hard and it becomes unreadeable 😂 @@ -36,4 +36,4 @@ This is also a good source of compact code examples. Some of them were inspired Each app is HTML/JS/CSS webpage contained within own directory in `apps`. Scripts `qr.py` and `qrs.py` will convert each app into [QR code](https://en.wikipedia.org/wiki/QR_code) by 'minifying'/'uglyfying' it and inlining all JS/CSS/whatever into single HTML. This HTML then encoded as [Data URL](https://en.wikipedia.org/wiki/Data_URI_scheme) and QR code of said URL is generated. ## Collection of QR apps! -Generated automatically on push and published to https://worldemar.github.io/qrapps/ +Generated automatically on push and published to https://worldemar.github.io/qrapps/ diff --git a/docker-compose.yml b/docker-compose.yml index e41d838..e8c597e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: stdin_open: true tty: true - # Code quality check with prospector + # Code quality check with ruff lint: build: context: . @@ -21,7 +21,7 @@ services: volumes: - .:/src working_dir: /src - entrypoint: prospector + entrypoint: ["ruff", "check"] # Convert all apps to QR codes qrs: diff --git a/docker.md b/docker.md index a693e7c..bf1814a 100644 --- a/docker.md +++ b/docker.md @@ -27,7 +27,7 @@ Opens an interactive bash shell with all dependencies installed and your local c docker-compose run --rm lint ``` -Runs prospector linting on Python code. +Runs ruff linting on Python code. ## Convert Everything to QR Codes diff --git a/requirements-contrib.txt b/requirements-contrib.txt index b989fb4..1548e8d 100644 --- a/requirements-contrib.txt +++ b/requirements-contrib.txt @@ -1 +1 @@ -prospector==1.10.3 +ruff==0.15.22 From 562e6567939277c8c5e416e26d4b443280262548 Mon Sep 17 00:00:00 2001 From: Vladimir Looze Date: Sat, 18 Jul 2026 18:33:01 +0300 Subject: [PATCH 4/5] sort apps on webpage --- qrs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qrs.py b/qrs.py index b973f11..984f8a4 100644 --- a/qrs.py +++ b/qrs.py @@ -16,7 +16,7 @@ def parse_args(): def main(): args = parse_args() - for package in os.listdir(args.htmldirs): + for package in sorted(os.listdir(args.htmldirs)): print(f'Processing package {package}') subprocess.check_call([ sys.executable, 'qr.py', From 398451fc274620eab134db902047a61531ba45de Mon Sep 17 00:00:00 2001 From: Vladimir Looze Date: Sat, 18 Jul 2026 18:34:43 +0300 Subject: [PATCH 5/5] docker-compose -> docker compose --- .github/workflows/bundle.yml | 4 ++-- .github/workflows/release.yml | 8 ++++---- .github/workflows/ruff.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml index 571413a..7ff9b52 100644 --- a/.github/workflows/bundle.yml +++ b/.github/workflows/bundle.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - name: build ci environment run: | - docker-compose build + docker compose build - name: generate QR run: | - docker-compose run --rm qrs + docker compose run --rm qrs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 381aa63..b470594 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,21 +11,21 @@ jobs: - uses: actions/checkout@v4 - name: build ci environment run: | - docker-compose build + docker compose build - name: generate QR run: | - docker-compose run --rm qrs + docker compose run --rm qrs - name: generate README.md run: | rm -rf docs mv bundles docs sudo cp -at docs .google/* sudo git checkout -- docs/_config.yml - docker-compose run --rm release + docker compose run --rm release - name: release run: | git config --global user.email "woldemar@mimas.ru" git config --global user.name "Vladimir Looze" git add docs git commit -m "release new bundles" - git push --force origin main + git push --force origin main diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 0519304..a44bd89 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - name: build ci environment run: | - docker-compose build + docker compose build - name: lint with ruff run: | - docker-compose run --rm lint + docker compose run --rm lint