From 914f500ed5e60144cedb74cfca0e3c0b4cae4a06 Mon Sep 17 00:00:00 2001 From: "H.E. Pennypacker" <115990865+pennypacker-he@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:49:40 +0000 Subject: [PATCH] Fix OJS beacon rendering and smoke matrix Render OJS_ENABLE_BEACON through an explicit truthy helper so false-like values do not enable the beacon in generated config. Limit buildkit create smoke tests to apps whose branch images were actually built and make fcrepo Syn auth fixtures corrupt signature bytes deterministically. --- .github/workflows/push.yml | 108 ++++++++++-------- .../fcrepo6/tests/SynAuthentication/test.sh | 18 ++- .../fcrepo7/tests/SynAuthentication/test.sh | 18 ++- .../etc/confd/templates/config.inc.tmpl | 3 +- internal/buildkit/metadata_test.go | 16 +++ 5 files changed, 108 insertions(+), 55 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index cab51f91..ac1c93cc 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -62,6 +62,7 @@ jobs: sitectl_drupal_image_tags: ${{ steps.plan.outputs.sitectl_drupal_image_tags }} sitectl_isle_libops_image_tags: ${{ steps.plan.outputs.sitectl_isle_libops_image_tags }} sitectl_isle_images: ${{ steps.plan.outputs.sitectl_isle_images }} + sitectl_create_smoke_matrix: ${{ steps.plan.outputs.sitectl_create_smoke_matrix }} steps: - uses: "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10" # v6 with: @@ -109,14 +110,65 @@ jobs: ./ci/image-metadata.sh first-tag "$1" "$mode" "$FALLBACK_TAG" } + planned_tag_assignment() { + local service="$1" + local image="$2" + if grep -Fq "\"${image}\"" build-plan.out; then + printf '%s=%s' "$service" "$(first_tag "$image")" + fi + } + + add_smoke_row() { + local plugin="$1" + local packages="$2" + local create_args="$3" + local image_tags="$4" + local images="$5" + if [ -z "${image_tags// }" ]; then + return + fi + smoke_matrix="$( + jq -c \ + --arg plugin "$plugin" \ + --arg packages "$packages" \ + --arg create_args "$create_args" \ + --arg image_tags "$image_tags" \ + --arg images "$images" \ + '. + [{ + "plugin": $plugin, + "packages": $packages, + "create-args": $create_args, + "image-tags": $image_tags, + "images": $images + }]' <<<"$smoke_matrix" + )" + } + + sitectl_wp_image_tags="$(planned_tag_assignment wp wp-php84)" + sitectl_omeka_s_image_tags="$(planned_tag_assignment omeka-s omeka-s-php84)" + sitectl_omeka_classic_image_tags="$(planned_tag_assignment omeka-classic omeka-classic-php84)" + sitectl_ojs_image_tags="$(planned_tag_assignment ojs ojs-php84)" + sitectl_drupal_image_tags="$(planned_tag_assignment drupal drupal-php84)" + sitectl_isle_libops_image_tags="$(planned_tag_assignment drupal islandora-php84)" + sitectl_isle_images="" + + smoke_matrix='[]' + add_smoke_row "wp" "sitectl sitectl-wp" "" "$sitectl_wp_image_tags" "" + add_smoke_row "omeka-s" "sitectl sitectl-omeka-s" "" "$sitectl_omeka_s_image_tags" "" + add_smoke_row "omeka-classic" "sitectl sitectl-omeka-classic" "" "$sitectl_omeka_classic_image_tags" "" + add_smoke_row "ojs" "sitectl sitectl-ojs" "" "$sitectl_ojs_image_tags" "" + add_smoke_row "drupal" "sitectl sitectl-drupal" "" "$sitectl_drupal_image_tags" "" + add_smoke_row "isle" "sitectl sitectl-isle sitectl-drupal" "--template-repo https://github.com/libops/isle --fcrepo off --blazegraph off --isle-file-system-uri private --iiif triplet --iiif-topology disabled --bot-mitigation off" "$sitectl_isle_libops_image_tags" "$sitectl_isle_images" + { - echo "sitectl_wp_image_tags=wp=$(first_tag wp-php84)" - echo "sitectl_omeka_s_image_tags=omeka-s=$(first_tag omeka-s-php84)" - echo "sitectl_omeka_classic_image_tags=omeka-classic=$(first_tag omeka-classic-php84)" - echo "sitectl_ojs_image_tags=ojs=$(first_tag ojs-php84)" - echo "sitectl_drupal_image_tags=drupal=$(first_tag drupal-php84)" - echo "sitectl_isle_libops_image_tags=drupal=$(first_tag islandora-php84) activemq=$(first_tag activemq6) alpaca=$(first_tag alpaca) crayfits=$(first_tag crayfits) fits=$(first_tag fits) homarus=$(first_tag homarus) houdini=$(first_tag houdini) hypercube=$(first_tag hypercube) init=$(first_tag base) mariadb=$(first_tag mariadb11) mergepdf=$(first_tag mergepdf) solr=$(first_tag solr9)" - echo "sitectl_isle_images=" + echo "sitectl_wp_image_tags=${sitectl_wp_image_tags}" + echo "sitectl_omeka_s_image_tags=${sitectl_omeka_s_image_tags}" + echo "sitectl_omeka_classic_image_tags=${sitectl_omeka_classic_image_tags}" + echo "sitectl_ojs_image_tags=${sitectl_ojs_image_tags}" + echo "sitectl_drupal_image_tags=${sitectl_drupal_image_tags}" + echo "sitectl_isle_libops_image_tags=${sitectl_isle_libops_image_tags}" + echo "sitectl_isle_images=${sitectl_isle_images}" + echo "sitectl_create_smoke_matrix=${smoke_matrix}" } >> "$GITHUB_OUTPUT" - name: Summary @@ -134,6 +186,7 @@ jobs: echo "sitectl ojs image tags: ${{ steps.plan.outputs.sitectl_ojs_image_tags }}" echo "sitectl drupal image tags: ${{ steps.plan.outputs.sitectl_drupal_image_tags }}" echo "sitectl isle libops image tags: ${{ steps.plan.outputs.sitectl_isle_libops_image_tags }}" + echo "sitectl create smoke matrix: ${{ steps.plan.outputs.sitectl_create_smoke_matrix }}" build-level-0: if: ${{ needs.plan.outputs.level0 != '[]' }} @@ -321,49 +374,12 @@ jobs: packages: read sitectl-create-smoke-test: - if: ${{ always() && !cancelled() && !failure() }} + if: ${{ always() && needs.plan.outputs.sitectl_create_smoke_matrix != '[]' && !cancelled() && !failure() }} needs: [plan, test-level-4] strategy: fail-fast: false matrix: - include: - - plugin: wp - packages: sitectl sitectl-wp - create-args: "" - image-tags: ${{ needs.plan.outputs.sitectl_wp_image_tags }} - images: "" - - plugin: omeka-s - packages: sitectl sitectl-omeka-s - create-args: "" - image-tags: ${{ needs.plan.outputs.sitectl_omeka_s_image_tags }} - images: "" - - plugin: omeka-classic - packages: sitectl sitectl-omeka-classic - create-args: "" - image-tags: ${{ needs.plan.outputs.sitectl_omeka_classic_image_tags }} - images: "" - - plugin: ojs - packages: sitectl sitectl-ojs - create-args: "" - image-tags: ${{ needs.plan.outputs.sitectl_ojs_image_tags }} - images: "" - - plugin: drupal - packages: sitectl sitectl-drupal - create-args: "" - image-tags: ${{ needs.plan.outputs.sitectl_drupal_image_tags }} - images: "" - - plugin: isle - packages: sitectl sitectl-isle sitectl-drupal - create-args: >- - --template-repo https://github.com/libops/isle - --fcrepo off - --blazegraph off - --isle-file-system-uri private - --iiif triplet - --iiif-topology disabled - --bot-mitigation off - image-tags: ${{ needs.plan.outputs.sitectl_isle_libops_image_tags }} - images: ${{ needs.plan.outputs.sitectl_isle_images }} + include: ${{ fromJson(needs.plan.outputs.sitectl_create_smoke_matrix) }} uses: libops/.github/.github/workflows/sitectl-create-smoke-test.yaml@main with: diff --git a/images/fcrepo6/tests/SynAuthentication/test.sh b/images/fcrepo6/tests/SynAuthentication/test.sh index 2b8cce4d..191733c1 100755 --- a/images/fcrepo6/tests/SynAuthentication/test.sh +++ b/images/fcrepo6/tests/SynAuthentication/test.sh @@ -19,6 +19,19 @@ create_jwt() { printf '%s.%s' "${signing_input}" "${signature}" } +corrupt_jwt_signature() { + local header payload replacement signature token + token=${1} + IFS=. read -r header payload signature <<<"${token}" + + case "${signature:0:1}" in + A) replacement=B ;; + *) replacement=A ;; + esac + + printf '%s.%s.%s%s' "${header}" "${payload}" "${replacement}" "${signature:1}" +} + post_status() { local body_file token token=${1} @@ -42,10 +55,7 @@ if [[ "${valid_status}" != 2* ]]; then fi echo "Valid JWT accepted with HTTP ${valid_status}." -case "${valid_jwt: -1}" in -x) bad_jwt="${valid_jwt%?}y" ;; -*) bad_jwt="${valid_jwt%?}x" ;; -esac +bad_jwt=$(corrupt_jwt_signature "${valid_jwt}") bad_status=$(post_status "${bad_jwt}" /tmp/syn-bad-response.txt) if [[ "${bad_status}" != "401" ]]; then diff --git a/images/fcrepo7/tests/SynAuthentication/test.sh b/images/fcrepo7/tests/SynAuthentication/test.sh index 2b8cce4d..191733c1 100755 --- a/images/fcrepo7/tests/SynAuthentication/test.sh +++ b/images/fcrepo7/tests/SynAuthentication/test.sh @@ -19,6 +19,19 @@ create_jwt() { printf '%s.%s' "${signing_input}" "${signature}" } +corrupt_jwt_signature() { + local header payload replacement signature token + token=${1} + IFS=. read -r header payload signature <<<"${token}" + + case "${signature:0:1}" in + A) replacement=B ;; + *) replacement=A ;; + esac + + printf '%s.%s.%s%s' "${header}" "${payload}" "${replacement}" "${signature:1}" +} + post_status() { local body_file token token=${1} @@ -42,10 +55,7 @@ if [[ "${valid_status}" != 2* ]]; then fi echo "Valid JWT accepted with HTTP ${valid_status}." -case "${valid_jwt: -1}" in -x) bad_jwt="${valid_jwt%?}y" ;; -*) bad_jwt="${valid_jwt%?}x" ;; -esac +bad_jwt=$(corrupt_jwt_signature "${valid_jwt}") bad_status=$(post_status "${bad_jwt}" /tmp/syn-bad-response.txt) if [[ "${bad_status}" != "401" ]]; then diff --git a/images/ojs/rootfs/etc/confd/templates/config.inc.tmpl b/images/ojs/rootfs/etc/confd/templates/config.inc.tmpl index 3e2c62f2..04a0f01f 100644 --- a/images/ojs/rootfs/etc/confd/templates/config.inc.tmpl +++ b/images/ojs/rootfs/etc/confd/templates/config.inc.tmpl @@ -1,4 +1,5 @@ {{- define "doubleQuoteEscape" -}}{{- $escaped := replace . "\\" "\\\\" -1 -}}{{- replace $escaped "\"" "\\\"" -1 -}}{{- end -}} +{{- define "booleanOnOff" -}}{{- if or (eq . "1") (eq . "on") (eq . "On") (eq . "ON") (eq . "true") (eq . "True") (eq . "TRUE") (eq . "y") (eq . "Y") (eq . "yes") (eq . "Yes") (eq . "YES") -}}On{{- else -}}Off{{- end -}}{{- end -}} ; ; DO NOT DELETE THE ABOVE LINE!!! ; Doing so will expose this configuration file through your web site! @@ -118,7 +119,7 @@ enable_minified = On ; Provide a unique site ID and OAI base URL to PKP for statistics and security ; alert purposes only. -enable_beacon = {{ if getenv "OJS_ENABLE_BEACON" }}On{{ else }}Off{{ end }} +enable_beacon = {{ template "booleanOnOff" (getenv "OJS_ENABLE_BEACON") }} ; Set this to "On" if you would like to only have a single, site-wide Privacy ; Statement, rather than a separate Privacy Statement for each journal. Setting diff --git a/internal/buildkit/metadata_test.go b/internal/buildkit/metadata_test.go index e6a22cc3..caada06a 100644 --- a/internal/buildkit/metadata_test.go +++ b/internal/buildkit/metadata_test.go @@ -151,6 +151,22 @@ func TestMariaDBLongrunStartsServer(t *testing.T) { } } +func TestOJSEnableBeaconTemplateUsesTruthyHelper(t *testing.T) { + root := repoRoot(t) + templateFile := filepath.Join(root, "images", "ojs", "rootfs", "etc", "confd", "templates", "config.inc.tmpl") + content, err := os.ReadFile(templateFile) + if err != nil { + t.Fatal(err) + } + got := string(content) + if strings.Contains(got, `if getenv "OJS_ENABLE_BEACON"`) { + t.Fatalf("%s treats any non-empty OJS_ENABLE_BEACON value as On", templateFile) + } + if !strings.Contains(got, `define "booleanOnOff"`) || !strings.Contains(got, `enable_beacon = {{ template "booleanOnOff" (getenv "OJS_ENABLE_BEACON") }}`) { + t.Fatalf("%s must render OJS_ENABLE_BEACON through the booleanOnOff helper", templateFile) + } +} + func TestBaseVaultSecretsBootstrap(t *testing.T) { root := repoRoot(t)