Skip to content
Draft
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
1 change: 1 addition & 0 deletions revdep/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/cloud.noindex
/cloud
/review/
/notifications/
131 changes: 131 additions & 0 deletions revdep/NOTIFY-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Maintainer Notification Script

This script (`notify-maintainers.sh`) automates the process of notifying package maintainers about reverse dependency issues discovered during igraph development.

## Features

- **GitHub Integration**: Automatically creates GitHub issues for packages hosted on GitHub
- **Email Fallback**: Generates email drafts for packages not on GitHub or when GitHub access fails
- **Template-based**: Creates well-formatted issue descriptions with all relevant information

## Prerequisites

### For GitHub Issues (Optional)

```bash
# Install GitHub CLI
# On macOS:
brew install gh

# On Linux:
# See https://github.com/cli/cli#installation

# Authenticate with GitHub
gh auth login
```

### For Email Drafts

No additional setup required - the script will generate email templates that can be manually sent.

## Usage

```bash
./notify-maintainers.sh
```

The script will:

1. Check if `gh` CLI is available
2. For each package (Cascade, jewel, rSpectral):
- Check if the GitHub repository is accessible
- If accessible: Create a GitHub issue directly using `gh issue create`
- If not accessible: Create an email draft in `notifications/`

The script determines upfront which action to take and only creates the appropriate output (either GitHub issue OR email draft, not both).

## Output

Files are created in the `notifications/` directory **only for packages that require email drafts**:

- `{Package}-email.txt` - Complete email draft with subject and body

For packages with accessible GitHub repositories, issues are created directly and no local files are saved.

## Manual Steps

### If GitHub Issues Fail

If you see authentication or permission errors when creating GitHub issues:

1. Check GitHub authentication: `gh auth status`
2. Authenticate if needed: `gh auth login`
3. Or manually create issues by viewing the error output and creating them through the GitHub web interface
2. Click "Issues" → "New Issue"
3. Copy the content from `notifications/{Package}-issue.md`

### For Email Drafts

When GitHub repositories are not accessible, email drafts are automatically generated. To send these emails:

1. Review the email content in `notifications/{Package}-email.txt`
2. Copy the content
3. Create a new email in your email client
4. Update the "To:" field with the actual maintainer email (check CRAN package page)
5. Paste the subject and body
6. Send the email

## Package Information

### Cascade
- **GitHub**: https://github.com/fbertran/Cascade
- **Issue**: Namespace collision warning
- **Severity**: Minor

### jewel
- **GitHub**: https://github.com/annaplaksienko/jewel
- **Issue**: Integer validation error
- **Severity**: High

### rSpectral
- **GitHub**: https://github.com/cmclean5/rSpectral
- **Issue**: Modularity test failures
- **Severity**: Medium

## Customization

To modify the issue templates or add more packages, edit the script directly. Each package section follows this pattern:

```bash
PACKAGE="PackageName"
GITHUB_URL="https://github.com/owner/repo"
VERSION="x.y.z"
ISSUE_TYPE="Description"
SEVERITY="High/Medium/Low"

cat > "$OUTPUT_DIR/${PACKAGE}-issue.md" << 'EOF'
# Issue title

Issue description...
EOF
```

## Troubleshooting

### "gh CLI not found"
Install the GitHub CLI as described in Prerequisites.

### "Failed to create issue"
- Check GitHub authentication: `gh auth status`
- Ensure you have permission to create issues in the repository
- The repository may have issues disabled

### "GitHub repository not accessible"
- The repository might be private
- The repository URL might be incorrect
- Use the email draft fallback instead

## See Also

- [problems-analysis.md](problems-analysis.md) - Detailed analysis of each issue
- [examples/](examples/) - Runnable reproduction scripts
38 changes: 38 additions & 0 deletions revdep/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Reverse Dependency Problem Examples

This directory contains minimal reproducible examples for the three packages that have newly broken checks compared to the most recent CRAN version of igraph.

## Files

- **`cascade-circulant-issue.R`** - Demonstrates the namespace collision between `igraph::circulant` and `magic::circulant`
- **`jewel-integer-issue.R`** - Demonstrates the strict integer validation error in `rewire_impl()`
- **`rspectral-modularity-issue.R`** - Demonstrates the automatic weight usage in modularity calculations

## Running the Examples

Each example is a standalone R script that can be run with:

```r
Rscript cascade-circulant-issue.R
Rscript jewel-integer-issue.R
Rscript rspectral-modularity-issue.R
```

Or from within R:

```r
pkgload::load_all() # Load the development version of igraph
source("revdep/examples/cascade-circulant-issue.R")
source("revdep/examples/jewel-integer-issue.R")
source("revdep/examples/rspectral-modularity-issue.R")
```

## Summary of Issues

| Package | Issue | Severity | Type |
|---------|-------|----------|------|
| Cascade | Namespace collision warning | Low | Inadvertent behavior change |
| jewel | Integer validation error | High | Uncovered downstream bug |
| rSpectral | Modularity test failures | Medium | Breaking change |

See `../problems-analysis.md` for detailed analysis and recommendations.
36 changes: 36 additions & 0 deletions revdep/examples/cascade-circulant-issue.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env Rscript
# Minimal reproducible example for Cascade namespace collision issue
# Issue: Warning when loading both igraph and magic packages

cat("=== Cascade Namespace Collision Issue ===\n\n")

# Load igraph (which now exports circulant)
library(igraph)
cat("Loaded igraph\n")
cat("igraph exports circulant:", "circulant" %in% ls("package:igraph"), "\n\n")

# Show that igraph::circulant exists as a constructor spec
cat("igraph::circulant is exported as a constructor spec\n")
cat("It is meant to be used with make_graph(), but make_graph() is deprecated\n")
cat("Example: make_graph(circulant(10, c(1, 3)))\n\n")

# Demonstrate the preferred way
cat("Preferred way (using make_circulant directly):\n")
g2 <- make_circulant(10, c(1, 3))
cat("Created graph with make_circulant:", vcount(g2), "vertices,", ecount(g2), "edges\n\n")

# Simulate what happens when magic package is loaded
cat("Attempting to demonstrate namespace collision:\n")
cat("If the magic package were loaded, it would show:\n")
cat(" Warning: replacing previous import 'igraph::circulant' by 'magic::circulant'\n\n")

cat("Root cause:\n")
cat("- igraph added make_circulant() and its constructor alias circulant() in v2.2.1.9003\n")
cat("- magic package also has a circulant() function for creating circulant matrices\n")
cat("- When both packages are loaded, there's a namespace collision\n\n")

cat("Assessment:\n")
cat("- This is an inadvertent behavior change in igraph\n")
cat("- The circulant() function is primarily a constructor alias\n")
cat("- Users should use make_circulant() directly\n")
cat("- Cascade package can resolve by explicitly importing magic::circulant\n")
70 changes: 70 additions & 0 deletions revdep/examples/jewel-integer-issue.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env Rscript
# Minimal reproducible example for jewel integer validation issue
# Issue: rewire_impl now strictly validates that n is an integer

cat("=== jewel Integer Validation Issue ===\n\n")

library(igraph)

# Create a simple graph for testing
g <- make_ring(10)
cat("Created test graph:", vcount(g), "vertices,", ecount(g), "edges\n\n")

# Test 1: Integer value (should work)
cat("Test 1: Using integer value (100)\n")
tryCatch({
result <- rewire(g, keeping_degseq(niter = 100))
cat("✓ SUCCESS: Integer value works\n")
cat(" Result:", vcount(result), "vertices,", ecount(result), "edges\n\n")
}, error = function(e) {
cat("✗ FAILED:", conditionMessage(e), "\n\n")
})

# Test 2: Non-integer value (will fail)
cat("Test 2: Using non-integer value (2.45)\n")
tryCatch({
result <- rewire(g, keeping_degseq(niter = 2.45))
cat("✓ SUCCESS: Non-integer value works\n")
cat(" Result:", vcount(result), "vertices,", ecount(result), "edges\n\n")
}, error = function(e) {
cat("✗ FAILED:", conditionMessage(e), "\n\n")
})

# Test 3: Computed value (simulating jewel package scenario)
cat("Test 3: Using computed value (p * 0.05 where p = 49)\n")
p <- 49
niter <- p * 0.05 # = 2.45
cat(" Computed niter =", niter, "\n")
tryCatch({
result <- rewire(g, keeping_degseq(niter = niter))
cat("✓ SUCCESS: Computed value works\n")
cat(" Result:", vcount(result), "vertices,", ecount(result), "edges\n\n")
}, error = function(e) {
cat("✗ FAILED:", conditionMessage(e), "\n\n")
})

# Test 4: Workaround using as.integer (should work)
cat("Test 4: Using as.integer() workaround\n")
cat(" Computed niter =", niter, "-> as.integer(niter) =", as.integer(niter), "\n")
tryCatch({
result <- rewire(g, keeping_degseq(niter = as.integer(niter)))
cat("✓ SUCCESS: Integer conversion works\n")
cat(" Result:", vcount(result), "vertices,", ecount(result), "edges\n\n")
}, error = function(e) {
cat("✗ FAILED:", conditionMessage(e), "\n\n")
})

cat("Root cause:\n")
cat("- rewire_impl() converts n with as.numeric(), preserving fractional parts\n")
cat("- C code in rinterface_extra.c now strictly validates integer values\n")
cat("- Previously may have silently truncated, now explicitly rejects\n\n")

cat("Assessment:\n")
cat("- This uncovered a bug in the jewel package\n")
cat("- niter should logically be an integer (number of iterations)\n")
cat("- jewel should use ceiling(), floor(), or round() on computed niter\n\n")

cat("Recommendation:\n")
cat("- Fix in jewel: niter <- ceiling(p * 0.05)\n")
cat("- OR fix in igraph for backward compatibility:\n")
cat(" Add as.integer() in rewire_keeping_degseq() before calling rewire_impl()\n")
75 changes: 75 additions & 0 deletions revdep/examples/rspectral-modularity-issue.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env Rscript
# Minimal reproducible example for rSpectral modularity issue
# Issue: Modularity values have changed slightly

cat("=== rSpectral Modularity Calculation Issue ===\n\n")

library(igraph)

# Create a test graph
cat("Creating test graph...\n")
g <- make_full_graph(5) + make_full_graph(5) + make_full_graph(5)
cat("Graph:", vcount(g), "vertices,", ecount(g), "edges\n\n")

# Test modularity with clear community structure
membership <- c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3)

cat("Test 1: Modularity without weights\n")
mod1 <- modularity(g, membership, weights = NULL)
cat(" Modularity:", mod1, "\n\n")

cat("Test 2: Modularity with default (may use weights if present)\n")
mod2 <- modularity(g, membership)
cat(" Modularity:", mod2, "\n\n")

# Add weights to demonstrate the issue
cat("Test 3: Adding edge weights to graph\n")
E(g)$weight <- 1.0
cat(" Added uniform weights of 1.0\n")
mod3 <- modularity(g, membership)
cat(" Modularity with weights:", mod3, "\n\n")

# Different weights
cat("Test 4: Using random edge weights\n")
set.seed(42)
E(g)$weight <- runif(ecount(g))
mod4 <- modularity(g, membership)
cat(" Modularity with random weights:", mod4, "\n\n")

cat("Test 5: Explicitly passing weights = NULL (doesn't disable auto-detection!)\n")
mod5 <- modularity(g, membership, weights = NULL)
cat(" Modularity (weights = NULL):", mod5, "\n")
cat(" Note: This does NOT match Test 1 because weights = NULL doesn't disable auto-detection!\n")
cat(" The function still uses the 'weight' attribute if it exists.\n\n")

cat("Test 6: WORKAROUND - Using weights = numeric() to disable auto-detection\n")
mod6 <- modularity(g, membership, weights = numeric())
cat(" Modularity (weights = numeric()):", mod6, "\n")
cat(" Note: This DOES match Test 1! The workaround works!\n")
cat(" numeric() is not NULL, so auto-detection is skipped\n")
cat(" Then !all(is.na(numeric())) is FALSE, so weights gets set to NULL internally\n\n")

cat("Root cause:\n")
cat("- igraph v2.2.1.9004 added: 'Use \"weights\" edge attribute in modularity() if available'\n")
cat("- modularity() now automatically uses edge weights if present\n")
cat("- Previously may have ignored weights by default\n")
cat("- rSpectral tests also show: 'This graph was created by an old(er) igraph version'\n\n")

cat("Assessment:\n")
cat("- This is likely an inadvertent behavior change in igraph\n")
cat("- Modularity differences are small but significant for exact tests\n")
cat("- Expected: 0.408, Actual: 0.432 (difference: +0.024)\n")
cat("- Expected: 0.3776, Actual: 0.3758 (difference: -0.0018)\n\n")

cat("Recommendation for rSpectral:\n")
cat("1. Update saved graph objects using upgrade_graph()\n")
cat("2. Review whether graphs should have weights or not\n")
cat("3. WORKAROUND: Use weights = numeric() to get unweighted modularity\n")
cat(" Example: modularity(g, membership, weights = numeric())\n")
cat("4. Or remove unintended weights: g <- delete_edge_attr(g, 'weight')\n")
cat("5. Update expected test values if the new weighted modularity is correct\n\n")

cat("Recommendation for igraph:\n")
cat("1. Document weights = numeric() as the official workaround\n")
cat("2. Or fix so that weights = NULL explicitly disables auto-detection\n")
cat("3. Or clearly document this as a breaking change in NEWS.md\n")
Loading
Loading