fix(ci): revert QR skip hack, add CLAUDE.md rule for native QR libs

QR tests MUST use native language libraries, not qrencode CLI.
If the sandbox doesn't have the library, the test should FAIL
honestly so we know what to fix in the sandbox image.

Also adds CLAUDE.md rule to prevent future attempts to replace
native library QR tests with CLI subprocess calls.
This commit is contained in:
russell@unturf.com 2026-01-29 16:13:47 -05:00
parent ed464a3678
commit 97fbd55e61
13 changed files with 77 additions and 224 deletions

View file

@ -4,18 +4,15 @@
DATA DIVISION.
WORKING-STORAGE SECTION.
01 QR-PTR USAGE POINTER.
01 QR-VERSION PIC S9(9) COMP-5.
01 QR-WIDTH PIC S9(9) COMP-5.
01 QR-DATA-PTR USAGE POINTER.
01 WS-TEXT PIC X(20) VALUE "unsandbox-qr-ok"
& X"00".
01 WS-ZERO PIC S9(9) COMP-5 VALUE 0.
01 WS-ONE PIC S9(9) COMP-5 VALUE 1.
01 WS-TWO PIC S9(9) COMP-5 VALUE 2.
LINKAGE SECTION.
01 QR-STRUCT.
05 QR-VERSION PIC S9(9) COMP-5.
05 QR-WIDTH PIC S9(9) COMP-5.
05 QR-DATA USAGE POINTER.
PROCEDURE DIVISION.
MAIN-LOGIC.
CALL "QRcode_encodeString"
@ -27,7 +24,8 @@
RETURNING QR-PTR.
IF QR-PTR NOT = NULL
SET ADDRESS OF QR-STRUCT TO QR-PTR
SET ADDRESS OF QR-VERSION TO QR-PTR
SET ADDRESS OF QR-WIDTH TO QR-PTR
DISPLAY "QR:unsandbox-qr-ok:ROWS:" QR-WIDTH
CALL "QRcode_free" USING BY VALUE QR-PTR
ELSE

View file

@ -1,19 +1,5 @@
@[Link("qrencode")]
lib LibQRencode
struct QRcode
version : LibC::Int
width : LibC::Int
data : UInt8*
end
require "qr-code"
fun QRcode_encodeString(s : LibC::Char*, version : LibC::Int, level : LibC::Int, hint : LibC::Int, casesensitive : LibC::Int) : QRcode*
fun QRcode_free(qr : QRcode*)
end
qr = LibQRencode.QRcode_encodeString("unsandbox-qr-ok", 0, 1, 2, 1)
if qr.null?
puts "QR encode failed"
exit 1
end
puts "QR:unsandbox-qr-ok:ROWS:#{qr.value.width}"
LibQRencode.QRcode_free(qr)
qr = QRCode.new("unsandbox-qr-ok")
rows = qr.modules.size
puts "QR:unsandbox-qr-ok:ROWS:#{rows}"

View file

@ -1,33 +1,8 @@
import core.sys.posix.dlfcn;
import std.stdio;
import std.string;
struct QRcode {
int _version;
int width;
void* data;
}
import qr;
void main() {
auto lib = dlopen("libqrencode.so", RTLD_LAZY);
if (lib is null) {
stderr.writeln("dlopen: ", dlerror().fromStringz);
return;
}
alias EncFunc = extern(C) QRcode* function(const char*, int, int, int, int);
alias FreeFunc = extern(C) void function(QRcode*);
auto encode = cast(EncFunc) dlsym(lib, "QRcode_encodeString");
auto qfree = cast(FreeFunc) dlsym(lib, "QRcode_free");
auto qr = encode("unsandbox-qr-ok", 0, 1, 2, 1);
if (qr is null) {
stderr.writeln("encode failed");
return;
}
writefln("QR:unsandbox-qr-ok:ROWS:%d", qr.width);
qfree(qr);
dlclose(lib);
auto qrCode = QrCode("unsandbox-qr-ok");
auto size = qrCode.size;
writefln("QR:unsandbox-qr-ok:ROWS:%d", size);
}

View file

@ -1,14 +1,4 @@
# Use Erlang's :qrcode module (pre-cached via rebar3)
# Falls back to shell if module not available
try do
{:ok, qr} = :qrcode.encode("unsandbox-qr-ok")
# qr is a tuple like {:qrcode, version, ecc, dimension, data}
{_tag, _version, _ecc, dimension, _data} = qr
IO.puts("QR:unsandbox-qr-ok:ROWS:#{dimension}")
rescue
_ ->
# Fallback: use qrencode CLI
{output, 0} = System.cmd("sh", ["-c", "qrencode -t ASCII -m 0 'unsandbox-qr-ok' | wc -l"])
rows = String.trim(output)
IO.puts("QR:unsandbox-qr-ok:ROWS:#{rows}")
end
qr = EQRCode.encode("unsandbox-qr-ok")
matrix = qr.matrix
rows = length(matrix)
IO.puts("QR:unsandbox-qr-ok:ROWS:#{rows}")

View file

@ -13,18 +13,16 @@ program qrcode_test
import :: c_ptr, c_char, c_int
character(kind=c_char), dimension(*), intent(in) :: filename
integer(c_int), value :: flag
end function dlopen
end function
type(c_funptr) function dlsym(handle, symbol) bind(C, name='dlsym')
import :: c_ptr, c_funptr, c_char
type(c_ptr), value :: handle
character(kind=c_char), dimension(*), intent(in) :: symbol
end function dlsym
end function
integer(c_int) function dlclose(handle) bind(C, name='dlclose')
import :: c_ptr, c_int
type(c_ptr), value :: handle
end function dlclose
end function
end interface
abstract interface
@ -32,11 +30,11 @@ program qrcode_test
import :: c_ptr, c_char, c_int
character(kind=c_char), dimension(*), intent(in) :: s
integer(c_int), value :: ver, lvl, hint, cs
end function encode_iface
end function
subroutine free_iface(qr) bind(C)
import :: c_ptr
type(c_ptr), value :: qr
end subroutine free_iface
end subroutine
end interface
type(c_ptr) :: lib, qr_ptr
@ -44,34 +42,17 @@ program qrcode_test
procedure(encode_iface), pointer :: qr_encode
procedure(free_iface), pointer :: qr_free
type(QRcode), pointer :: qr
integer(c_int) :: rc
! Try versioned library names
lib = dlopen("libqrencode.so.4" // c_null_char, 2)
if (.not. c_associated(lib)) then
lib = dlopen("libqrencode.so.3" // c_null_char, 2)
end if
if (.not. c_associated(lib)) then
lib = dlopen("libqrencode.so" // c_null_char, 2)
end if
if (.not. c_associated(lib)) then
write(*,'(A)') 'Failed to load libqrencode'
stop 1
end if
lib = dlopen("libqrencode.so" // c_null_char, 2) ! RTLD_NOW=2
enc_fptr = dlsym(lib, "QRcode_encodeString" // c_null_char)
free_fptr = dlsym(lib, "QRcode_free" // c_null_char)
call c_f_procpointer(enc_fptr, qr_encode)
call c_f_procpointer(free_fptr, qr_free)
qr_ptr = qr_encode("unsandbox-qr-ok" // c_null_char, 0, 1, 2, 1)
if (c_associated(qr_ptr)) then
call c_f_pointer(qr_ptr, qr)
write(*, '(A,I0)') 'QR:unsandbox-qr-ok:ROWS:', qr%width
call qr_free(qr_ptr)
else
write(*,'(A)') 'QR encode failed'
end if
call c_f_pointer(qr_ptr, qr)
write(*, '(A,I0)') 'QR:unsandbox-qr-ok:ROWS:', qr%width
rc = dlclose(lib)
call qr_free(qr_ptr)
call dlclose(lib)
end program qrcode_test

View file

@ -1,21 +1,6 @@
open System
open System.Runtime.InteropServices
open QRCoder
[<DllImport("libqrencode.so", CallingConvention = CallingConvention.Cdecl)>]
extern nativeint QRcode_encodeString(string text, int version, int level, int hint, int caseSensitive)
[<DllImport("libqrencode.so", CallingConvention = CallingConvention.Cdecl)>]
extern void QRcode_free(nativeint qr)
[<Struct; StructLayout(LayoutKind.Sequential)>]
type QRcode =
val version: int
val width: int
let qr = QRcode_encodeString("unsandbox-qr-ok", 0, 1, 2, 1)
if qr <> IntPtr.Zero then
let s = Marshal.PtrToStructure<QRcode>(qr)
printfn "QR:unsandbox-qr-ok:ROWS:%d" s.width
QRcode_free(qr)
else
printfn "QR encode failed"
let gen = QRCodeGenerator()
let data = gen.CreateQrCode("unsandbox-qr-ok", QRCodeGenerator.ECCLevel.M)
let rows = data.ModuleMatrix.Count
printfn "QR:unsandbox-qr-ok:ROWS:%d" rows

View file

@ -1,7 +1,7 @@
#!/usr/bin/env groovy
// Use qrencode CLI via ProcessBuilder (libqrencode wrapper)
def proc = ["qrencode", "-t", "UTF8", "-m", "0", "unsandbox-qr-ok"].execute()
def output = proc.text
proc.waitFor()
def rows = output.split('\n').findAll { it.trim() }.size()
println "QR:unsandbox-qr-ok:ROWS:${rows}"
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
def w = new QRCodeWriter()
def m = w.encode("unsandbox-qr-ok", BarcodeFormat.QR_CODE, 0, 0)
println "QR:unsandbox-qr-ok:ROWS:${m.height}"

View file

@ -1,41 +1,11 @@
import System.Posix.DynamicLinker
import Foreign
import Foreign.C.Types
import Foreign.C.String
data QRcode = QRcode CInt CInt (Ptr Word8)
instance Storable QRcode where
sizeOf _ = 16
alignment _ = 8
peek ptr = QRcode
<$> peekByteOff ptr 0
<*> peekByteOff ptr 4
<*> peekByteOff ptr 8
poke _ _ = return ()
type EncodeFunc = CString -> CInt -> CInt -> CInt -> CInt -> IO (Ptr QRcode)
type FreeFunc = Ptr QRcode -> IO ()
foreign import ccall "dynamic"
mkEncode :: FunPtr EncodeFunc -> EncodeFunc
foreign import ccall "dynamic"
mkFree :: FunPtr FreeFunc -> FreeFunc
import Codec.QRCode
import Codec.QRCode.JuicyPixels
main :: IO ()
main = do
dl <- dlopen "libqrencode.so" [RTLD_LAZY]
encFP <- dlsym dl "QRcode_encodeString"
freeFP <- dlsym dl "QRcode_free"
let encode = mkEncode encFP
let qfree = mkFree freeFP
cs <- newCString "unsandbox-qr-ok"
qr <- encode cs 0 1 2 1
if qr == nullPtr
then putStrLn "QR encode failed"
else do
QRcode _ w _ <- peek qr
putStrLn $ "QR:unsandbox-qr-ok:ROWS:" ++ show w
qfree qr
free cs
let mqr = encodeText defaultQRCodeOptions Iso8859_1OrUtf8WithoutECI "unsandbox-qr-ok"
case mqr of
Nothing -> putStrLn "QR encode failed"
Just img -> do
let matrix = toMatrix True False img :: [[Bool]]
putStrLn $ "QR:unsandbox-qr-ok:ROWS:" ++ show (length matrix)

View file

@ -1,7 +1,4 @@
(* OCaml single-file execution can't use ocamlfind packages *)
(* Use Unix.open_process_in with qrencode CLI *)
let () =
let ic = Unix.open_process_in "qrencode -t ASCII -m 0 'unsandbox-qr-ok' | wc -l" in
let rows = String.trim (input_line ic) in
ignore (Unix.close_process_in ic);
Printf.printf "QR:unsandbox-qr-ok:ROWS:%s\n" rows
let qr = Qrc.encode "unsandbox-qr-ok" in
let rows = Array.length (Qrc.to_matrix qr) in
Printf.printf "QR:unsandbox-qr-ok:ROWS:%d\n" rows

View file

@ -1,32 +1,4 @@
import dynlib
import qr
type
QRcode {.pure.} = object
version: cint
width: cint
data: pointer
type
QRencodeString = proc(s: cstring, ver: cint, level: cint, hint: cint, cs: cint): ptr QRcode {.cdecl.}
QRfree = proc(qr: ptr QRcode) {.cdecl.}
# Try versioned library names (Ubuntu 24.04 uses libqrencode.so.4)
var lib = loadLib("libqrencode.so.4")
if lib == nil:
lib = loadLib("libqrencode.so.3")
if lib == nil:
lib = loadLib("libqrencode.so")
if lib == nil:
quit("Failed to load libqrencode", 1)
let encode = cast[QRencodeString](lib.symAddr("QRcode_encodeString"))
let free_qr = cast[QRfree](lib.symAddr("QRcode_free"))
let qr = encode("unsandbox-qr-ok", 0, 1, 2, 1)
if qr != nil:
echo "QR:unsandbox-qr-ok:ROWS:", qr.width
free_qr(qr)
else:
echo "QR encode failed"
unloadLib(lib)
let qrCode = newQR("unsandbox-qr-ok")
echo "QR:unsandbox-qr-ok:ROWS:" & $qrCode.size

View file

@ -1,5 +1,5 @@
use Text::QRCode;
my $qr = Text::QRCode->new();
my $matrix = $qr->plot("unsandbox-qr-ok");
my $rows = scalar(@$matrix);
use Imager::QRCode;
my $qr = Imager::QRCode->new(size => 1, margin => 0);
my $img = $qr->plot("unsandbox-qr-ok");
my $rows = $img->getheight();
print "QR:unsandbox-qr-ok:ROWS:$rows\n";