diff --git a/.github/hurl-examples.yml b/.github/hurl-examples.yml new file mode 100644 index 00000000..54fd0298 --- /dev/null +++ b/.github/hurl-examples.yml @@ -0,0 +1,111 @@ +# Shared changes run every examples/*/test.hurl, including newly added examples. +all_examples: + - 'examples/**' + - 'Dockerfile' + - '.dockerignore' + - 'Cargo.toml' + - 'Cargo.lock' + - 'build.rs' + - '.cargo/**' + - 'scripts/**' + - '.github/workflows/ci.yml' + - '.github/hurl-examples.yml' + +# Keys are example folders; add one glob per line to associate more files. +# Keep suites with distinctive integration coverage; describe it above each entry. + +# Trace propagation through nginx and actual OTLP export into Tempo. +'examples/telemetry': + - 'src/telemetry.rs' + - 'src/app_config.rs' + - 'src/main.rs' + - 'src/webserver/http.rs' + - 'src/webserver/http_client.rs' + - 'src/webserver/database/execute_queries.rs' + +# Mailpit checks delivered MIME parts; the form asserts delayed multipart encoding. +'examples/sending emails': + - 'src/app_config.rs' + - 'src/webserver/http_request_info.rs' + - 'src/webserver/database/sqlpage_functions/functions/send_mail.rs' + - 'src/webserver/database/sqlpage_functions/functions/uploaded_file_name.rs' + - 'src/webserver/database/sqlpage_functions/functions/uploaded_file_path.rs' + - 'src/webserver/database/sqlpage_functions/functions/read_file_as_data_url.rs' + - 'sqlpage/templates/form.handlebars' + +# Real Keycloak discovery, login, callback cookies, and identity claims in SQL. +'examples/single sign on': + - 'src/app_config.rs' + - 'src/lib.rs' + - 'src/webserver/oidc.rs' + - 'src/webserver/http.rs' + - 'src/webserver/routing.rs' + - 'src/webserver/http_client.rs' + - 'src/webserver/http_request_info.rs' + - 'src/webserver/database/sqlpage_functions/functions/user_info*.rs' + - 'src/webserver/database/sqlpage_functions/functions/oidc_logout_url.rs' + +# A real proxy connects to SQLPage's Unix socket and rewrites pretty URLs. +'examples/nginx': + - 'src/app_config.rs' + - 'src/main.rs' + - 'src/webserver/http.rs' + - 'src/webserver/routing.rs' + +# Apache proxies a site prefix, including the missing trailing slash redirect. +'examples/web servers - apache': + - 'src/app_config.rs' + - 'src/webserver/http.rs' + - 'src/webserver/routing.rs' + +# Load SpatiaLite's native library and use its spatial functions on pooled SQLite. +'examples/make a geographic data application using sqlite extensions': + - 'src/app_config.rs' + - 'src/webserver/database/connect.rs' + +# Create a PostgreSQL-backed page, request it, and render dynamic rows in a table. +'examples/SQLPage developer user interface': + - 'src/filesystem.rs' + - 'src/file_cache.rs' + - 'src/webserver/routing.rs' + - 'src/webserver/database/sqlpage_functions/functions/link.rs' + - 'sqlpage/templates/table.handlebars' + +# Posted arrays feed MySQL JSON_TABLE; native JSON builds the select options. +'examples/mysql json handling': + - 'src/webserver/http_request_info.rs' + - 'src/webserver/request_variables.rs' + - 'src/webserver/single_or_vec.rs' + - 'src/webserver/database/sql.rs' + - 'src/webserver/database/sql/**' + - 'src/webserver/database/execute_queries.rs' + - 'src/webserver/database/sql_to_json.rs' + - 'sqlpage/templates/form.handlebars' + +# A real cookie jar round-trips an encoded space and removes it from a subfolder. +'examples/read-and-set-http-cookies': + - 'src/render.rs' + - 'src/webserver/http_request_info.rs' + - 'src/webserver/database/sqlpage_functions/functions/cookie.rs' + +# Hash a new signup's password, authenticate it, and invalidate its login session. +'examples/user-authentication': + - 'src/render.rs' + - 'src/webserver/http_request_info.rs' + - 'src/webserver/database/sqlpage_functions/functions/hash_password.rs' + - 'src/webserver/database/sqlpage_functions/functions/random_string.rs' + - 'src/webserver/database/sqlpage_functions/functions/cookie.rs' + +# Authenticated multipart upload persists an image into a writable container mount. +'examples/image gallery with user uploads': + - 'src/app_config.rs' + - 'src/webserver/http_request_info.rs' + - 'src/webserver/database/sqlpage_functions/functions/persist_uploaded_file.rs' + - 'src/webserver/database/sqlpage_functions/functions/uploaded_file_mime_type.rs' + - 'src/webserver/database/sqlpage_functions/functions/random_string.rs' + +# JSON menus with icon-only submenus and arrays of custom scripts/styles. +'examples/CRUD - Authentication': ['sqlpage/templates/shell.handlebars'] + +# Cards combine Markdown and remote chart/map/table embed markup. +'examples/cards-with-remote-content': ['sqlpage/templates/card.handlebars'] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b72e4e7f..5db2d07f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -333,22 +333,42 @@ jobs: retention-days: 1 hurl_examples: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + env: + DIFF_BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }} outputs: examples: ${{ steps.examples.outputs.examples }} steps: - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - id: paths + name: Match changed paths to Hurl suites + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + if: ${{ github.ref_type != 'tag' && env.DIFF_BASE != '' && !startsWith(env.DIFF_BASE, '0000000') }} + continue-on-error: true + with: + base: ${{ env.DIFF_BASE }} + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} + filters: .github/hurl-examples.yml - id: examples - run: | - examples="$(find examples -mindepth 2 -maxdepth 2 -name test.hurl -print | sed 's#/test.hurl$##' | sort | jq -R -s -c 'split("\n")[:-1]')" - echo "examples=$examples" >> "$GITHUB_OUTPUT" + name: Select relevant Hurl examples + run: scripts/select-hurl-examples.sh + env: + PATHS_OUTCOME: ${{ steps.paths.outcome }} + ALL_EXAMPLES: ${{ steps.paths.outputs.all_examples }} + MATCHED_EXAMPLES: ${{ steps.paths.outputs.changes }} - name: Restore Hurl archive id: hurl-cache + if: steps.examples.outputs.examples != '[]' uses: actions/cache@v6 with: path: .cache/hurl key: hurl-${{ runner.os }}-x86_64-unknown-linux-gnu-${{ env.HURL_VERSION }} - name: Download Hurl archive - if: steps.hurl-cache.outputs.cache-hit != 'true' + if: steps.examples.outputs.examples != '[]' && steps.hurl-cache.outputs.cache-hit != 'true' run: | set -euo pipefail archive="hurl-${HURL_VERSION}-x86_64-unknown-linux-gnu.tar.gz" @@ -363,6 +383,7 @@ jobs: test "$expected_sha" = "$actual_sha" ) - name: Upload Hurl archive + if: steps.examples.outputs.examples != '[]' uses: actions/upload-artifact@v7 with: name: hurl-${{ env.HURL_VERSION }}-x86_64-unknown-linux-gnu @@ -373,6 +394,7 @@ jobs: hurl: name: hurl (${{ matrix.example }}) runs-on: ubuntu-latest + if: ${{ needs.hurl_examples.outputs.examples != '[]' }} timeout-minutes: 15 needs: - docker_build diff --git a/scripts/select-hurl-examples.sh b/scripts/select-hurl-examples.sh new file mode 100755 index 00000000..94b21c5b --- /dev/null +++ b/scripts/select-hurl-examples.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${PATHS_OUTCOME:-}" == success && "${ALL_EXAMPLES:-}" != true ]]; then + examples="$MATCHED_EXAMPLES" +else + # Tags, missing diff bases, and failed path filtering run every example. + examples="$(find examples -mindepth 2 -maxdepth 2 -name test.hurl -print \ + | sed 's#/test.hurl$##' | sort | jq -R -s -c 'split("\n")[:-1]')" +fi + +echo "examples=$examples" >> "$GITHUB_OUTPUT" +echo "Selected Hurl examples: $examples" >> "$GITHUB_STEP_SUMMARY"