Skip to content

feat: louvain community detection + fix complexity build regression (… #120

feat: louvain community detection + fix complexity build regression (…

feat: louvain community detection + fix complexity build regression (… #120

Workflow file for this run

name: Publish
on:
push:
branches: [main]
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g. 2.1.0, without v prefix). Used to retry a failed stable release.'
required: true
type: string
concurrency:
group: publish-${{ github.event_name }}
cancel-in-progress: true
permissions: {}
jobs:
preflight:
name: Preflight checks
runs-on: ubuntu-latest
# Skip dev publish when the push is a stable release version bump (direct push or merged PR)
if: "${{ github.event_name != 'push' || (!startsWith(github.event.head_commit.message, 'chore: release v') && !contains(github.event.head_commit.message, 'release/v')) }}"
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: npm install
- run: npm test
compute-version:
needs: preflight
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.compute.outputs.version }}
npm_tag: ${{ steps.compute.outputs.npm_tag }}
steps:
- uses: actions/checkout@v4
- name: Compute version
id: compute
run: |
CURRENT=$(node -p "require('./package.json').version")
if [ "${{ github.event_name }}" = "release" ]; then
TAG="${{ github.event.release.tag_name }}"
VERSION="${TAG#v}"
NPM_TAG="latest"
echo "Stable release: $VERSION (from tag $TAG)"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}"
VERSION="${VERSION#v}"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "::error::Invalid version '$VERSION'. Expected semver (e.g. 2.1.0)."
exit 1
fi
NPM_TAG="latest"
echo "Stable release (manual retry): $VERSION"
else
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
DEV_PATCH=$((PATCH + 1))
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
VERSION="${MAJOR}.${MINOR}.${DEV_PATCH}-dev.${SHORT_SHA}"
NPM_TAG="dev"
echo "Dev release: $VERSION"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "npm_tag=$NPM_TAG" >> "$GITHUB_OUTPUT"
build-native:
needs: preflight
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
node_arch: x64
node_os: linux
- os: macos-latest
target: aarch64-apple-darwin
node_arch: arm64
node_os: darwin
- os: macos-14
target: x86_64-apple-darwin
node_arch: x64
node_os: darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
node_arch: x64
node_os: win32
runs-on: ${{ matrix.os }}
name: Build ${{ matrix.target }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: crates/codegraph-core
- name: Install napi-rs CLI
run: npm install -g @napi-rs/cli@3
- name: Build native addon
working-directory: crates/codegraph-core
run: napi build --release --target ${{ matrix.target }}
- name: Upload native binary
uses: actions/upload-artifact@v4
with:
name: native-${{ matrix.node_os }}-${{ matrix.node_arch }}
path: crates/codegraph-core/*.node
if-no-files-found: error
# ── Dev builds: GitHub pre-release with tarballs ──
publish-dev:
if: github.event_name == 'push'
needs: [compute-version, build-native]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: npm install
- name: Set version
env:
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
npm version "$VERSION" --no-git-tag-version --allow-same-version
node scripts/sync-native-versions.js
echo "Packaging version $VERSION"
- name: Disable prepublishOnly
run: npm pkg delete scripts.prepublishOnly
- name: Download native artifacts
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/artifacts/
- name: Pack main package
run: npm pack
- name: Pack platform packages
env:
VERSION: ${{ needs.compute-version.outputs.version }}
shell: bash
run: |
declare -A PACKAGES=(
["linux-x64"]="@optave/codegraph-linux-x64-gnu"
["darwin-arm64"]="@optave/codegraph-darwin-arm64"
["darwin-x64"]="@optave/codegraph-darwin-x64"
["win32-x64"]="@optave/codegraph-win32-x64-msvc"
)
ARTIFACTS="${RUNNER_TEMP}/artifacts"
PKG_DIR="${RUNNER_TEMP}/pkg"
for artifact_dir in "${ARTIFACTS}"/native-*/; do
platform=$(basename "$artifact_dir" | sed 's/^native-//')
pkg_name=${PACKAGES[$platform]}
node_os=${platform%%-*}
node_arch=${platform##*-}
mkdir -p "${PKG_DIR}/$platform"
cp "$artifact_dir"/*.node "${PKG_DIR}/$platform/codegraph-core.node"
cat > "${PKG_DIR}/$platform/package.json" <<PKGJSON
{
"name": "${pkg_name}",
"version": "${VERSION}",
"description": "Native codegraph-core binary for ${node_os}-${node_arch}",
"os": ["${node_os}"],
"cpu": ["${node_arch}"],
"main": "codegraph-core.node",
"files": ["codegraph-core.node"],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/optave/codegraph.git"
}
}
PKGJSON
npm pack "${PKG_DIR}/$platform"
done
- name: Create GitHub pre-release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
TAG="dev-v${VERSION}"
gh release create "$TAG" \
--prerelease \
--title "Dev build ${VERSION}" \
--notes "Dev build from commit \`${{ github.sha }}\` on \`main\`." \
*.tgz
- name: Prune old dev releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# List dev releases sorted newest-first, skip the first 5, delete the rest
# Non-critical: failures here should not fail the workflow
TAGS=$(gh release list --limit 100 --json tagName,isPrerelease,createdAt 2>&1) || {
echo "::warning::Failed to list releases for pruning: ${TAGS}"
exit 0
}
OLD_TAGS=$(echo "$TAGS" | jq -r '
[ .[] | select(.isPrerelease and (.tagName | startswith("dev-v"))) ]
| sort_by(.createdAt) | reverse
| .[5:]
| .[].tagName
' 2>&1) || {
echo "::warning::Failed to parse release list for pruning: ${OLD_TAGS}"
exit 0
}
# When fewer than 5 dev releases exist, OLD_TAGS is empty and the loop is a no-op
echo "$OLD_TAGS" | while read -r tag; do
[ -z "$tag" ] && continue
echo "Deleting old dev release: $tag"
gh release delete "$tag" --yes --cleanup-tag || echo "::warning::Failed to delete release ${tag}"
done
- name: Summary
env:
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
TAG="dev-v${VERSION}"
cat >> "$GITHUB_STEP_SUMMARY" <<EOF
## Dev Build Published
**Version:** \`${VERSION}\`
**Commit:** \`${{ github.sha }}\`
### Download
Tarballs attached to [GitHub release \`${TAG}\`](${{ github.server_url }}/${{ github.repository }}/releases/tag/${TAG}).
\`\`\`bash
# Install the main package (uses WASM fallback):
npm install ${{ github.server_url }}/${{ github.repository }}/releases/download/${TAG}/optave-codegraph-${VERSION}.tgz
# For native performance, also install your platform package:
# Linux x64:
npm install ${{ github.server_url }}/${{ github.repository }}/releases/download/${TAG}/optave-codegraph-linux-x64-gnu-${VERSION}.tgz
# macOS arm64:
npm install ${{ github.server_url }}/${{ github.repository }}/releases/download/${TAG}/optave-codegraph-darwin-arm64-${VERSION}.tgz
\`\`\`
EOF
# ── Stable releases: publish to npm ──
publish:
if: github.event_name != 'push'
needs: [compute-version, build-native]
runs-on: ubuntu-latest
environment: npm-publish
permissions:
contents: write
id-token: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Upgrade npm for OIDC trusted publishing
run: npm install -g npm@latest
- run: npm install
- name: Set version
env:
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
git checkout -- package-lock.json
npm version "$VERSION" --no-git-tag-version --allow-same-version
node scripts/sync-native-versions.js
echo "Publishing version $VERSION"
- name: Disable prepublishOnly
run: npm pkg delete scripts.prepublishOnly
- name: Download native artifacts
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/artifacts/
- name: Check if main package already published
id: check-main
env:
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
PKG="@optave/codegraph"
echo "Checking if $PKG@$VERSION already exists on npm..."
if npm view "$PKG@$VERSION" version 2>/dev/null; then
echo "⚠️ $PKG@$VERSION is already published — will skip publish steps"
echo "already_published=true" >> "$GITHUB_OUTPUT"
else
echo "$PKG@$VERSION is not yet published — proceeding"
echo "already_published=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish platform packages
if: steps.check-main.outputs.already_published == 'false'
env:
VERSION: ${{ needs.compute-version.outputs.version }}
NPM_TAG: ${{ needs.compute-version.outputs.npm_tag }}
shell: bash
run: |
declare -A PACKAGES=(
["linux-x64"]="@optave/codegraph-linux-x64-gnu"
["darwin-arm64"]="@optave/codegraph-darwin-arm64"
["darwin-x64"]="@optave/codegraph-darwin-x64"
["win32-x64"]="@optave/codegraph-win32-x64-msvc"
)
ARTIFACTS="${RUNNER_TEMP}/artifacts"
PKG_DIR="${RUNNER_TEMP}/pkg"
for artifact_dir in "${ARTIFACTS}"/native-*/; do
platform=$(basename "$artifact_dir" | sed 's/^native-//')
pkg_name=${PACKAGES[$platform]}
node_os=${platform%%-*}
node_arch=${platform##*-}
mkdir -p "${PKG_DIR}/$platform"
cp "$artifact_dir"/*.node "${PKG_DIR}/$platform/codegraph-core.node"
cat > "${PKG_DIR}/$platform/package.json" <<PKGJSON
{
"name": "${pkg_name}",
"version": "${VERSION}",
"description": "Native codegraph-core binary for ${node_os}-${node_arch}",
"os": ["${node_os}"],
"cpu": ["${node_arch}"],
"main": "codegraph-core.node",
"files": ["codegraph-core.node"],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/optave/codegraph.git"
}
}
PKGJSON
# Skip if this exact version is already published (idempotent re-runs)
if npm view "${pkg_name}@${VERSION}" version 2>/dev/null; then
echo "⚠️ ${pkg_name}@${VERSION} already published — skipping"
continue
fi
echo "Publishing ${pkg_name}@${VERSION} with --tag ${NPM_TAG}"
npm publish "${PKG_DIR}/$platform" --access public --provenance --tag "$NPM_TAG"
done
- name: Publish main package
if: steps.check-main.outputs.already_published == 'false'
env:
NPM_TAG: ${{ needs.compute-version.outputs.npm_tag }}
run: npm publish --access public --provenance --tag "$NPM_TAG"
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Generate DEPENDENCIES.json
run: mkdir -p generated && npm ls --json --all --omit=dev > generated/DEPENDENCIES.json 2>/dev/null || true
- name: Push version bump via PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.compute-version.outputs.version }}
run: |
TAG="v${VERSION}"
BRANCH="release/v${VERSION}"
# Check if there are version bump changes to push
if git diff --quiet HEAD -- package.json package-lock.json CHANGELOG.md generated/DEPENDENCIES.json; then
echo "No version bump commit to push — skipping PR"
else
git add -f package.json package-lock.json CHANGELOG.md generated/DEPENDENCIES.json
git commit -m "chore: release v${VERSION}"
git push origin "HEAD:refs/heads/${BRANCH}"
gh pr create \
--base main \
--head "$BRANCH" \
--title "chore: release v${VERSION}" \
--body "Automated version bump to \`${VERSION}\` and CHANGELOG update from publish workflow run [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})."
echo "::notice::Version bump PR created for ${BRANCH} → main"
fi
# Push tag (skip if it already exists on remote)
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then
echo "Tag $TAG already exists on remote — skipping tag push"
else
git tag -a "$TAG" -m "release: $TAG"
git push origin "$TAG"
fi