Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,15 @@ jobs:
matrix:
python-version: ${{ github.event_name == 'push' && fromJSON('["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]') || fromJSON('["3.12"]') }}

services:
redis:
image: redis@sha256:4bfd9eca23339865dc14fe75f6d9ae643f714924623978dd2798f1a673b08f43 # redis:7-alpine amd64
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Start Redis (no persistence)
run: |
docker run -d --name redis -p 6379:6379 \
redis:7-alpine redis-server --save "" --appendonly no
until docker exec redis redis-cli ping | grep -q PONG; do sleep 1; done

Comment on lines +72 to +77
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and read the relevant section
if [ -f ".github/workflows/ci.yml" ]; then
  echo "=== File exists: .github/workflows/ci.yml ==="
  echo "=== Lines 70-80 ==="
  sed -n '70,80p' .github/workflows/ci.yml | cat -n
else
  echo "File not found. Searching for workflow files..."
  fd -e yml -e yaml | head -20
fi

Repository: cachekit-io/cachekit-py

Length of output: 638


🏁 Script executed:

# Let's examine the full workflow file to check for any timeout settings or other safeguards
echo "=== Full CI workflow (first 50 lines) ==="
head -50 .github/workflows/ci.yml | cat -n

echo ""
echo "=== Checking for timeout configurations ==="
grep -n "timeout" .github/workflows/ci.yml

echo ""
echo "=== Checking the full 'Start Redis' step context ==="
sed -n '70,85p' .github/workflows/ci.yml | cat -n

Repository: cachekit-io/cachekit-py

Length of output: 2418


🏁 Script executed:

# Let's find which job contains this Redis step and verify its timeout
echo "=== Looking for the job that contains the Redis step (around lines 55-80) ==="
sed -n '55,80p' .github/workflows/ci.yml | cat -n

echo ""
echo "=== Checking lines around line 60 for job definition ==="
sed -n '55,65p' .github/workflows/ci.yml | cat -n

Repository: cachekit-io/cachekit-py

Length of output: 1710


🏁 Script executed:

# Let's verify the suggested fix syntax is valid for the shell context
# GitHub Actions uses bash by default, so let's check if the syntax is correct
echo "=== Testing the proposed loop syntax ==="
cat << 'EOF' > /tmp/test_syntax.sh
#!/bin/bash
# This tests if the suggested syntax would work

# Simulate what the suggested fix would do
for i in {1..60}; do
  echo "Iteration $i"
  if [ $i -eq 3 ]; then
    echo "Would break here"
    break
  fi
  sleep 0.1
done

# The second part of the suggested fix
echo "After loop"
EOF

bash -n /tmp/test_syntax.sh && echo "✓ Syntax is valid" || echo "✗ Syntax error"

echo ""
echo "=== Examining the actual behavior of the current unbounded loop ==="
echo "If docker run fails OR Redis crashes, the 'until' loop will:"
echo "1. Keep running 'docker exec redis redis-cli ping' indefinitely"
echo "2. Fail at each iteration but continue"
echo "3. Not exit until the job timeout (15 minutes)"
echo "4. The actual error (container not running) gets hidden"

Repository: cachekit-io/cachekit-py

Length of output: 462


Bound the Redis readiness loop to prevent hang-on-failure.

The until loop on line 76 is unbounded. If the container fails to start or exits early, it will spin indefinitely until the 15-minute job timeout, obscuring the actual failure and wasting CI time across all test runs.

Suggested fix
    - name: Start Redis (no persistence)
      run: |
        docker run -d --name redis -p 6379:6379 \
          redis:7-alpine redis-server --save "" --appendonly no
-        until docker exec redis redis-cli ping | grep -q PONG; do sleep 1; done
+        for i in {1..60}; do
+          if docker exec redis redis-cli ping 2>/dev/null | grep -q PONG; then
+            break
+          fi
+          if ! docker ps --filter "name=^redis$" --filter "status=running" | grep -q redis; then
+            echo "Redis container is not running"
+            docker logs redis || true
+            exit 1
+          fi
+          sleep 1
+        done
+        docker exec redis redis-cli ping | grep -q PONG || { echo "Redis did not become ready in time"; docker logs redis || true; exit 1; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Start Redis (no persistence)
run: |
docker run -d --name redis -p 6379:6379 \
redis:7-alpine redis-server --save "" --appendonly no
until docker exec redis redis-cli ping | grep -q PONG; do sleep 1; done
- name: Start Redis (no persistence)
run: |
docker run -d --name redis -p 6379:6379 \
redis:7-alpine redis-server --save "" --appendonly no
for i in {1..60}; do
if docker exec redis redis-cli ping 2>/dev/null | grep -q PONG; then
break
fi
if ! docker ps --filter "name=^redis$" --filter "status=running" | grep -q redis; then
echo "Redis container is not running"
docker logs redis || true
exit 1
fi
sleep 1
done
docker exec redis redis-cli ping | grep -q PONG || { echo "Redis did not become ready in time"; docker logs redis || true; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 72 - 77, The readiness loop using
"until docker exec redis redis-cli ping | grep -q PONG" is unbounded and can
hang CI; modify the script that follows "docker run -d --name redis -p 6379:6379
\ redis:7-alpine ..." to bound the wait by replacing the raw until loop with a
retry/timed loop (e.g., a for/while loop with a MAX_ATTEMPTS or a timeout in
seconds), break early on success, and exit non‑zero with a clear error message
if the ping never succeeds (so the job fails fast instead of spinning
indefinitely).

- name: Install dependencies
run: uv sync --python ${{ matrix.python-version }} --group dev

Expand Down
59 changes: 15 additions & 44 deletions .github/workflows/fuzz-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ permissions:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
# Avoid EXDEV "cross-device link" errors on ephemeral runners where
# hostPath cache and overlay are on different filesystems
RUSTUP_HOME: /tmp/rustup
CARGO_HOME: /tmp/cargo

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -29,7 +33,11 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Install Rust nightly
run: rustup toolchain install nightly
run: |
# Pin nightly: cargo-fuzz 0.13.1 → rustix uses rustc_layout_scalar_valid_range_*
# attributes reserved after nightly-2026-04-27. Last known-good date.
rustup toolchain install nightly-2026-04-27
rustup default nightly-2026-04-27
Comment on lines +36 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

Is Rust nightly-2026-04-27 currently available for download?

💡 Result:

Yes, the Rust nightly toolchain for 2026-04-27 is available [1]. You can install this specific version using the rustup toolchain manager by running the following command in your terminal: rustup toolchain install nightly-2026-04-27 This command instructs rustup to download and install the nightly compiler release corresponding to that specific date [2][3][4]. Once installed, you can use it by referencing the toolchain name directly (e.g., rustup run nightly-2026-04-27 cargo build) or by setting it as an override for a specific project directory [2][3]. Note that nightly toolchains are generated daily, and while a build exists for 2026-04-27 [1], it is possible for some nightly builds to be missing certain non-default components (such as clippy or rust-docs) depending on the build status for that day [5]. If you encounter issues with missing components, you can try using the --profile=minimal flag or check the build status via the rustup-components-history project [5].

Citations:


🌐 Web query:

Does cargo-fuzz 0.13.1 have a known issue with rustix and rustc_layout_scalar_valid_range attributes in Rust nightly builds after April 2026?

💡 Result:

Yes, cargo-fuzz 0.13.1 has a known issue with Rust nightly builds from May 2026 [1]. This issue arises because the Rust compiler removed support for the internal rustc_layout_scalar_valid_range_* attributes [2][3][4], which were previously used by dependencies like rustix [1]. As of May 2026, the Rust compiler has fully deprecated and removed these internal attributes in favor of new pattern types [2][3][5]. Because cargo-fuzz 0.13.1 relies on older versions of its dependencies (such as rustix) that still utilize these now-removed attributes, the crate fails to compile on recent nightly toolchains [1]. Users encountering this error are advised to check for updates to cargo-fuzz or its dependencies that have migrated away from these internal compiler attributes [1].

Citations:


Monitor for cargo-fuzz updates to resolve rustix compatibility and remove nightly pin.

The pinned nightly (2026-04-27) is ~19 days old. Rust nightlies are retained for ~90 days before expiring on distribution servers. Once this nightly expires, the workflow will fail. The pin exists because cargo-fuzz 0.13.1 depends on rustix, which uses the now-removed rustc_layout_scalar_valid_range_* internal compiler attributes. Track updates to cargo-fuzz that resolve this dependency issue, allowing migration away from a pinned nightly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/fuzz-smoke.yml around lines 36 - 40, The workflow pins a
specific nightly via the rustup commands ("rustup toolchain install
nightly-2026-04-27" and "rustup default nightly-2026-04-27") because cargo-fuzz
0.13.1 pulls in rustix which uses the removed rustc_layout_scalar_valid_range_*
attributes; monitor cargo-fuzz (and rustix) releases for a version that removes
or replaces that dependency and, when available, drop the nightly pin by
removing those rustup lines and reverting to the project’s normal toolchain
selection so the workflow no longer depends on a time‑boxed nightly.


- name: Install cargo-fuzz
run: cargo install --locked cargo-fuzz
Expand All @@ -42,30 +50,21 @@ jobs:
# Create artifacts directory
mkdir -p artifacts

# Define all fuzz targets
# Fuzz targets that compile against cachekit-core 0.1.1.
# 9 encryption/advanced targets are disabled — cachekit-core API
# changed (encrypt_aes_gcm → encrypt_with_keys etc.) and the
# fuzz targets haven't been updated. See #114.
FUZZ_TARGETS=(
byte_storage_compress
byte_storage_decompress
encryption_roundtrip
byte_storage_corrupted_envelope
byte_storage_integer_overflow
byte_storage_checksum_collision
byte_storage_empty_data
byte_storage_format_injection
encryption_key_derivation
encryption_nonce_reuse
encryption_truncated_ciphertext
encryption_aad_injection
encryption_large_payload
integration_layered_security
)

# Run each target for 60 seconds
for target in "${FUZZ_TARGETS[@]}"; do
echo "Fuzzing $target (60s)..."
echo "Fuzzing $target..."

# Run fuzzing, capture exit code
if ! cargo +nightly fuzz run "$target" -- -max_total_time=60; then
if ! cargo +nightly-2026-04-27 fuzz run "$target" -- -max_total_time=60; then
echo "::warning::Fuzz target '$target' found potential issues"
# Continue to test other targets even if one fails
touch artifacts/.fuzz_failures
Expand All @@ -88,31 +87,3 @@ jobs:
path: rust/fuzz/artifacts/
retention-days: 30
if-no-files-found: warn

- name: Post fuzzing summary
if: always()
run: |
{
echo "## Fuzzing Smoke Test Results"
echo ""

if [ -f rust/fuzz/artifacts/.fuzz_failures ]; then
echo "Status: Some fuzz targets found potential issues"
echo ""

# Count crashes by type
CRASHES=$(find rust/fuzz/artifacts -name 'crash-*' 2>/dev/null | wc -l || echo 0)
TIMEOUTS=$(find rust/fuzz/artifacts -name 'timeout-*' 2>/dev/null | wc -l || echo 0)
OOMS=$(find rust/fuzz/artifacts -name 'oom-*' 2>/dev/null | wc -l || echo 0)

echo "- Crashes: ${CRASHES}"
echo "- Timeouts: ${TIMEOUTS}"
echo "- OOM: ${OOMS}"
echo ""
echo "Check uploaded artifacts for crash details."
else
echo "Status: All fuzz targets passed (14 targets, 60s each)"
echo ""
echo "No crashes, timeouts, or OOM errors detected."
fi
} >> "$GITHUB_STEP_SUMMARY"
9 changes: 5 additions & 4 deletions rust/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
rmp-serde = "1"

[dependencies.cachekit-storage]
path = ".."
# Target pure Rust core without Python
default-features = false
# Fuzz targets use cachekit-core's internal module paths (byte_storage::, encryption::)
# not the thin PyO3 wrapper's flat re-exports.
[dependencies.cachekit_storage]
package = "cachekit-core"
version = "=0.1.1"
features = ["compression", "checksum", "messagepack", "encryption"]

# Prevent this from interfering with normal build
Expand Down
Loading