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
15 changes: 7 additions & 8 deletions .github/workflows/ci-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ name: Java CI
on:
push:
branches: [main]
paths:
- 'src/**'
- 'pom.xml'
- '.github/workflows/ci-java.yml'
pull_request:
branches: [main]
paths:
- 'src/**'
- 'pom.xml'
- '.github/workflows/ci-java.yml'
# NOTE: no `paths:` filter. The `build` job name is a required check
# on main's branch protection, and a `paths:` filter would cause the
# check to be skipped on PRs that don't touch Java — leaving the
# required check stuck at "Waiting for status to be reported", which
# blocks merge of every non-Java PR (e.g. PR #131 phase 5 release infra).
# Java compile is ~1 minute; the cost is worth the always-on signal
# until Phase 6 cutover deletes the Java tree entirely.

permissions: read-all

Expand Down
112 changes: 112 additions & 0 deletions .github/workflows/perf-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: perf-gate

# Performance regression gate. Runs `codeiq index` against fixture-multi-lang
# and asserts wall-clock + node-count budgets. Catches regressions like:
# - Regex pathology re-introduced (e.g. the CertificateAuthDetector
# pre-screen miss that pushed indexing from 0.1s → 42s on PSA).
# - Detector over-emission past the dedup budget.
#
# Trigger: push to main + PRs that touch go/**. Manual via workflow_dispatch.
# Failure is informational on PRs (`continue-on-error`) until the threshold
# is curated against real-world load; once stable, set strict gate.

on:
push:
branches: [main]
paths:
- 'go/**'
- '.github/workflows/perf-gate.yml'
pull_request:
branches: [main]
paths:
- 'go/**'
- '.github/workflows/perf-gate.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
bench:
name: index perf gate (fixture-multi-lang)
runs-on: ubuntu-latest
env:
CGO_ENABLED: '1'
# Per-target budgets. Tune as the fixture grows. Current
# fixture-multi-lang sits at ~50 files; an 8 s ceiling leaves
# headroom over the observed ~0.3 s without hiding obvious
# regressions (10x cushion catches the kinds of regex pathology
# that pushed PSA from 0.1 s → 42 s mid-port).
MAX_INDEX_SECONDS: '8'
MIN_NODES: '40'
MAX_PHANTOM_DROP_RATIO: '50'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.25.10'
cache: true
cache-dependency-path: go/go.sum
- name: Install C toolchain
run: sudo apt-get update -y && sudo apt-get install -y build-essential
- name: Build codeiq
working-directory: go
run: go build -o /tmp/codeiq ./cmd/codeiq
- name: Stage fixture (separate copy so cache writes don't dirty git)
run: cp -r go/testdata/fixture-multi-lang /tmp/fm-perf
- name: Run + measure
id: bench
run: |
set -euo pipefail
START=$(date +%s.%N)
/tmp/codeiq index /tmp/fm-perf > /tmp/perf.log 2>&1
END=$(date +%s.%N)
ELAPSED=$(awk "BEGIN{printf \"%.3f\", $END - $START}")

# Parse the "Files: F Nodes: N Edges: E ..." summary line.
NODES=$(awk -F'[ ]+' '/^Files:/ {print $4}' /tmp/perf.log)
EDGES=$(awk -F'[ ]+' '/^Files:/ {print $6}' /tmp/perf.log)
# Optional "Deduped: D nodes, ... Dropped: P phantom edges"
# line; absence is fine, defaults to 0.
DEDUP_NODES=$(awk -F'[ ,]+' '/^Deduped:/ {print $2}' /tmp/perf.log)
DEDUP_NODES=${DEDUP_NODES:-0}
DROPPED=$(awk -F'[ ]+' '/^Deduped:/ {for(i=1;i<=NF;i++) if($i=="Dropped:") print $(i+1)}' /tmp/perf.log)
DROPPED=${DROPPED:-0}

echo "elapsed=$ELAPSED" >> "$GITHUB_OUTPUT"
echo "nodes=$NODES" >> "$GITHUB_OUTPUT"
echo "edges=$EDGES" >> "$GITHUB_OUTPUT"
echo "dropped=$DROPPED" >> "$GITHUB_OUTPUT"

{
echo "## codeiq perf gate"
echo ""
echo "| metric | value | budget |"
echo "|---|---:|---:|"
echo "| wall-clock (s) | $ELAPSED | $MAX_INDEX_SECONDS |"
echo "| nodes | $NODES | >= $MIN_NODES |"
echo "| edges | $EDGES | — |"
echo "| deduped nodes | $DEDUP_NODES | — |"
echo "| dropped phantom edges | $DROPPED | ratio gated |"
} >> "$GITHUB_STEP_SUMMARY"

cat /tmp/perf.log >> "$GITHUB_STEP_SUMMARY"

# --- Hard gates ---
fail=0
if awk "BEGIN{exit !($ELAPSED > $MAX_INDEX_SECONDS)}"; then
echo "::error::wall-clock $ELAPSED s exceeds budget $MAX_INDEX_SECONDS s"
fail=1
fi
if [ "${NODES:-0}" -lt "$MIN_NODES" ]; then
echo "::error::node count $NODES below minimum $MIN_NODES"
fail=1
fi
if [ "${EDGES:-0}" -gt 0 ] && [ "${DROPPED:-0}" -gt 0 ]; then
RATIO=$(( DROPPED * 100 / (EDGES + DROPPED) ))
if [ "$RATIO" -gt "$MAX_PHANTOM_DROP_RATIO" ]; then
echo "::error::phantom-edge drop ratio ${RATIO}% exceeds ${MAX_PHANTOM_DROP_RATIO}%"
fail=1
fi
fi
exit $fail
124 changes: 124 additions & 0 deletions .github/workflows/release-go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: release-go

# Tag-triggered release pipeline for the codeiq Go binary.
#
# Trigger: push a tag matching `v*.*.*` (e.g. `git tag v1.0.0 && git push --tags`).
# Cross-OS build via per-runner matrix (CGO + native kuzudb/sqlite means
# we can't cross-compile cleanly from a single host).
#
# Phase 5 of the Java→Go port. Replaces release-java.yml (kept around
# until Phase 6 cutover for any emergency Java release).

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g. v1.0.0). Must already exist.'
required: true

permissions:
contents: write
id-token: write # Sigstore keyless via GitHub OIDC
packages: write
attestations: write

jobs:
# Per-target release. Runs the same .goreleaser.yml on each runner;
# archives are merged in the publish job below.
build:
name: build (${{ matrix.os }} / ${{ matrix.goarch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- os: linux
goarch: amd64
runner: ubuntu-latest
- os: linux
goarch: arm64
runner: ubuntu-24.04-arm
- os: darwin
goarch: arm64
runner: macos-14
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.25.10'
cache: true
cache-dependency-path: go/go.sum
- name: Install build deps (linux)
if: runner.os == 'Linux'
run: sudo apt-get update -y && sudo apt-get install -y build-essential
- name: Install Syft (SBOM)
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
- name: Install Cosign (signing)
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1
with:
distribution: goreleaser
version: '~> v2'
# Single-target build per runner; combined publish runs in a
# separate job that consumes all three artifact bundles.
args: build --single-target --clean --snapshot
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.goarch }}
- name: Upload binary artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: codeiq-${{ matrix.os }}-${{ matrix.goarch }}
path: dist/codeiq_*/codeiq*
retention-days: 1

# Combined publish: pulls the three binaries built above, packages
# them with SBOMs, signs the checksum manifest via Sigstore keyless,
# and uploads the GitHub Release. Runs on linux only.
release:
name: publish release
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.25.10'
cache: true
cache-dependency-path: go/go.sum
- name: Install build deps
run: sudo apt-get update -y && sudo apt-get install -y build-essential
- name: Install Syft (SBOM)
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
- name: Install Cosign (signing)
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Download pre-built binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: codeiq-*
path: prebuilt
- uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1
with:
distribution: goreleaser
version: '~> v2'
# Full release: archives + SBOMs + cosign sigs + GitHub Release
# draft + (optional) Homebrew tap. The owning org sets
# HOMEBREW_TAP_GITHUB_TOKEN to publish to homebrew-codeiq;
# forks leave it unset and the brew step skips silently.
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_OWNER: RandomCodeSpace
HOMEBREW_TAP_REPO: homebrew-codeiq
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
- name: Attest release artifacts (build provenance)
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: 'dist/codeiq_*.tar.gz'
Loading