-
Notifications
You must be signed in to change notification settings - Fork 3
refactor: extract cora-review to standalone workflow + harden action v2 #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a5b48b2
ecf9458
19685ba
50a09a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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.6" | ||||||||||||||||||||||||||||||||||
| 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,27 @@ 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 ] && [ -s /tmp/cora-checksums.txt ]; then | ||||||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||
| 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,26 +121,31 @@ 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>/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 | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+134
to
+136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
echo "MISSING: $FILE"
exit 1
fi
# Show line range around 120-150
nl -ba "$FILE" | sed -n '110,170p'
# Search for cora-stderr usage
echo "== rg cora-stderr.log =="
rg -n "cora-stderr\.log" -S .github/actions/cora-review/action.yml .github/actions || trueRepository: ajianaz/cora-cli Length of output: 173 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
echo "MISSING: $FILE"
exit 1
fi
# Show line range around 120-170 (covers 132-134)
nl -ba "$FILE" | sed -n '110,170p'
echo "== rg cora-stderr.log (broader context) =="
rg -n "cora-stderr\.log" -S .github/actions || true
echo "== rg for '::warning::Cora stderr' =="
rg -n "::warning::Cora stderr" -S .github/actions || trueRepository: ajianaz/cora-cli Length of output: 173 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
echo "MISSING: $FILE"
exit 1
fi
echo "== Lines 110-170 =="
cat -n "$FILE" | sed -n '110,170p'
echo "== rg cora-stderr.log (action dir) =="
rg -n "cora-stderr\.log" -S .github/actions || true
echo "== rg ::warning::Cora stderr =="
rg -n "::warning::Cora stderr" -S .github/actions || trueRepository: ajianaz/cora-cli Length of output: 3255 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
echo "MISSING: $FILE"
exit 1
fi
echo "== Lines 110-170 =="
cat -n "$FILE" | sed -n '110,170p'
echo "== rg cora-stderr.log (action dir) =="
rg -n "cora-stderr\.log" -S .github/actions || true
echo "== rg ::warning::Cora stderr =="
rg -n "::warning::Cora stderr" -S .github/actions || trueRepository: ajianaz/cora-cli Length of output: 3255 🌐 Web query:
💡 Result: The GitHub Actions workflow command::stop-commands:: is used to disable the processing of subsequent workflow commands, preventing the runner from interpreting them as instructions [1]. The is a unique, randomly generated token that acts as a key to both stop and resume command processing [1][2]. How it works: 1. Disabling commands: When you execute echo "::stop-commands::", the runner stops parsing any following output for workflow commands [1]. This is useful when you need to print text to the logs that happens to contain patterns that look like GitHub Actions commands (e.g., scripts with comments) and you want to ensure they are not accidentally executed by the runner [1]. 2. Resuming commands: To re-enable command processing, you must output the exact same token using the syntax:::: [1]. This tells the runner to resume parsing the workflow output for commands [1]. Best Practices: - Unique tokens: Always use a randomly generated, unique token for each workflow run (e.g., using uuidgen or a language-specific random generator) to ensure security and prevent accidental re-enabling of commands [1]. - Masking: Because the token used to stop commands is echoed to the logs, it is recommended to mask the token using::add-mask:: if you want to keep the token itself hidden from the log output [3]. Example: # Generate a unique token stopMarker=$(uuidgen) # Mask the token for security echo "::add-mask::$stopMarker" # Disable command processing echo "::stop-commands::$stopMarker" # The following will NOT be processed as commands echo "::warning:: This will not be rendered as a warning" # Resume command processing echo "::$stopMarker::" # Commands are processed normally again echo "::warning:: This will be rendered as a warning" Citations:
Don't emit raw Suggested fix if [ -s cora-stderr.log ]; then
- echo "::warning::Cora stderr: $(cat cora-stderr.log)"
+ stop_marker=$(python3 - <<'PY'
+import uuid
+print(uuid.uuid4())
+PY
+)
+ echo "::warning::Cora emitted stderr; see grouped log output below."
+ echo "::group::Cora stderr"
+ echo "::stop-commands::$stop_marker"
+ cat cora-stderr.log
+ echo "::$stop_marker::"
+ echo "::endgroup::"
fi📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - 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: | ||||||||||||||||||||||||||||||||||
|
|
@@ -134,7 +161,17 @@ runs: | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| let body; | ||||||||||||||||||||||||||||||||||
| 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_`; | ||||||||||||||||||||||||||||||||||
| 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 { | ||||||||||||||||||||||||||||||||||
| 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_`; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+164
to
+174
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Malformed non-empty SARIF is reported as a clean pass. If parsing fails but Suggested fix- let sarifContent;
+ let sarifContent;
+ let sarifParseFailed = false;
try {
sarifContent = JSON.parse(fs.readFileSync('cora-results.sarif', 'utf8'));
} catch (e) {
sarifContent = null;
+ sarifParseFailed = true;
}
@@
- if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) {
+ if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) {
let fileSize = 0;
try {
fileSize = fs.statSync('cora-results.sarif').size;
} catch (e) {
fileSize = 0;
}
- if (fileSize < 10) {
+ if (sarifParseFailed || 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_`;
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| const results = sarifContent.runs[0].results || []; | ||||||||||||||||||||||||||||||||||
| if (results.length === 0) { | ||||||||||||||||||||||||||||||||||
|
|
@@ -173,7 +210,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, | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: ajianaz/cora-cli
Length of output: 8098
🏁 Script executed:
rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" -S .github/actions/cora-review/action.ymlRepository: ajianaz/cora-cli
Length of output: 361
🏁 Script executed:
rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.ymlRepository: ajianaz/cora-cli
Length of output: 361
🏁 Script executed:
rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.ymlRepository: ajianaz/cora-cli
Length of output: 361
🏁 Script executed:
Repository: ajianaz/cora-cli
Length of output: 361
🏁 Script executed:
Repository: ajianaz/cora-cli
Length of output: 361
Fail cora asset downloads on HTTP errors (current
curl -sLcan silently write error pages)..github/actions/cora-review/action.ymlusescurl -sLto download both the tarball andchecksums-sha256.txt. If GitHub returns 404/500,curlcan still create/tmp/cora.tar.gzand/tmp/cora-checksums.txt; because verification only errors whenEXPECTEDis non-empty, a bogus/emptyEXPECTEDcan skip checksum validation and the workflow may fail later duringtar.Suggested fix
🤖 Prompt for AI Agents