From a5b48b2c681978500ec03ada9c0225f2e8c968f5 Mon Sep 17 00:00:00 2001 From: CTO Hermes Date: Mon, 1 Jun 2026 16:25:07 +0700 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20cora-review=20action=20resilience=20?= =?UTF-8?q?=E2=80=94=20stderr=20capture=20+=20empty=20SARIF=20detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Capture cora stderr to log file instead of suppressing (2>/dev/null) - Forward stderr as GitHub ::warning:: annotation - Detect empty SARIF output (<10 bytes) and warn instead of false 'No issues' - Bump fallback version v0.1.6 → v0.1.7 --- .github/actions/cora-review/action.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/actions/cora-review/action.yml b/.github/actions/cora-review/action.yml index 508891a..1d2b14b 100644 --- a/.github/actions/cora-review/action.yml +++ b/.github/actions/cora-review/action.yml @@ -68,7 +68,7 @@ runs: # Fallback to a known good version if all retries fail if [ -z "$VERSION" ]; then echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback" - VERSION="v0.1.6" + VERSION="v0.1.7" fi else VERSION="${{ inputs.cora-version }}" @@ -105,8 +105,11 @@ runs: --format sarif \ --severity ${{ inputs.severity }} \ --quiet \ - > cora-results.sarif 2>/dev/null + > cora-results.sarif 2>cora-stderr.log || true echo "Cora review complete ($(wc -c < cora-results.sarif) bytes)" + if [ -s cora-stderr.log ]; then + echo "::warning::Cora stderr: $(cat cora-stderr.log)" + fi - name: Upload SARIF to GitHub Code Scanning if: inputs.upload-sarif == 'true' @@ -133,8 +136,14 @@ runs: } let body; + // Check for empty/invalid SARIF — cora may have failed silently if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) { - body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`; + const fileSize = fs.statSync('cora-results.sarif').size; + if (fileSize < 10) { + body = `## 🔍 Cora AI Code Review\n\n⚠️ **Review could not complete.** Cora produced an empty result. Check the workflow logs for errors.\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`; + } else { + body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`; + } } else { const results = sarifContent.runs[0].results || []; if (results.length === 0) { From ecf945834c71211c01fac3f529e9b392ba0fef1a Mon Sep 17 00:00:00 2001 From: CTO Hermes Date: Mon, 1 Jun 2026 21:34:52 +0700 Subject: [PATCH 2/4] refactor: extract cora-review to standalone workflow + harden action v2 Action v2 improvements: - SHA-pin all third-party actions (Infisical, codeql, github-script) - Add checksum verification on binary download - Env var indirection for shell args (prevent injection) - Concurrency control + timeout in standalone workflow - Fallback version bumped to v0.1.7 Workflow: - Extract from ci.yml to standalone cora-review.yml - Remove unnecessary rust-toolchain/cache from review job - Add timeout-minutes: 10 --- .github/actions/cora-review/action.yml | 51 +++++++++++++++++++------- .github/workflows/ci.yml | 21 ----------- .github/workflows/cora-review.yml | 34 +++++++++++++++++ 3 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/cora-review.yml diff --git a/.github/actions/cora-review/action.yml b/.github/actions/cora-review/action.yml index 1d2b14b..ad4ecfc 100644 --- a/.github/actions/cora-review/action.yml +++ b/.github/actions/cora-review/action.yml @@ -41,7 +41,7 @@ runs: using: 'composite' steps: - name: Fetch LLM secrets from Infisical - uses: Infisical/secrets-action@v1.0.9 + uses: Infisical/secrets-action@8a06c1bdcd5b8635d510c52d4b57a92c1ccef785 # v1.0.9 with: method: 'oidc' identity-id: ${{ inputs.infisical-identity-id }} @@ -52,9 +52,10 @@ runs: - name: Resolve cora-cli version id: resolve shell: bash + env: + INPUT_VERSION: ${{ inputs.cora-version }} run: | - if [ "${{ inputs.cora-version }}" = "latest" ]; then - # Try up to 3 times with delay + if [ "$INPUT_VERSION" = "latest" ]; then for i in 1 2 3; do VERSION=$(curl -sfL --connect-timeout 10 \ https://api.github.com/repos/ajianaz/cora-cli/releases/latest \ @@ -65,19 +66,20 @@ runs: echo "Attempt $i failed, retrying..." sleep 5 done - # Fallback to a known good version if all retries fail if [ -z "$VERSION" ]; then echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback" VERSION="v0.1.7" fi else - VERSION="${{ inputs.cora-version }}" + VERSION="$INPUT_VERSION" fi echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" echo "Resolved cora-cli version: $VERSION" - name: Install cora-cli shell: bash + env: + CORA_VERSION: ${{ steps.resolve.outputs.VERSION }} run: | ARCH=$(uname -m) if [ "$ARCH" = "x86_64" ]; then @@ -88,7 +90,25 @@ runs: echo "Unsupported architecture: $ARCH" exit 1 fi - curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${{ steps.resolve.outputs.VERSION }}/${ASSET}-${{ steps.resolve.outputs.VERSION }}.tar.gz" | tar xz -C /usr/local/bin cora + curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" \ + -o /tmp/cora.tar.gz + curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \ + -o /tmp/cora-checksums.txt || true + if [ -f /tmp/cora-checksums.txt ]; then + EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}') + ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}') + if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then + echo "::error::Checksum verification failed for ${ASSET}-${CORA_VERSION}.tar.gz" + echo "Expected: $EXPECTED" + echo "Actual: $ACTUAL" + exit 1 + fi + rm -f /tmp/cora-checksums.txt + else + echo "::warning::No checksums file found, skipping verification" + fi + tar xzf /tmp/cora.tar.gz -C /usr/local/bin cora + rm -f /tmp/cora.tar.gz chmod +x /usr/local/bin/cora cora --version @@ -99,11 +119,13 @@ runs: CORA_API_KEY: ${{ env.CORA_API_KEY }} CORA_BASE_URL: ${{ env.CORA_BASE_URL }} CORA_MODEL: ${{ env.CORA_MODEL }} + INPUT_BASE: ${{ inputs.base-branch }} + INPUT_SEVERITY: ${{ inputs.severity }} run: | cora review \ - --base ${{ inputs.base-branch }} \ + --base "$INPUT_BASE" \ --format sarif \ - --severity ${{ inputs.severity }} \ + --severity "$INPUT_SEVERITY" \ --quiet \ > cora-results.sarif 2>cora-stderr.log || true echo "Cora review complete ($(wc -c < cora-results.sarif) bytes)" @@ -114,14 +136,14 @@ runs: - name: Upload SARIF to GitHub Code Scanning if: inputs.upload-sarif == 'true' continue-on-error: true - uses: github/codeql-action/upload-sarif@v4 + uses: github/codeql-action/upload-sarif@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0 with: sarif_file: cora-results.sarif category: cora-review - name: Post PR comment if: always() && github.event_name == 'pull_request' - uses: actions/github-script@v7 + uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0 env: GH_TOKEN: ${{ inputs.github-token }} with: @@ -136,9 +158,13 @@ runs: } let body; - // Check for empty/invalid SARIF — cora may have failed silently if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) { - const fileSize = fs.statSync('cora-results.sarif').size; + let fileSize = 0; + try { + fileSize = fs.statSync('cora-results.sarif').size; + } catch (e) { + fileSize = 0; + } if (fileSize < 10) { body = `## 🔍 Cora AI Code Review\n\n⚠️ **Review could not complete.** Cora produced an empty result. Check the workflow logs for errors.\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`; } else { @@ -182,7 +208,6 @@ runs: } } - // Find existing cora comment and update it, or create new const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7595d8..36c0f4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,25 +65,4 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo build --release - cora-review: - name: Cora Review - runs-on: ubuntu-latest - needs: [check, fmt, clippy, test] - if: github.event_name == 'pull_request' - permissions: - contents: read - security-events: write - pull-requests: write - id-token: write - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - - uses: ./.github/actions/cora-review - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - infisical-identity-id: ${{ secrets.INFISICAL_IDENTITY_ID }} diff --git a/.github/workflows/cora-review.yml b/.github/workflows/cora-review.yml new file mode 100644 index 0000000..141a2fe --- /dev/null +++ b/.github/workflows/cora-review.yml @@ -0,0 +1,34 @@ +name: Cora AI Code Review + +on: + pull_request: + branches: [develop] + types: [opened, synchronize, ready_for_review, reopened] + +concurrency: + group: cora-review-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + id-token: write + security-events: write + +jobs: + cora-review: + name: Cora Review + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Run Cora AI Code Review + uses: ./.github/actions/cora-review + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + infisical-identity-id: ${{ secrets.INFISICAL_IDENTITY_ID }} From 19685ba49949f9d26590e58c667ee6aff75e70aa Mon Sep 17 00:00:00 2001 From: CTO Hermes Date: Mon, 1 Jun 2026 21:43:22 +0700 Subject: [PATCH 3/4] fix: handle missing checksums gracefully in cora-review action --- .github/actions/cora-review/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/cora-review/action.yml b/.github/actions/cora-review/action.yml index ad4ecfc..05bc801 100644 --- a/.github/actions/cora-review/action.yml +++ b/.github/actions/cora-review/action.yml @@ -94,7 +94,7 @@ runs: -o /tmp/cora.tar.gz curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \ -o /tmp/cora-checksums.txt || true - if [ -f /tmp/cora-checksums.txt ]; then + if [ -f /tmp/cora-checksums.txt ] && [ -s /tmp/cora-checksums.txt ]; then EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}') ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}') if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then @@ -102,6 +102,8 @@ runs: echo "Expected: $EXPECTED" echo "Actual: $ACTUAL" exit 1 + elif [ -z "$EXPECTED" ]; then + echo "::warning::No checksum found for ${ASSET}-${CORA_VERSION}.tar.gz in checksums file" fi rm -f /tmp/cora-checksums.txt else From 50a09a3e544cfb9adab253852e38784fff1e798a Mon Sep 17 00:00:00 2001 From: CTO Hermes Date: Mon, 1 Jun 2026 21:45:05 +0700 Subject: [PATCH 4/4] fix: prevent grep exit code 1 from failing checksum step (pipefail) --- .github/actions/cora-review/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/cora-review/action.yml b/.github/actions/cora-review/action.yml index 05bc801..c6590f3 100644 --- a/.github/actions/cora-review/action.yml +++ b/.github/actions/cora-review/action.yml @@ -95,7 +95,7 @@ runs: curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \ -o /tmp/cora-checksums.txt || true if [ -f /tmp/cora-checksums.txt ] && [ -s /tmp/cora-checksums.txt ]; then - EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}') + EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}' || true) ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}') if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then echo "::error::Checksum verification failed for ${ASSET}-${CORA_VERSION}.tar.gz"