From b1f093fa7db290fd00d860b901757d6fa2cac484 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 29 Sep 2026 13:19:12 +0200 Subject: [PATCH] Add issue policy and script to close stale issues * Add issue policy and script to close stale issues * Update README with current features, input formats and requirements --- CONTRIBUTING.md | 33 ++++++++ README.md | 40 +++++---- docs/agents/core.md | 2 +- scripts/tools/close_stale_issues.py | 123 ++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 scripts/tools/close_stale_issues.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..3ce77ce296 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing to ResInsight + +## What an open issue means + +An open issue is either + +- a confirmed, reproducible bug, or +- work the team realistically intends to do within about 18 months. + +Everything else is closed with a reason. Closing is not deleting: the issue stays searchable, links keep working, and anyone can reopen it with new information. + +Issues are closed when they are + +- **stale**: no activity for about three years. These are closed as *not planned* and labelled `ClosedStale`. +- **obsolete or fixed**: they refer to code, file formats or UI that has since been rewritten or fixed. +- **waiting on the reporter**: labelled `NeedsInput` and closed if there is no answer within 30 days. +- **duplicates** or **not reproducible** (`Cannot Reproduce`). +- **covered by a tracking issue**: related issues are consolidated into one issue, and the rest are closed as duplicates of it. + +If an issue was closed and still matters in the current release, reopen it or comment with details. + +## Reporting a bug + +Search existing issues first, including closed ones. Include: + +- ResInsight version +- Operating system +- Steps to reproduce, and what you expected to happen +- Sample data or a project file, if you can share it + +## Pull requests + +- Base pull requests on the `dev` branch. \ No newline at end of file diff --git a/README.md b/README.md index 60fcc3ba52..60f7319ba6 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,28 @@ ResInsight is an open source, cross-platform 3D visualization and post-processin ## Key Features -- **Specialized Visualizations**: Tailored for efficient interpretation of reservoir simulation data with visualizations of properties, faults, and wells -- **Performance-Oriented**: Exploits multi-core CPUs and GPUs for highly responsive operation -- **Statistical Analysis**: Handles large numbers of realizations with built-in statistical calculations -- **Python Integration**: Enables powerful result manipulation and computations with two-way data exchange -- **GNU Octave Integration**: Enables powerful result manipulation and computations with two-way data exchange -- **Extensible Framework**: Supports additional data sources and visualization methods (solvers, seismic data, CSEM, geomechanics) -- **Eclipse Integration**: Supports export to Eclipse input formats for simulation cycles and parameter studies +- **3D Visualization**: Grid properties, faults, LGRs, intersections, contour maps and cell filters, using multi-core CPUs for responsive operation +- **Ensembles**: Grid and summary ensembles with statistics, contour maps and correlation analysis +- **Plotting**: Summary, well log, RFT, PLT, well allocation, flow characteristics, cross plots and histograms +- **Wells and Completions**: Well paths, perforations, fishbones, valves and fractures, with export of completion data (COMPDAT, WELSEGS, COMPSEGS) +- **Geomechanics**: Abaqus results with derived stress and strain quantities +- **Seismic**: Seismic volumes displayed together with grids and wells +- **Model Export**: Sector models and grid properties in simulator input format, surfaces as IRAP and TSurf +- **Python API**: The `rips` package drives ResInsight and exchanges data through gRPC +- **GNU Octave**: Result manipulation and computations with two-way data exchange +- **Cloud Data**: Well paths and well logs from OSDU, and grid properties from Sumo ## Supported Input Formats -- Eclipse binary output (*.GRID, *.EGRID files with corresponding *.INIT, *.XNNN, and *.UNRST files) -- Selected Eclipse input file sections -- Grid information with cell property data sets +- **Grids and results**: Eclipse/OPM Flow binary output (`*.GRID`, `*.EGRID` with `*.INIT`, `*.UNRST`, `*.XNNN`), Eclipse input (`*.GRDECL`), ROFF (`*.roff`, `*.roffasc`) +- **Summary data**: Eclipse/OPM Flow summary (`*.SMSPEC`, `*.ESMRY`), observed data (`*.RSM`, `*.txt`, `*.csv`), Reveal and StimPlan summary (`*.csv`) +- **Well paths**: `*.dev`, `*.asc`, `*.ascii`, `*.rmswell`, `*.w`, `*.json` +- **Well logs**: LAS (`*.las`), CSV +- **Completions**: Perforation intervals (`*.ev`), valve templates, StimPlan and thermal fracture templates (`*.xml`, `*.csv`) +- **Surfaces**: IRAP (`*.irap`, `*.gri`), GOCAD TSurf (`*.ts`), VTK (`*.vtu`, `*.pvd`), point sets (`*.ptl`, `*.xyz`, `*.dat`) +- **Geomechanics**: Abaqus (`*.odb`, `*.inp`), VTK (`*.pvd`) +- **Seismic**: ZGY (`*.zgy`), OpenVDS (`*.vds`), SEG-Y (`*.sgy`, `*.segy`) +- **Other**: Formation names (`*.lyr`), polygons (`*.pol`, `*.csv`, `*.dat`), VFP tables (`*.vfp`, `*.ecl`), well measurements, pressure-depth data ## Technology Stack @@ -32,6 +41,7 @@ ResInsight is cross-platform with automated testing on: - Red Hat Enterprise Linux (RHEL) - Ubuntu - Windows 11 +- macOS (Apple Silicon and Intel) ## Documentation @@ -44,12 +54,13 @@ ResInsight is cross-platform with automated testing on: ### Source Code ``` -git clone git://github.com/OPM/ResInsight.git +git clone https://github.com/OPM/ResInsight.git ``` ### Minimum Requirements - gcc 13 - clang 19 -- CMake 3.15 +- AppleClang 17 (Xcode 16) +- CMake 3.26 - MSVC 2022 17.4 These requirements are defined by used features in c++23 like `std::stacktrace` and `std::expected`. @@ -58,9 +69,10 @@ These requirements are defined by used features in c++23 like `std::stacktrace` Most dependencies are managed using vcpkg as defined in [vcpkg.json](https://github.com/OPM/ResInsight/blob/dev/vcpkg.json) ### Contributing -Contributions are welcome! Please: +Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for the issue policy and pull request guidelines. + - Use the `dev` branch for contributions and pull requests -- Note that the `master` branch is kept stable and updated only for releases +- Releases are defined by tags - See [Spell Checking Guide](docs/spell-checking.md) for information on automated spell checking ### Building diff --git a/docs/agents/core.md b/docs/agents/core.md index 9fe9d854d8..5273e7dfbd 100644 --- a/docs/agents/core.md +++ b/docs/agents/core.md @@ -53,7 +53,7 @@ ResInsight includes Python integration via gRPC when `RESINSIGHT_ENABLE_GRPC=ON` ## Development Notes - **Version**: Current version defined in `ResInsightVersion.cmake` (2025.04.4-dev) -- **Git Workflow**: Main development on `dev` branch, `master` branch for stable releases +- **Git Workflow**: Main development on `dev` branch, releases are defined by tags (e.g. `v2026.09.1`) - **Dependencies**: Extensive third-party integration via git submodules in `ThirdParty/` - **Build Output**: Goes to `CMAKE_RUNTIME_OUTPUT_DIRECTORY` (CMAKE_CURRENT_BINARY_DIR) - **Qt Integration**: Uses Qt's resource system (.qrc files) for UI resources diff --git a/scripts/tools/close_stale_issues.py b/scripts/tools/close_stale_issues.py new file mode 100644 index 0000000000..2b898cde6d --- /dev/null +++ b/scripts/tools/close_stale_issues.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +""" +Close open GitHub issues with no activity for a given number of years, +following the issue policy in CONTRIBUTING.md. Requires an authenticated gh CLI. + +Lists the candidates by default. Pass --apply to label and close them. + +Usage: + python close_stale_issues.py [--years 3] [--keep-label KeepOpen] [--apply] +""" + +import argparse +import json +import subprocess +import time +from datetime import datetime, timedelta, timezone + +STALE_LABEL = "ClosedStale" + +COMMENT = ( + "Closing as part of a backlog cleanup: no activity for {years}+ years. " + "See the issue policy in " + "[CONTRIBUTING.md](https://github.com/{repo}/blob/dev/CONTRIBUTING.md).\n\n" + "If this is still relevant in the current release, please reopen with details." +) + + +def gh(*args: str) -> str: + return subprocess.run( + ["gh", *args], check=True, capture_output=True, text=True, encoding="utf-8" + ).stdout + + +def find_stale_issues(repo: str, cutoff: datetime, keep_labels: set) -> list: + # Filter locally, as gh issue list --search is capped at 1000 results + issues = json.loads( + gh( + "issue", + "list", + "-R", + repo, + "--state", + "open", + "--limit", + "10000", + "--json", + "number,title,updatedAt,labels", + ) + ) + + stale = [] + for issue in issues: + updated = datetime.fromisoformat(issue["updatedAt"].replace("Z", "+00:00")) + labels = {label["name"] for label in issue["labels"]} + if updated < cutoff and not labels & keep_labels: + stale.append(issue) + + return sorted(stale, key=lambda issue: issue["updatedAt"]) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip().splitlines()[0]) + parser.add_argument("--repo", default="OPM/ResInsight") + parser.add_argument( + "--years", type=float, default=3, help="Inactivity threshold (default: 3)" + ) + parser.add_argument( + "--keep-label", + action="append", + default=None, + help="Never close issues with this label; can be repeated (default: KeepOpen)", + ) + parser.add_argument( + "--apply", action="store_true", help="Label and close the issues" + ) + args = parser.parse_args() + + keep_labels = set(args.keep_label or ["KeepOpen"]) + cutoff = datetime.now(timezone.utc) - timedelta(days=365.25 * args.years) + stale = find_stale_issues(args.repo, cutoff, keep_labels) + + for issue in stale: + print(f"#{issue['number']:<6} {issue['updatedAt'][:10]} {issue['title']}") + print(f"\n{len(stale)} issues not updated since {cutoff:%Y-%m-%d}") + + if not args.apply or not stale: + if stale: + print("Dry run; pass --apply to close them.") + return + + gh( + "label", + "create", + STALE_LABEL, + "-R", + args.repo, + "--force", + "--description", + "Closed by backlog cleanup due to inactivity", + ) + + comment = COMMENT.format(years=f"{args.years:g}", repo=args.repo) + for issue in stale: + number = str(issue["number"]) + gh("issue", "edit", number, "-R", args.repo, "--add-label", STALE_LABEL) + gh( + "issue", + "close", + number, + "-R", + args.repo, + "--reason", + "not planned", + "--comment", + comment, + ) + print(f"Closed #{number}") + # Stay below GitHub's secondary rate limit for content creation + time.sleep(1) + + +if __name__ == "__main__": + main()