diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 94c14033..636b46bd 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.4@sha256:9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc # Main Base Image (Ubuntu 24.04 GLIBC compatible) FROM mcr.microsoft.com/devcontainers/base:2.1.7-ubuntu24.04@sha256:4bcb1b466771b1ba1ea110e2a27daea2f6093f9527fb75ee59703ec89b5561cb @@ -48,84 +48,105 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && apt-get clean && rm -rf /var/lib/apt/lists/* COPY .devcontainer/pinned-artifacts.json* pinned-artifacts.json* /tmp/ +# Pre-downloaded tarballs (committed to repo) — avoids CDN non-deterministic gzip re-encoding +COPY .devcontainer/tarballs/npm.tgz* /tmp/tarballs/ +COPY .devcontainer/tarballs/playwright.tgz* /tmp/tarballs/ +COPY .devcontainer/tarballs/playwright-core.tgz* /tmp/tarballs/ +COPY .devcontainer/tarballs/firebase-tools.tgz* /tmp/tarballs/ +COPY .devcontainer/tarballs/infisical.tgz* /tmp/tarballs/ -# 2. CLI Tools (Npm, Supabase, Firebase, Infisical, Playwright) -ARG NPM_TARBALL_URL -ARG NPM_SHASUM -ARG PLAYWRIGHT_TARBALL_URL -ARG PLAYWRIGHT_SHASUM -ARG FIREBASE_TARBALL_URL -ARG FIREBASE_SHASUM -ARG INFISICAL_TARBALL_URL -ARG INFISICAL_SHASUM +# 2. CLI Tools (Npm, Supabase, gh CLI, Firebase, Infisical, Playwright) ARG SUPABASE_TARBALL_URL ARG SUPABASE_SHA256 +ARG GH_TARBALL_URL +ARG GH_SHA256 RUN set -eu; \ + # Read pinned-artifact hashes for verification + NPM_SHA256=""; PLAYWRIGHT_SHA256=""; PLAYWRIGHT_CORE_SHA256=""; \ + FIREBASE_SHA256=""; INFISICAL_SHA256=""; \ + SUPABASE_TARBALL_URL=""; SUPABASE_SHA256=""; \ + GH_TARBALL_URL=""; GH_SHA256=""; \ if [ -f /tmp/pinned-artifacts.json ]; then \ - NPM_TARBALL_URL=${NPM_TARBALL_URL:-$(jq -r '.npm.url // empty' /tmp/pinned-artifacts.json)}; \ - NPM_SHASUM=${NPM_SHASUM:-$(jq -r '.npm.shasum // empty' /tmp/pinned-artifacts.json)}; \ - PLAYWRIGHT_TARBALL_URL=${PLAYWRIGHT_TARBALL_URL:-$(jq -r '.playwright.url // empty' /tmp/pinned-artifacts.json)}; \ - PLAYWRIGHT_SHASUM=${PLAYWRIGHT_SHASUM:-$(jq -r '.playwright.shasum // empty' /tmp/pinned-artifacts.json)}; \ - FIREBASE_TARBALL_URL=${FIREBASE_TARBALL_URL:-$(jq -r '.firebase.url // empty' /tmp/pinned-artifacts.json)}; \ - FIREBASE_SHASUM=${FIREBASE_SHASUM:-$(jq -r '.firebase.shasum // empty' /tmp/pinned-artifacts.json)}; \ - INFISICAL_TARBALL_URL=${INFISICAL_TARBALL_URL:-$(jq -r '.infisical.url // empty' /tmp/pinned-artifacts.json)}; \ - INFISICAL_SHASUM=${INFISICAL_SHASUM:-$(jq -r '.infisical.shasum // empty' /tmp/pinned-artifacts.json)}; \ - SUPABASE_TARBALL_URL=${SUPABASE_TARBALL_URL:-$(jq -r '.supabase.url // empty' /tmp/pinned-artifacts.json)}; \ - SUPABASE_SHA256=${SUPABASE_SHA256:-$(jq -r '.supabase.sha256 // empty' /tmp/pinned-artifacts.json)}; \ + NPM_SHA256=$(jq -r '.npm.sha256 // empty' /tmp/pinned-artifacts.json); \ + PLAYWRIGHT_SHA256=$(jq -r '.playwright.sha256 // empty' /tmp/pinned-artifacts.json); \ + PLAYWRIGHT_CORE_SHA256=$(jq -r '.playwright_core.sha256 // empty' /tmp/pinned-artifacts.json); \ + FIREBASE_SHA256=$(jq -r '.firebase.sha256 // empty' /tmp/pinned-artifacts.json); \ + INFISICAL_SHA256=$(jq -r '.infisical.sha256 // empty' /tmp/pinned-artifacts.json); \ + SUPABASE_TARBALL_URL=$(jq -r '.supabase.url // empty' /tmp/pinned-artifacts.json); \ + SUPABASE_SHA256=$(jq -r '.supabase.sha256 // empty' /tmp/pinned-artifacts.json); \ + GH_TARBALL_URL=${GH_TARBALL_URL:-$(jq -r '.gh.url // empty' /tmp/pinned-artifacts.json)}; \ + GH_SHA256=${GH_SHA256:-$(jq -r '.gh.sha256 // empty' /tmp/pinned-artifacts.json)}; \ fi; \ NODE_LIB_DIR="/usr/lib/node_modules"; \ - # 1. Install NPM tarball (pinned by SHASUM, direct tar extraction avoids Scorecard unpinned npmCommand) - if [ -n "${NPM_TARBALL_URL:-}" ]; then \ - if [ -z "${NPM_SHASUM:-}" ]; then echo "ERROR: NPM_SHASUM missing for $NPM_TARBALL_URL" >&2; exit 1; fi; \ - curl -fSL "$NPM_TARBALL_URL" -o /tmp/npm.tgz; \ - echo "$NPM_SHASUM /tmp/npm.tgz" | sha1sum -c - || { echo "ERROR: Checksum verification failed for npm" >&2; exit 1; }; \ + # 1. Install NPM tarball from pre-downloaded copy (sha256-verified, no CDN download) + if [ -f /tmp/tarballs/npm.tgz ]; then \ + if [ -n "${NPM_SHA256:-}" ]; then \ + echo "$NPM_SHA256 /tmp/tarballs/npm.tgz" | sha256sum -c - || { echo "ERROR: Checksum verification failed for npm" >&2; exit 1; }; \ + fi; \ rm -rf "$NODE_LIB_DIR/npm" && mkdir -p "$NODE_LIB_DIR/npm"; \ - tar -xzf /tmp/npm.tgz --strip-components=1 -C "$NODE_LIB_DIR/npm"; \ + tar -xzf /tmp/tarballs/npm.tgz --strip-components=1 -C "$NODE_LIB_DIR/npm"; \ chmod +x "$NODE_LIB_DIR/npm/bin/npm-cli.js" "$NODE_LIB_DIR/npm/bin/npx-cli.js"; \ ln -sf "$NODE_LIB_DIR/npm/bin/npm-cli.js" /usr/local/bin/npm; \ ln -sf "$NODE_LIB_DIR/npm/bin/npx-cli.js" /usr/local/bin/npx; \ - rm -f /tmp/npm.tgz; \ fi; \ - # 2. Install Supabase CLI (requires SHA256 binary) + # 2. Install Supabase CLI (GitHub release binary, SHA256-verified) if [ -n "${SUPABASE_TARBALL_URL:-}" ]; then \ if [ -z "${SUPABASE_SHA256:-}" ]; then echo "ERROR: SUPABASE_SHA256 missing for $SUPABASE_TARBALL_URL" >&2; exit 1; fi; \ curl -fSL "$SUPABASE_TARBALL_URL" -o /tmp/supabase.tar.gz; \ echo "$SUPABASE_SHA256 /tmp/supabase.tar.gz" | sha256sum -c - || { echo "ERROR: Checksum verification failed for supabase" >&2; exit 1; }; \ tar -xzf /tmp/supabase.tar.gz -C /tmp; \ - find /tmp -type f -name "supabase" -exec mv {} /usr/local/bin/ \; ; \ + find /tmp -maxdepth 2 -type f -name "supabase" -exec mv {} /usr/local/bin/ \; ; \ chmod +x /usr/local/bin/supabase; \ rm -f /tmp/supabase.tar.gz; \ fi; \ - # 3. Install NPM CLI tool tarballs via direct tar extraction & symlinking (avoids Scorecard unpinned npmCommand) + # 2b. Install gh CLI (GitHub release binary, SHA256-verified) + if [ -n "${GH_TARBALL_URL:-}" ]; then \ + if [ -z "${GH_SHA256:-}" ]; then echo "ERROR: GH_SHA256 missing for $GH_TARBALL_URL" >&2; exit 1; fi; \ + curl -fSL "$GH_TARBALL_URL" -o /tmp/gh.tar.gz; \ + echo "$GH_SHA256 /tmp/gh.tar.gz" | sha256sum -c - || { echo "ERROR: Checksum verification failed for gh" >&2; exit 1; }; \ + tar -xzf /tmp/gh.tar.gz -C /tmp; \ + find /tmp -maxdepth 3 -type f -name "gh" -exec mv {} /usr/local/bin/ \; ; \ + chmod +x /usr/local/bin/gh; \ + rm -f /tmp/gh.tar.gz; \ + rm -rf /tmp/gh_*; \ + fi; \ + # 3. Install NPM CLI tool tarballs from pre-downloaded copies (sha256-verified, no CDN download) for NAME in "playwright" "firebase-tools" "infisical"; do \ case "$NAME" in \ - playwright) URL="${PLAYWRIGHT_TARBALL_URL:-}"; SHASUM="${PLAYWRIGHT_SHASUM:-}" ;; \ - firebase-tools) URL="${FIREBASE_TARBALL_URL:-}"; SHASUM="${FIREBASE_SHASUM:-}" ;; \ - infisical) URL="${INFISICAL_TARBALL_URL:-}"; SHASUM="${INFISICAL_SHASUM:-}" ;; \ + playwright) TARBALL="/tmp/tarballs/playwright.tgz"; SHA256="${PLAYWRIGHT_SHA256:-}" ;; \ + firebase-tools) TARBALL="/tmp/tarballs/firebase-tools.tgz"; SHA256="${FIREBASE_SHA256:-}" ;; \ + infisical) TARBALL="/tmp/tarballs/infisical.tgz"; SHA256="${INFISICAL_SHA256:-}" ;; \ esac; \ - if [ -n "$URL" ]; then \ - if [ -z "$SHASUM" ]; then echo "ERROR: SHASUM missing for $NAME ($URL)" >&2; exit 1; fi; \ - curl -fSL "$URL" -o "/tmp/$NAME.tgz"; \ - echo "$SHASUM /tmp/$NAME.tgz" | sha1sum -c - || { echo "ERROR: Checksum verification failed for $NAME" >&2; exit 1; }; \ + if [ -f "$TARBALL" ]; then \ + if [ -n "$SHA256" ]; then \ + echo "$SHA256 $TARBALL" | sha256sum -c - || { echo "ERROR: Checksum verification failed for $NAME" >&2; exit 1; }; \ + fi; \ case "$NAME" in \ playwright) \ + if [ -f /tmp/tarballs/playwright-core.tgz ]; then \ + if [ -n "${PLAYWRIGHT_CORE_SHA256:-}" ]; then \ + echo "$PLAYWRIGHT_CORE_SHA256 /tmp/tarballs/playwright-core.tgz" | sha256sum -c - || { echo "ERROR: Checksum verification failed for playwright-core" >&2; exit 1; }; \ + fi; \ + rm -rf "$NODE_LIB_DIR/playwright-core" && mkdir -p "$NODE_LIB_DIR/playwright-core"; \ + tar -xzf /tmp/tarballs/playwright-core.tgz --strip-components=1 -C "$NODE_LIB_DIR/playwright-core"; \ + fi; \ rm -rf "$NODE_LIB_DIR/playwright" && mkdir -p "$NODE_LIB_DIR/playwright"; \ - tar -xzf "/tmp/$NAME.tgz" --strip-components=1 -C "$NODE_LIB_DIR/playwright"; \ + tar -xzf "$TARBALL" --strip-components=1 -C "$NODE_LIB_DIR/playwright"; \ chmod +x "$NODE_LIB_DIR/playwright/cli.js"; \ ln -sf "$NODE_LIB_DIR/playwright/cli.js" /usr/local/bin/playwright; \ ln -sf "$NODE_LIB_DIR/playwright/cli.js" /usr/bin/playwright; \ ;; \ firebase-tools) \ rm -rf "$NODE_LIB_DIR/firebase-tools" && mkdir -p "$NODE_LIB_DIR/firebase-tools"; \ - tar -xzf "/tmp/$NAME.tgz" --strip-components=1 -C "$NODE_LIB_DIR/firebase-tools"; \ + tar -xzf "$TARBALL" --strip-components=1 -C "$NODE_LIB_DIR/firebase-tools"; \ chmod +x "$NODE_LIB_DIR/firebase-tools/lib/bin/firebase.js"; \ ln -sf "$NODE_LIB_DIR/firebase-tools/lib/bin/firebase.js" /usr/local/bin/firebase; \ ln -sf "$NODE_LIB_DIR/firebase-tools/lib/bin/firebase.js" /usr/bin/firebase; \ ;; \ infisical) \ rm -rf "$NODE_LIB_DIR/@infisical/cli" && mkdir -p "$NODE_LIB_DIR/@infisical/cli/bin"; \ - tar -xzf "/tmp/$NAME.tgz" --strip-components=1 -C "$NODE_LIB_DIR/@infisical/cli"; \ + tar -xzf "$TARBALL" --strip-components=1 -C "$NODE_LIB_DIR/@infisical/cli"; \ for TGZ in "$NODE_LIB_DIR/@infisical/cli/"*.tgz; do \ if [ -f "$TGZ" ]; then \ tar -xzf "$TGZ" --strip-components=1 -C "$NODE_LIB_DIR/@infisical/cli/bin"; \ @@ -139,7 +160,6 @@ RUN set -eu; \ fi; \ ;; \ esac; \ - rm -f "/tmp/$NAME.tgz"; \ fi; \ done; \ # 4. Playwright dependencies & cleanup diff --git a/.devcontainer/pinned-artifacts.json b/.devcontainer/pinned-artifacts.json index 0ec3d323..b46fa747 100644 --- a/.devcontainer/pinned-artifacts.json +++ b/.devcontainer/pinned-artifacts.json @@ -1,22 +1,30 @@ { "npm": { "url": "https://registry.npmjs.org/npm/-/npm-12.0.2.tgz", - "shasum": "788d93dc8869000b1078e0395c60748a0aadc4f1" + "sha256": "5dbb86c71d07a1957f2e90734092dd6a58bdcd9ebc2d8d41ca1c6e6a21d364e1" }, "playwright": { "url": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", - "shasum": "8447b6755e8aec85a3cb7207c823e3ed2fc66700" + "sha256": "1982556a882b246ccb7c16337fab5e4e790292b69f835a2db1011dddc440ed98" + }, + "playwright_core": { + "url": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "sha256": "954be1e183d0ddb9748fe0d2d08b0b66a9210c74dd75c397aeb70303b9f08a00" }, "firebase": { "url": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-15.25.1.tgz", - "shasum": "56f46b47cafdf89dbb3f9c14e039a3095da9c50c" + "sha256": "3517effab3f6c869934da9836a43db6683213e7431c9ef6f3f4b7b349f058bd4" }, "supabase": { "url": "https://github.com/supabase/cli/releases/download/v2.111.0/supabase_2.111.0_linux_amd64.tar.gz", "sha256": "31ee8a152e9c8c8eddae072c6bc7c9119748a96c8cdaf21a6d31c9ce7e62cc18" }, + "gh": { + "url": "https://github.com/cli/cli/releases/download/v2.97.0/gh_2.97.0_linux_amd64.tar.gz", + "sha256": "a2c9b8497e1f85b1ad0dfcb78b5a622e098801b8e461e459e88e1ee12f018112" + }, "infisical": { "url": "https://registry.npmjs.org/@infisical/cli/-/cli-0.43.114.tgz", - "shasum": "f6c8ec4b7c0efcb568694653455f96fb947e4993" + "sha256": "7450bc5a7e5bc56436bf5705292234f28b5dbb185b04cb6606ff7b002c5d84b1" } } diff --git a/.devcontainer/tarballs/firebase-tools.tgz b/.devcontainer/tarballs/firebase-tools.tgz new file mode 100644 index 00000000..bbe3f90d Binary files /dev/null and b/.devcontainer/tarballs/firebase-tools.tgz differ diff --git a/.devcontainer/tarballs/infisical.tgz b/.devcontainer/tarballs/infisical.tgz new file mode 100644 index 00000000..1d39888c Binary files /dev/null and b/.devcontainer/tarballs/infisical.tgz differ diff --git a/.devcontainer/tarballs/npm.tgz b/.devcontainer/tarballs/npm.tgz new file mode 100644 index 00000000..611ead86 Binary files /dev/null and b/.devcontainer/tarballs/npm.tgz differ diff --git a/.devcontainer/tarballs/playwright-core.tgz b/.devcontainer/tarballs/playwright-core.tgz new file mode 100644 index 00000000..8d43ec4d Binary files /dev/null and b/.devcontainer/tarballs/playwright-core.tgz differ diff --git a/.devcontainer/tarballs/playwright.tgz b/.devcontainer/tarballs/playwright.tgz new file mode 100644 index 00000000..278eba18 Binary files /dev/null and b/.devcontainer/tarballs/playwright.tgz differ diff --git a/.example.env b/.example.env index b7a0eaa0..df852193 100644 --- a/.example.env +++ b/.example.env @@ -44,7 +44,7 @@ NEXT_PUBLIC_APP_NAME=GhostClass # âš ī¸ App version displayed in footer and health checks # 🔨 Build-time (Infisical `/build-time` folder) -NEXT_PUBLIC_APP_VERSION=4.5.4 +NEXT_PUBLIC_APP_VERSION=4.5.5 # âš ī¸ Your production domain WITHOUT https:// # All URL-based variables are derived from this. @@ -262,6 +262,14 @@ FIREBASE_PROJECT_ID= FIREBASE_STORAGE_BUCKET= FIREBASE_IOS_BUNDLE_ID= +# â„šī¸ Android package name +# 🔨 Build-time (Infisical `/build-time` folder) +NEXT_PUBLIC_ANDROID_PACKAGE_NAME=com.devakesu.apps.ghostclass + +# â„šī¸ Comma-separated list of SHA-256 cert fingerprints (keystore SHA + Google Play Signing SHA) +# Format: XX:XX:XX... (uppercase hex with colons, comma-separated for multiple SHAs) +# 🔨 Build-time (Infisical `/build-time` folder) +NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS= # ---------------------------------------------------------------------------- # Development / Local Sync Utilities @@ -371,7 +379,7 @@ REQUEST_SIGNATURE_MAX_AGE=600 # âš ī¸ Minimum supported app version required to bypass forced update # 🚀 Runtime (Infisical `/runtime` folder → Server Env Var) -MIN_APP_VERSION=4.5.4 +MIN_APP_VERSION=4.5.5 # â„šī¸ Enforce Firebase App Check for all mobile clients in production # Valid: "true", "false" (default: false in dev, true recommended in prod) diff --git a/.github/requirements.txt b/.github/requirements.txt new file mode 100644 index 00000000..a13e1e3f --- /dev/null +++ b/.github/requirements.txt @@ -0,0 +1,416 @@ +check-jsonschema==0.31.2 \ + --hash=sha256:1804111ce0ab47cd741581190ec6b86e104a82ddd37ffd4e2432f425f765b867 \ + --hash=sha256:a99203b8f82cadbfc3bac787174a15e8336820c24ac58c5b65c627075563401f + +attrs==25.1.0 \ + --hash=sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a \ + --hash=sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e + +certifi==2025.1.31 \ + --hash=sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe \ + --hash=sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651 + +charset-normalizer==3.4.1 \ + --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ + --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ + --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ + --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ + --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ + --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ + --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ + --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ + --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ + --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ + --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ + --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ + --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ + --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ + --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ + --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ + --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ + --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ + --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ + --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ + --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ + --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ + --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ + --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ + --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ + --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ + --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ + --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ + --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ + --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ + --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ + --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 \ + --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ + --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ + --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ + --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ + --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ + --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ + --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ + --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ + --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ + --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ + --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ + --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ + --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ + --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ + --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ + --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ + --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ + --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ + --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ + --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ + --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ + --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ + --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ + --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ + --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ + --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ + --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ + --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ + --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ + --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ + --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ + --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ + --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ + --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ + --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ + --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ + --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ + --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ + --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ + --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ + --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ + --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ + --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ + --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ + --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ + --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ + --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ + --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ + --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ + --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ + --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ + --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ + --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ + --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ + --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ + --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ + --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ + --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ + --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ + --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 + +click==8.1.8 \ + --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ + --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a + +idna==3.10 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 + +jsonschema==4.23.0 \ + --hash=sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566 \ + --hash=sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4 + +jsonschema-specifications==2024.10.1 \ + --hash=sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf \ + --hash=sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272 + +referencing==0.36.2 \ + --hash=sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0 \ + --hash=sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa + +regress==2024.11.1 \ + --hash=sha256:8ff83d641b1a92cc303a4abb61c0c65c9bab31992cb62ea08852820debb2d413 \ + --hash=sha256:adb9e875fc3c3f49e99573c32efd8a20ff353b9228e5f5304dc2b6e021d3a7f2 \ + --hash=sha256:0557c042ce1de9c272558b31eb3e1aedf2581b1d2eb8b9e275f994e7ba50315e \ + --hash=sha256:57bac0cce326bd75dcb54fad9bff82077f92b3345c19908350d5b1d25ff4fde7 \ + --hash=sha256:b7a0f36e1c07e0cb7187f1b6c75ffdabfd95d7f5b8d6c3d717fb0ac73af3992f \ + --hash=sha256:96254b08a1a720913474be1304d97f27e928228286835d8c26f5cdff30a7da36 \ + --hash=sha256:767a6444fa93b2d0c19ea8f7165c63c2fda230720fd1df744cfea9705f131a14 \ + --hash=sha256:81ccaf54691627025fa5fb23629dadcd25ffd40eb091c0133694127894155ee4 \ + --hash=sha256:b2519605b7859ba7641ab736dc8b74c9b95aa61fca697cb9a22be6bef01d9484 \ + --hash=sha256:fae1deff1a124d95025cb516a6a7bf09e632aa743ec2646cda795f7dd105fecf \ + --hash=sha256:5ff11a01c6a843cf02a0b82199376eede756782cb2afea7efe700fb5e41d67b6 \ + --hash=sha256:4fcab0f0db9a305d3ceb260477a52236a3f5ac29a38b4f85d9280dd3f56c0987 \ + --hash=sha256:93ea451e4526d77fbe614d6cf41c439f623c0148d86d78493628a49cce3fb5ab \ + --hash=sha256:89dfdddf69de6c39f535cc1ada6d02256bfcde362e099722a2e927c1f8448fb8 \ + --hash=sha256:0b04d4bb4c1a2232731e57f6c8a9fe035785d4a046dc300df30b4c8dee372048 \ + --hash=sha256:011e39fcf0e23f96d39f122764ee6e74b6fb071d8dd0589a45debdee3ac02867 \ + --hash=sha256:3c81b0e90d9bccddc2242032597ff1222305d8c5c27e6440d792a5247f38fd0a \ + --hash=sha256:87105791bdf1bbe0b751a826599540145890689b6cffe6056c99972cd1ba3e7f \ + --hash=sha256:9254531e7488bb4ba4260bcbcad705d1b075f5b076fd1919100adde0e1f1c0bf \ + --hash=sha256:8e2ba73c37bde3fff627d099609f02444543e0f4b05f8134d6b734da5a7c2968 \ + --hash=sha256:f7c27ce06c2cdd4949532ece6a75e9b880ded3fd07e2e6960a6d0195badff76c \ + --hash=sha256:fa3811a8a2c3272bfcb44c2b27ffb99c1d245376fe40850075307fd4f0ffa8dc \ + --hash=sha256:a88465fa256c44d2e85732ac294fd9e031ad6d5770c88f6a9b72cfe0f3a28a1f \ + --hash=sha256:9ea0746b052529841c6ed45d1a197c68fb589571288fb717469e587b5924501e \ + --hash=sha256:d7aa2c15fc28c5a357cd7385835edc948e21c4f83bd3180b6c63c2a6328bd599 \ + --hash=sha256:35f23fa23d2720a2ed37e25dd3b431b3a79ec058893b471f034ddcb478240a4a \ + --hash=sha256:45e5e0b5ab144de4e333677ca369fe944db7d899327288303a817caae9048b98 \ + --hash=sha256:7d4ad22ae695d91223163a090edddfabf42bbf97bed0c6934789a01f74dc9526 \ + --hash=sha256:e18a4e5b6302b36be92eff7b8aa9715a82b5a752c5e9774f74615c3d583a9b60 \ + --hash=sha256:c5f51d702572a684490760361d9bafa1759719cfd42bec82fa8aab3ae63ed4d3 \ + --hash=sha256:6cdaefd0850409b2b86b984060e040a05b4921f4e61fbeac033dd329bea82e1c \ + --hash=sha256:de4d4c96d4d479a7cff18e405a84d52be96ecbfda0e8ba52f219d164e9baf920 \ + --hash=sha256:28f5aba12761fdf71b9a97ae3f09354981ae91088ac6a61c62198d9350c4c899 \ + --hash=sha256:943c37b96c577f22a61f10c22952e74957897316fd22182a1fcb3f9262cbe513 \ + --hash=sha256:91b5548416b51faa67ff35ae15a12f5c3849a19dbc542a874089beac646d2d73 \ + --hash=sha256:0d493f7e3a3737df8177f5f6157ce5eb9c78ead96f6e97d5dab2d5dde1b3925d \ + --hash=sha256:e920ab3ea3b704c76926e6b7fdb27ebd563e4f28ebcae2f7a67a23ec7ca23fc3 \ + --hash=sha256:27098014fbe30ec38273b36961a9ca639fa6a86c437ba2c8335d657d1134bdbe \ + --hash=sha256:65de555d2075ca5627acf28c6699e8a7725862dc401bfb736e854294697ac5a0 \ + --hash=sha256:9ad5ca2d6de35a6cc268df2933522c38da56992c4db8ef106c71f54504e4ed1e \ + --hash=sha256:b88e2925285d4d5729bf3f992769541045621202c83892c881f78571a6273013 \ + --hash=sha256:f2d6c9d6031cb20ad95d5bf96cf713b351fd2fc7c96d3c8c54591eaf1ebcfae6 \ + --hash=sha256:4b481ae92cd4b9260cbf80bccb97c08038f7ba4ac9b1552c4fffcaf6cce01349 \ + --hash=sha256:1ca6ebfe0bd65d838e1eb8f22ddd163a6551378995514b00b8e17b97cde9f816 \ + --hash=sha256:38d9509d38c19bc728d09e33e1a66da77706b2a343283fc8c49726fe0f41ac2f \ + --hash=sha256:40790209995acb758ee4dc7527a48e229014f79321236dcb860c0e5f5e623bf4 \ + --hash=sha256:bf98db330df24181ac00b3609bcd5c6fa86e9eccfe7d720067ee5d1e338670f4 \ + --hash=sha256:9eda444e7acb2a80ae06406c1afc5dd7c53721f75f3cafdb06ca4e7bafc4c3d2 \ + --hash=sha256:81f70510234c0135c162f9414f8b56eee40c0b4db9650b0f33517bfc6afa1010 \ + --hash=sha256:33440d3af60278a2a151159bb3ab375ba50053b172aca6435c27f43d72ede1c8 \ + --hash=sha256:12e30de63a4414ba66f8afb32f6d0ec0205fb0896dfba152de17ca9bf9062591 \ + --hash=sha256:956a7fcce0f52dae3bc3e24a5a1d951001c17a85fda688b21ef135c9b62a6c9a \ + --hash=sha256:334af29b27dc086509112ec28cb5f7ad6f8f9d7ccbebb1eb4bf9db7e349a4a1d \ + --hash=sha256:c9d5f5dde8660de357de88cd2fdb845c469ae1bea2e19a7379067dc6afd715c6 \ + --hash=sha256:0ed56f3ad312edd0fa1081350be85ad0d8a0cad48698c91fb5b6640504ca91fa \ + --hash=sha256:ebe8fc843f13fd7ef8faab641ba9886b8c021451b1aef5585c34ab44a95d1bce \ + --hash=sha256:d4d00b9303db36d20cb903baa2d5416c3a49a4f2a35c2d8f272ebbd313fddfed \ + --hash=sha256:0a9f8d0e91d7cd924e1f5f5c6552d1160701bc8bf2f27b495ec9a928dd70922b \ + --hash=sha256:167d4f86a715b1f938f9ab0a5a152956231ebbec0021ab99d2c9019f4d056315 \ + --hash=sha256:b7e58df09ecbfc4f19fd1ac271749745aa8f35bd47590223fb31504ed570ff63 \ + --hash=sha256:c89ae59edc27ce6a9d00dd6f233cad857e89671889df3220fa7d6c331857f9d8 \ + --hash=sha256:92dab173ad3f2037e9f0b0fcda9cb49c2b1f7d84c3826de474db5ee47f0568fe \ + --hash=sha256:32d4c454b5cf509bde88d0cb5503d3359760a536669faae12eeacd9f68c2746f \ + --hash=sha256:d3fc8dd82851903dc9b084a4cd44b389d53c9a1871a51a29692d062f181a3633 \ + --hash=sha256:15ea584c214a57a62977f324d10c28a70c29192d0a5955575d5ece06f434098a \ + --hash=sha256:ba6ac9aab10c2fc239174a6b77ffb9e6d7bdfac003a7515ef097ffa5955ee4cf \ + --hash=sha256:a8180242606d24682fdc2a8a28fc16be06ad4f0405a3c0ed0efffbce80ce8f05 \ + --hash=sha256:12a056f8aef3bc982b69d29222d5d510d3be4b862dd8b3745845c761c946edc5 \ + --hash=sha256:dc971e42d6435e8212af4c39317abd9a9adb63b546b0799c3831a5371ab9c04d \ + --hash=sha256:1c138b74d1e5a15f019edd10c4f37276d6e97f1dc37c40f268bd94203e3a51f9 \ + --hash=sha256:ea855e6e9c712e97d6bcfebce2b54085cf4d1e60e12799b9796e84c12ae3b302 \ + --hash=sha256:dc93fc2c125fb57f3fd3fc95f12a7e1415f562955437e0ed59805090c69030e3 \ + --hash=sha256:5f3dd48827c00d4c78e994f76625902f46513e057d371f516fb00142c9ff1412 \ + --hash=sha256:46b50512d1b6c249e7618b286b56842a60b493c9a2ee12ca90924312bc510354 \ + --hash=sha256:551f881725c5c7be28051849a4fb94fc0670c1c9ffd2ff5243e343f83314cc81 \ + --hash=sha256:41965eb1c1fa836815f8889e312dca3964adf23462d8250abcb8020729e59f31 \ + --hash=sha256:7c714f84eae2537084c785ee845660d47b3cb670c8b5069a8eb8db90b2e2387e \ + --hash=sha256:d16e300b2c51051b42c71dbc86689c2804ad10a6ccadb4033d888265d0b49690 \ + --hash=sha256:08f7999d6d7ca0382572e4b1c51c573d495f9e76b75b9edc20d963227b000fcf \ + --hash=sha256:1d24a8e49bc3e94500a77313525c503c4c21a22360af32c27fc606b6cc0bf2f6 \ + --hash=sha256:ef65be077fa48999bd48ba57deccef408c71c2338b3850c7654cb056ce34971a \ + --hash=sha256:008fd56ef147fc6f04979e735357ee7f694fb0985de60ecfe4572ac05e50a30d \ + --hash=sha256:513f57d968585245849ce4167469b1ca3808a4e800164075ed5ab825596a7945 \ + --hash=sha256:cc74689372685d9a5a70d4ab30ede6e38d07e54a6c23ea8a3a8d53b8d50b05f4 \ + --hash=sha256:3d550fd2b18d83a8a80e6169f96b78e543539aa4732553de0d0a50b94092e1e3 \ + --hash=sha256:38dba797c3720f240d093488c9658f4c486d2263041314a5183f9fda1b025847 \ + --hash=sha256:62a6ab5cb9c4bdc3e91c3afe0f2deea0fb5bac185056158ba62aa136ca301d00 \ + --hash=sha256:f328788ebdd7bd5607ac1291a27ad14823408d2e2e55f4f2d59b7bdfce3019da \ + --hash=sha256:3cf96e42782107b9e12dcecc6f27f1a44e95a26ed1f44a0d8f3eb549eb337f19 \ + --hash=sha256:f029ac1c6fe6facdc99700b0169cfe5fb0bd7e5e373a1299fe5084af6982b983 + +requests==2.32.3 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 + +rpds-py==0.23.0 \ + --hash=sha256:1b36e993b95f0744a94a5add7624cfaf77b91805819c1a960026bc452f95841e \ + --hash=sha256:72a0dd4d599fadaf519d4e4b8092e5d7940057c61e70f9f06c1d004a47895204 \ + --hash=sha256:bba83d703c6728a3a2676a14a9649d7cc87b9e4654293f13f8d4b4d7007d6383 \ + --hash=sha256:1191bf5975a0b001c161a62d5833a6b2f838b10ff19e203910dd6210e88d89f5 \ + --hash=sha256:3154e132e685f907813ace8701721ad4420244f6e07afc2a61763894e8a22961 \ + --hash=sha256:62d8fe953110a98a118cacdc1ca79fe344a946c72a2d19fa7d17d0b2ace58f3d \ + --hash=sha256:2e27dfcea222c81cd8bece98a73ebb8ca69870de01dc27002d433ad06e55dd8b \ + --hash=sha256:7cca21adfefe5a2237f1e64d769c1ed7ccdc2515d376d1774e7fbe918e03cd8c \ + --hash=sha256:8c708f5c2d604e0acc9489df3ea879f4fc75030dfa590668fd959fda34fcc0b8 \ + --hash=sha256:c23cbff21154951731866358e983d01d536a2c0f60f2765be85f00682eae60d9 \ + --hash=sha256:16826a5346e293bedf0acd5c2f4c8e05415b1970aa3cc448eea19f02724dd453 \ + --hash=sha256:1e0fb88357f59c70b8595bc8e5887be35636e646a9ab519c1876063159812cf6 \ + --hash=sha256:c79544d0be2c7c3891fe448bc006666410bc219fdf29bf35990f0ea88ff72b64 \ + --hash=sha256:827b334702a04df2e1b7fe85ed3784512f6fd3d3a40259180db0c8fdeb20b37f \ + --hash=sha256:0e1ece346395e127a8024e5c13d304bdd7dbd094e05329a2f4f27ea1fbe14aa3 \ + --hash=sha256:3adc0b2e71e62fde524389634df4b53f4d16d5f3830ab35c1e511d50b75674f6 \ + --hash=sha256:b1eb4757f9c9f96e26a420db97c3ecaa97568961ce718f1f89e03ce1f59ec12e \ + --hash=sha256:e17402e8f3b49a7ec22e7ef7bbbe0ac0797fcbf0f1ba844811668ef24b37fc9d \ + --hash=sha256:8212c5d25514386a14a032fde7f7f0383a88355f93a1d0fde453f38ebdc43a1b \ + --hash=sha256:5211b646a0beb1f8f4b1cde8c7c073f9d6ca3439d5a93ea0874c8ece6cab66a9 \ + --hash=sha256:83f71359d81cfb3bd39522045d08a7031036fb0b1b0a43a066c094cc52a9fd00 \ + --hash=sha256:9e66aaa24e0dc3cfaf63a8fc2810ae296792c18fb4cfb99868f52e7c598911b6 \ + --hash=sha256:35336790b4d70c31a59c922d7d603010fe13c5ff56a1dce14849b6bb6a2ad4b9 \ + --hash=sha256:377ba75ebce48d5df69b0ab2e3333cd86f6acfee8cf0a2c286af4e32e4a8b499 \ + --hash=sha256:784a79474675ee12cab90241f3df328129e15443acfea618df069a7d67d12abb \ + --hash=sha256:f1023b1de400ef9d3d9f8f9e88f3f5d8c66c26e48c3f83cffe83bd423def8d81 \ + --hash=sha256:d1f3baf652aeb91775eb3343535890156b07e0cbb2a7b72651f4bbaf7323d40f \ + --hash=sha256:6593dc9b225f8fc900df43c40625c998b8fa99ba78ec69bcd073fe3fb1018a5d \ + --hash=sha256:75d5a2c5629e3582aa73c3a11ac0a3dd454e86cc70188a9b6e2ed51889c331dd \ + --hash=sha256:64ba22924340d7e200b48befcc75ff2379301902381ca4ebbfec81d80c5216b5 \ + --hash=sha256:04d7fc114ca57d25f0d8c324d2d0ddd675df92b2f7da8284f806711c25fe00f7 \ + --hash=sha256:5ff50d7a5b206af7ac8342255ae3ab6c6c86d86520f4413bf9d2561bf4f1ffa1 \ + --hash=sha256:3b147c0d49de69dac573c8e05a5f7edf18a83136bf8c98e2cd3e87dafee184e5 \ + --hash=sha256:5bc79d528e65c877a5e254ddad394d51797bc6bba44c9aa436f61b94448d5f87 \ + --hash=sha256:ce1a2fe8eea2e956a11112ba426b9be79b2da65e27a533cf152ba8e9882bf9be \ + --hash=sha256:e2c26f1e0ebbe85dc275816cd53fcbb225aaf7923a4d48b7cdf8b8eb6291e5ae \ + --hash=sha256:6893a88925972635c843eb02a113d7aabacd386c05d54f2fda29125befbc1b05 \ + --hash=sha256:06962dc9462fe97d0355e01525ebafcd317316e80e335272751a1857b7bdec97 \ + --hash=sha256:04882cc4adbdc2778dd49f5ed71b1d9ab43349c45cde7e461456d0432d7d323e \ + --hash=sha256:c46247ea1382758987417b9c47b05d32dc7f971cd2553e7b3088a76ad48c5a67 \ + --hash=sha256:fa93e2460b7791872a5dd355438b854a5d9ab317107380c2143d94a1ca5b10a7 \ + --hash=sha256:784d2ef454b42451a1efca40f888105536b6d2374d155c14f51831980c384461 \ + --hash=sha256:aae64cb7faaecd5d36ebcb99dc3f0196f4357586e095630207047f35183431fb \ + --hash=sha256:d8c754d4d021a010df79e0ce10b2dbf0ed12997ff4e508274337fdceed32275f \ + --hash=sha256:96f0261ef2a45c9dc48c4105ab798e8ba1c0c912ae5c59c2d9f899242cf3ed79 \ + --hash=sha256:3cb0ddf0ecc705f8f6dfe858e703c1b9b3ea240b1f56e33316e89dc6c2994ac0 \ + --hash=sha256:c7fee301c715ce2fed4c0620a65dff12686002061cd38c6f11a427f64bd0c8ff \ + --hash=sha256:aef4f05059aa6f5f22c76f23f45b6908da4871589c9efb882e58c33ebf8f4c4f \ + --hash=sha256:77c3e51d994c39227facc742001b7be98c2ad634f8a0cf2ed08c30cf2f7f9249 \ + --hash=sha256:9901d57e8dc3b7245d349a255af097e309602986a604d073414a3826bc5c2cdd \ + --hash=sha256:c5e3c7d7cdbbd450acb62c5d29d39ea6d5f8584019d391947d73fb998f54acc5 \ + --hash=sha256:d59582ddbeabf217d1b815b60acaec9ff5e2ded79e440c3b3e4ddc970ff59160 \ + --hash=sha256:6097538c81a94d4432de645a20bbbbfa7a0eb52c6dcb7370feda18eb8eed61de \ + --hash=sha256:ac766c8127ee9c9a72f1a6ad6b4291e5acfd14d9685964b771bf8820fe65aeed \ + --hash=sha256:0edf94328feaae49a96caa3459784614365708c38f610316601b996e5f085be1 \ + --hash=sha256:0f74d8babe0139b8ee30c24c65040cdad81e00547e7eefe43d13b31da9d2bbc5 \ + --hash=sha256:cf06007aca17ea31069adc8396d718b714559fd7f7db8302399b4697c4564fec \ + --hash=sha256:9b263adb8e54bc7a5b2b8feebe99ff79f1067037a9178989e9341ea76e935706 \ + --hash=sha256:516cec4c1a45bed3c417c402a2f52515561a1d8e578ff675347dcf4180636cca \ + --hash=sha256:37af2ee37efeb0a09463124cc1e560192cc751c2a5ae650effb36469e1f17dc8 \ + --hash=sha256:312981d4da5dc463baeca3ba23a3e74dc7a48a4500d267566d8e9c0680ac54c6 \ + --hash=sha256:ce1c4277d7f235faa2f31f1aad82e3ab3caeb66f13c97413e738592ec7fef7e0 \ + --hash=sha256:f46d53a6a37383eca41a111df0e9993399a60e9e1e2110f467fddc5de4a43b68 \ + --hash=sha256:56bbf34e129551004e4952db16087bb4912e8cf4fa335ad5c70e126666f97788 \ + --hash=sha256:fbeade9f0284a5c5965f8a4805ef1864e5fb4bc4c5d3d8dd60c5fd2a44f0b51a \ + --hash=sha256:d5504bd1d637e7633d953418520d9b109b0d8a419153a56537938adf068da9d5 \ + --hash=sha256:7730442bb642748dddfbe1de24275bf0cdbae938c68e1c38e0a9d285a056e17d \ + --hash=sha256:374d2c0067f5ef18e73bfb2a555ef0b8f2b01f5b653a3eca68e9fbde5625c305 \ + --hash=sha256:a8983725590ddeb62acf7e585badb7354fa71e3d08d3326eaac6886aa91e526c \ + --hash=sha256:048dc18eb2cc83a67bec07c6f9ffe1da83fb94d5af6cc32e333248013576dc4c \ + --hash=sha256:f4b699830ced68db4294e2e47f25a4ff935a54244814b76fa683e0b857391e3e \ + --hash=sha256:1fa3476c9845152091f62edca5e543df77fc0fc2e83027c389fa4c4f52633369 \ + --hash=sha256:c6c98bde8ec93dd4e19c413e3ac089fb0ff731da54bab8aaf1e8263f55f01406 \ + --hash=sha256:947db56d8ee2f567a597f7484ac6c8cb94529181eaa498bd9c196079c395c69f \ + --hash=sha256:a20fa5cd1cb074c145c3955732cfc3eca19bef16d425b32f14c3d275230110fb \ + --hash=sha256:f27867c24f0a81065ef94e575dbb1846867257994ac41ebbe5e66c6a3976ac73 \ + --hash=sha256:5e549c7ef1ae42b79878bff27c33363b2de77f23de2f4c19541ef69ae4c11ac7 \ + --hash=sha256:0b3b3553d9216153eb3f8cf0d369b0e31e83912e50835ee201794d9b410e227f \ + --hash=sha256:b233a2bdb15dbb4c05b0c79c94d2367a05d0c54351b76c74fdc81aae023a2df8 \ + --hash=sha256:d2e0cace96976f4e86fc3c51cb3fba24225976e26341e958be42f3d8d0a634ee \ + --hash=sha256:210aa7c699cc61320630c4be33348d9bfef4785fabd6f33ea6be711d4eb45f1f \ + --hash=sha256:7cd550ee493adab33e95ce00cb42529b0435c916ed949d298887ee9acdcd3f2f \ + --hash=sha256:174602fe067a5b622ce47a5b09022e0128c526a308354abd9cc4bf0391f3cfd2 \ + --hash=sha256:b8b7b4e5cc5a981a147e1602cf4bd517e57617f9a4c7e96a22a27e4d18de2523 \ + --hash=sha256:aa9d67acbcf2cb11acd44da7d41a0495b7799a32fb7ec9a6bc0b14d8552e00fb \ + --hash=sha256:f482453aeebdae7774781e8c9b1884e0df0bdb1c61f330f95c63a401dfc2fc31 \ + --hash=sha256:eb841a8e1c2615dfc721d3c28fe81e6300e819a01d3305ecd7f75c7d58c31b2b \ + --hash=sha256:41f6bb731bfcbd886bd6399717971dd881d759ea831b9f513bc57a10f52c7d53 \ + --hash=sha256:a49aeb989ee5e057137910059610bfa8f571a4af674404ce05c59862bbeeecbe \ + --hash=sha256:670c29a74f8e632aa58b48425b12d026703af1ea5e3b131adbb2601c7ae03108 \ + --hash=sha256:e5305ee98053a0f0155e4e5f9fe4d196fa2e43ae7c2ecc61534babf6390511d9 \ + --hash=sha256:903344afbc46dfb488a73a7eeb9c14d8484c6d80eb402e6737a520a55327f26c \ + --hash=sha256:87b8e416f55f2be671d5dbf55e7517a8144f8b926609d2f1427f8310c95e4e13 \ + --hash=sha256:8529a28b0dffe7e0c56537912ab8594df7b71b24032622aadce33a2643beada5 \ + --hash=sha256:55fe404f2826c5821661e787dffcb113e682d9ff011d9d39a28c992312d7029b \ + --hash=sha256:1bda53037dcac2465d0b2067a7129283eb823c7e0175c0991ea7e28ae7593555 \ + --hash=sha256:7c2ba6b0f4eccf3738a03878c13f18037931c947d70a75231448954e42884feb \ + --hash=sha256:95d7ffa91b423c974fb50384561736aa16f5fb7a8592d81b2ca5fcaf8afd69a0 \ + --hash=sha256:c1523dae0321bf21d0e4151a7438c9bd26c0b712602fb56116efd4ee5b463b5d \ + --hash=sha256:cec9feef63e213ec9f9cac44d8454643983c422b318b67059da796f55780b4d4 \ + --hash=sha256:f9c49366f19c06ce31af1312ae4718292081e73f454a56705e7d56acfd25ac1e \ + --hash=sha256:f119176191c359cb33ff8064b242874bfb1352761379bca8e6ccb74a6141db27 \ + --hash=sha256:ffac3b13182dc1bf648cde2982148dc9caf60f3eedec7ae639e05636389ebf5d + +ruamel.yaml==0.18.10 \ + --hash=sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1 \ + --hash=sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58 + +ruamel.yaml.clib==0.2.12 \ + --hash=sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5 \ + --hash=sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969 \ + --hash=sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df \ + --hash=sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76 \ + --hash=sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6 \ + --hash=sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd \ + --hash=sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a \ + --hash=sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da \ + --hash=sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28 \ + --hash=sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6 \ + --hash=sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e \ + --hash=sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e \ + --hash=sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52 \ + --hash=sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642 \ + --hash=sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2 \ + --hash=sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3 \ + --hash=sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4 \ + --hash=sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb \ + --hash=sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632 \ + --hash=sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d \ + --hash=sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c \ + --hash=sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd \ + --hash=sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31 \ + --hash=sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680 \ + --hash=sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d \ + --hash=sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5 \ + --hash=sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4 \ + --hash=sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a \ + --hash=sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475 \ + --hash=sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef \ + --hash=sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6 \ + --hash=sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf \ + --hash=sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1 \ + --hash=sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01 \ + --hash=sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6 \ + --hash=sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3 \ + --hash=sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987 \ + --hash=sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45 \ + --hash=sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519 \ + --hash=sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7 \ + --hash=sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285 \ + --hash=sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed \ + --hash=sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7 \ + --hash=sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12 \ + --hash=sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b \ + --hash=sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f + +tomli==2.2.1 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff + +urllib3==2.3.0 \ + --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ + --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d diff --git a/.github/workflows/build-guard.yml b/.github/workflows/build-guard.yml index 5be92ff3..2b8d9c94 100644 --- a/.github/workflows/build-guard.yml +++ b/.github/workflows/build-guard.yml @@ -27,5 +27,5 @@ jobs: - name: Validate Dependabot config run: | - python3 -m pip install check-jsonschema + python3 -m pip install --require-hashes -r .github/requirements.txt check-jsonschema --schemafile https://json.schemastore.org/dependabot-2.0.json .github/dependabot.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd7b1d35..7eedb655 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -182,6 +182,7 @@ jobs: NEXT_PUBLIC_TURNSTILE_SITE_KEY=${{ env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || vars.NEXT_PUBLIC_TURNSTILE_SITE_KEY }} NEXT_PUBLIC_GA_ID=${{ env.NEXT_PUBLIC_GA_ID || vars.NEXT_PUBLIC_GA_ID }} NEXT_PUBLIC_ANDROID_PACKAGE_NAME=${{ env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME || vars.NEXT_PUBLIC_ANDROID_PACKAGE_NAME }} + NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS=${{ env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS || vars.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS }} SENTRY_ORG=${{ env.SENTRY_ORG || vars.SENTRY_ORG }} SENTRY_PROJECT=${{ env.SENTRY_PROJECT || vars.SENTRY_PROJECT }} NEXT_PUBLIC_APP_NAME=${{ env.NEXT_PUBLIC_APP_NAME || vars.NEXT_PUBLIC_APP_NAME }} diff --git a/Dockerfile b/Dockerfile index f3bf7d0d..b3bb2144 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,12 @@ # =============================== # 0. Global deterministic settings # =============================== -ARG NODE_IMAGE=node:24.18.1-alpine3.24@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 ARG SOURCE_DATE_EPOCH=1767225600 # =============================== # 0.1. Base layer with npm upgrade (pinned by hash) # =============================== -FROM ${NODE_IMAGE} AS base +FROM node:24.18.1-alpine3.24@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 AS base # Update npm to version 12 without using `npm install -g` (avoids scorecard "npmCommand not pinned" flag). # /usr/local/bin/npm already symlinks to /usr/local/lib/node_modules/npm/bin/npm-cli.js, @@ -119,6 +118,7 @@ ARG NEXT_PUBLIC_LEGAL_EMAIL ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY ARG NEXT_PUBLIC_GA_ID ARG NEXT_PUBLIC_ANDROID_PACKAGE_NAME +ARG NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS # Optional compile-time overrides ARG NEXT_PUBLIC_DONATE_URL ARG NEXT_PUBLIC_DEFAULT_DOMAIN @@ -146,6 +146,7 @@ ENV NEXT_PUBLIC_LEGAL_EMAIL=${NEXT_PUBLIC_LEGAL_EMAIL} ENV NEXT_PUBLIC_TURNSTILE_SITE_KEY=${NEXT_PUBLIC_TURNSTILE_SITE_KEY} ENV NEXT_PUBLIC_GA_ID=${NEXT_PUBLIC_GA_ID} ENV NEXT_PUBLIC_ANDROID_PACKAGE_NAME=${NEXT_PUBLIC_ANDROID_PACKAGE_NAME} +ENV NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS=${NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS} ENV NEXT_PUBLIC_DONATE_URL=${NEXT_PUBLIC_DONATE_URL} ENV NEXT_PUBLIC_DEFAULT_DOMAIN=${NEXT_PUBLIC_DEFAULT_DOMAIN} ENV NEXT_PUBLIC_ATTENDANCE_TARGET_MIN=${NEXT_PUBLIC_ATTENDANCE_TARGET_MIN} @@ -207,7 +208,7 @@ RUN sed -i 's|/app/|/|g' .next/standalone/server.js # =============================== # 3. Runtime layer # =============================== -FROM ${NODE_IMAGE} AS runner +FROM node:24.18.1-alpine3.24@sha256:f70403e87646dc51b45295f4b8b70cdad0b63d2297c4c9899119b03f7af7a6b3 AS runner ARG SOURCE_DATE_EPOCH ARG APP_COMMIT_SHA diff --git a/mobile/lib/config/app_config.dart b/mobile/lib/config/app_config.dart index e6f82ff6..9e12c60d 100644 --- a/mobile/lib/config/app_config.dart +++ b/mobile/lib/config/app_config.dart @@ -81,7 +81,7 @@ class AppConfig { /// Current application version (derived from Infisical compilation injection). static String get appVersion => - const String.fromEnvironment('APP_VERSION', defaultValue: '4.5.4'); + const String.fromEnvironment('APP_VERSION', defaultValue: '4.5.5'); /// Commit SHA injected by CI for release builds. static String get appCommitSha => diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 11c7514c..3d78750c 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 4.5.4+1 +version: 4.5.5+1 environment: sdk: ^3.11.4 diff --git a/package-lock.json b/package-lock.json index ca3e74f0..e7226ce5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostclass", - "version": "4.5.4", + "version": "4.5.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostclass", - "version": "4.5.4", + "version": "4.5.5", "dependencies": { "@hookform/resolvers": "^5.5.7", "@radix-ui/react-alert-dialog": "^1.1.23", diff --git a/package.json b/package.json index 9463f7f2..a42effbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostclass", - "version": "4.5.4", + "version": "4.5.5", "private": true, "engines": { "node": ">=22.12.0", diff --git a/public/openapi/openapi.yaml b/public/openapi/openapi.yaml index b4379472..1617f18d 100644 --- a/public/openapi/openapi.yaml +++ b/public/openapi/openapi.yaml @@ -6,7 +6,7 @@ openapi: 3.1.0 info: title: GhostClass API - version: 4.5.4 + version: 4.5.5 description: | **GhostClass API** provides endpoints for authentication, profile synchronization, attendance integrations with EzyGo, telemetry, and build provenance. diff --git a/scripts/update-pinned-artifacts.sh b/scripts/update-pinned-artifacts.sh index 8eb082b1..7241ac47 100644 --- a/scripts/update-pinned-artifacts.sh +++ b/scripts/update-pinned-artifacts.sh @@ -3,6 +3,8 @@ set -euo pipefail ROOT_DIR=$(dirname "$(dirname "${BASH_SOURCE[0]}")") PIN_FILE="$ROOT_DIR/.devcontainer/pinned-artifacts.json" +TARBALLS_DIR="$ROOT_DIR/.devcontainer/tarballs" +mkdir -p "$TARBALLS_DIR" if [ ! -f "$PIN_FILE" ]; then echo "Pinned artifacts file not found: $PIN_FILE" >&2 @@ -27,30 +29,52 @@ else exit 1 fi -# Update npm packages: fetch registry metadata and write dist.tarball and dist.shasum +# Update npm packages: fetch registry metadata, download tarball, compute sha256 update_npm() { pkg_name=$1 key_name=${2:-$1} echo "Fetching npm metadata for $pkg_name (latest)..." meta="$tmp/${key_name}.json" curl -fsSL "https://registry.npmjs.org/$pkg_name/latest" -o "$meta" - + if [ "$HAS_JQ" = true ]; then url=$(jq -r '.dist.tarball' "$meta") - shasum=$(jq -r '.dist.shasum' "$meta") - jq --arg u "$url" --arg s "$shasum" --arg key "$key_name" '.[$key] |= . + {url: $u, shasum: $s}' "$PIN_FILE" > "$tmp/pinned.json" && mv "$tmp/pinned.json" "$PIN_FILE" else - node -e ' + url=$(node -e ' const fs = require("fs"); const meta = JSON.parse(fs.readFileSync(process.argv[1])); - const pin = JSON.parse(fs.readFileSync(process.argv[2])); - const key = process.argv[3]; - pin[key] = Object.assign({}, pin[key], { - url: meta.dist.tarball, - shasum: meta.dist.shasum - }); - fs.writeFileSync(process.argv[2], JSON.stringify(pin, null, 2) + "\n"); - ' "$meta" "$PIN_FILE" "$key_name" + console.log(meta.dist.tarball); + ' "$meta") + fi + + echo "Downloading $pkg_name tarball to compute sha256..." + curl -fsSL "$url" -o "$tmp/${key_name}.tgz" + sha256=$(sha256sum "$tmp/${key_name}.tgz" | cut -d' ' -f1) + + # Determine filename for .devcontainer/tarballs/ + tarball_filename="${pkg_name##*/}.tgz" # e.g. "npm.tgz", "@infisical/cli" -> "cli.tgz" + # Use key_name-based filename for clarity + case "$key_name" in + npm) tarball_filename="npm.tgz" ;; + playwright) tarball_filename="playwright.tgz" ;; + playwright_core) tarball_filename="playwright-core.tgz" ;; + firebase) tarball_filename="firebase-tools.tgz" ;; + infisical) tarball_filename="infisical.tgz" ;; + *) tarball_filename="${key_name}.tgz" ;; + esac + cp "$tmp/${key_name}.tgz" "$TARBALLS_DIR/$tarball_filename" + echo " ✅ Saved to .devcontainer/tarballs/$tarball_filename (sha256: $sha256)" + + if [ "$HAS_JQ" = true ]; then + jq --arg u "$url" --arg s "$sha256" --arg key "$key_name" '.[$key] |= {url: $u, sha256: $s}' "$PIN_FILE" > "$tmp/pinned.json" && mv "$tmp/pinned.json" "$PIN_FILE" + else + node -e ' + const fs = require("fs"); + const pin = JSON.parse(fs.readFileSync(process.argv[1])); + const key = process.argv[4]; + pin[key] = { url: process.argv[2], sha256: process.argv[3] }; + fs.writeFileSync(process.argv[1], JSON.stringify(pin, null, 2) + "\n"); + ' "$PIN_FILE" "$url" "$sha256" "$key_name" fi } @@ -99,12 +123,53 @@ update_supabase() { fi } +# Update gh CLI: fetch latest release asset URL and compute sha256 +update_gh() { + echo "Fetching latest gh CLI release info..." + api="https://api.github.com/repos/cli/cli/releases/latest" + release="$tmp/gh_release.json" + curl -fsSL "$api" -o "$release" + + if [ "$HAS_JQ" = true ]; then + asset_url=$(jq -r '.assets[] | select(.name | test("gh_.*_linux_amd64\\.tar\\.gz$")) | .browser_download_url' "$release" | head -n1) + else + asset_url=$(node -e ' + const fs = require("fs"); + const rel = JSON.parse(fs.readFileSync(process.argv[1])); + const match = (rel.assets || []).find(a => /gh_.*_linux_amd64\.tar\.gz$/.test(a.name)); + console.log(match ? match.browser_download_url : ""); + ' "$release") + fi + + if [ -z "$asset_url" ]; then + echo "Could not find gh release asset URL" >&2 + return 1 + fi + + echo "Downloading gh asset to compute sha256..." + curl -fsSL "$asset_url" -o "$tmp/gh.tar.gz" + sha256=$(sha256sum "$tmp/gh.tar.gz" | cut -d' ' -f1) + + if [ "$HAS_JQ" = true ]; then + jq --arg u "$asset_url" --arg s "$sha256" '.gh |= {url: $u, sha256: $s}' "$PIN_FILE" > "$tmp/pinned.json" && mv "$tmp/pinned.json" "$PIN_FILE" + else + node -e ' + const fs = require("fs"); + const pin = JSON.parse(fs.readFileSync(process.argv[1])); + pin.gh = { url: process.argv[2], sha256: process.argv[3] }; + fs.writeFileSync(process.argv[1], JSON.stringify(pin, null, 2) + "\n"); + ' "$PIN_FILE" "$asset_url" "$sha256" + fi +} + # Update entries update_npm "npm" "npm" update_npm "playwright" "playwright" +update_npm "playwright-core" "playwright_core" update_npm "firebase-tools" "firebase" update_npm "@infisical/cli" "infisical" update_supabase +update_gh echo "✅ Updated $PIN_FILE:" cat "$PIN_FILE" diff --git a/src/app/.well-known/assetlinks.json/__tests__/route.test.ts b/src/app/.well-known/assetlinks.json/__tests__/route.test.ts new file mode 100644 index 00000000..885d8885 --- /dev/null +++ b/src/app/.well-known/assetlinks.json/__tests__/route.test.ts @@ -0,0 +1,75 @@ +/// +import { afterEach, describe, expect, it } from "vitest"; +import { GET } from "../route"; + +describe("Android Asset Links (.well-known/assetlinks.json) Route", () => { + const originalNextPubPackageName = + process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME; + const originalNextPubFingerprints = + process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS; + const originalPackageName = process.env.ANDROID_PACKAGE_NAME; + const originalFingerprints = process.env.ANDROID_SHA256_FINGERPRINTS; + + afterEach(() => { + delete process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME; + delete process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS; + delete process.env.ANDROID_PACKAGE_NAME; + delete process.env.ANDROID_SHA256_FINGERPRINTS; + + if (originalNextPubPackageName !== undefined) { + process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME = originalNextPubPackageName; + } + if (originalNextPubFingerprints !== undefined) { + process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS = + originalNextPubFingerprints; + } + if (originalPackageName !== undefined) { + process.env.ANDROID_PACKAGE_NAME = originalPackageName; + } + if (originalFingerprints !== undefined) { + process.env.ANDROID_SHA256_FINGERPRINTS = originalFingerprints; + } + }); + + it("should return default package name and empty fingerprints list when env vars are unset", async () => { + delete process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME; + delete process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS; + delete process.env.ANDROID_PACKAGE_NAME; + delete process.env.ANDROID_SHA256_FINGERPRINTS; + + const response = GET(); + expect(response.status).toBe(200); + expect(response.headers.get("Content-Type")).toBe("application/json"); + + const data = await response.json(); + expect(data).toHaveLength(1); + expect(data[0].target.package_name).toBe("com.devakesu.apps.ghostclass"); + expect(data[0].target.sha256_cert_fingerprints).toEqual([]); + }); + + it("should support build-time NEXT_PUBLIC_ variables for multiple SHA256 fingerprints", async () => { + process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME = + "com.devakesu.apps.ghostclass"; + process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS = + "82:D2:7B:08:7C:61:2D:C0:83:ED:A4:A4:6E:05:C2:C9:BC:A0:1C:68:3A:77:7C:25:56:77:1B:09:D8:76:C5:D7, AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"; + + const response = GET(); + const data = await response.json(); + + expect(data[0].target.package_name).toBe("com.devakesu.apps.ghostclass"); + expect(data[0].target.sha256_cert_fingerprints).toEqual([ + "82:D2:7B:08:7C:61:2D:C0:83:ED:A4:A4:6E:05:C2:C9:BC:A0:1C:68:3A:77:7C:25:56:77:1B:09:D8:76:C5:D7", + "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99", + ]); + }); + + it("should include delegate_permission relations", async () => { + const response = GET(); + const data = await response.json(); + + expect(data[0].relation).toEqual([ + "delegate_permission/common.handle_all_urls", + "delegate_permission/common.get_login_creds", + ]); + }); +}); diff --git a/src/app/.well-known/assetlinks.json/route.ts b/src/app/.well-known/assetlinks.json/route.ts new file mode 100644 index 00000000..0c9d9662 --- /dev/null +++ b/src/app/.well-known/assetlinks.json/route.ts @@ -0,0 +1,47 @@ +export const dynamic = "force-dynamic"; + +const DEFAULT_PACKAGE_NAME = "com.devakesu.apps.ghostclass"; + +/** + * Serves the Digital Asset Links file required by Android App Links + * at GET /.well-known/assetlinks.json + * + * Uses build-time environment variables NEXT_PUBLIC_ANDROID_PACKAGE_NAME + * and NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS (Infisical /build-time) + * to ensure build transparency, auditability, and reproducibility. + */ +export function GET() { + const packageName = process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME?.trim() || + process.env.ANDROID_PACKAGE_NAME?.trim() || + DEFAULT_PACKAGE_NAME; + + const rawFingerprints = process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS || + process.env.ANDROID_SHA256_FINGERPRINTS || + ""; + + const fingerprints = rawFingerprints + .split(",") + .map((fp) => fp.trim().toUpperCase()) + .filter(Boolean); + + const assetLinks = [ + { + relation: [ + "delegate_permission/common.handle_all_urls", + "delegate_permission/common.get_login_creds", + ], + target: { + namespace: "android_app", + package_name: packageName, + sha256_cert_fingerprints: fingerprints, + }, + }, + ]; + + return Response.json(assetLinks, { + headers: { + "Content-Type": "application/json", + "Cache-Control": "public, max-age=3600, stale-while-revalidate=86400", + }, + }); +} diff --git a/src/lib/validate-env.ts b/src/lib/validate-env.ts index 649449e7..aa429de0 100644 --- a/src/lib/validate-env.ts +++ b/src/lib/validate-env.ts @@ -397,6 +397,26 @@ function validateServiceAccountJson(errors: string[], warnings: string[]) { } } +function validateAndroidFingerprints(errors: string[]) { + const raw = ( + process.env.NEXT_PUBLIC_ANDROID_SHA256_FINGERPRINTS || + process.env.ANDROID_SHA256_FINGERPRINTS + )?.trim(); + if (!raw) return; + + const parts = raw.split(",").map((s) => s.trim()).filter(Boolean); + for (const fp of parts) { + const segments = fp.split(":"); + const isValid = segments.length === 32 && + segments.every((b) => b.length === 2 && !Number.isNaN(parseInt(b, 16))); + if (!isValid) { + errors.push( + `❌ Invalid Android SHA-256 cert fingerprint format: "${fp}". Must be 32 colon-separated hex bytes.`, + ); + } + } +} + function validateAppCheck(errors: string[]) { const enforceAppCheck = process.env.ENFORCE_APP_CHECK; if ( @@ -410,6 +430,7 @@ function validateAppCheck(errors: string[]) { if (!process.env.NEXT_PUBLIC_ANDROID_PACKAGE_NAME) { errors.push("❌ NEXT_PUBLIC_ANDROID_PACKAGE_NAME is required"); } + validateAndroidFingerprints(errors); if (!process.env.FIREBASE_APP_ID_ANDROID) { errors.push("❌ FIREBASE_APP_ID_ANDROID is required"); }