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
21 changes: 21 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Docs

on:
push:
branches: [main]

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e ".[docs]"
# --strict so a broken link or a page missing from the nav fails the build
# rather than shipping quietly.
- run: mkdocs gh-deploy --force --strict
40 changes: 40 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install build twine
- run: python -m build
- run: twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
# Trusted Publishing: PyPI verifies this workflow's OIDC identity, so no API
# token exists anywhere and nothing has to be rotated or handed over. The
# `release` environment is what PyPI's publisher config is pinned to.
environment: release
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ is `0.x` the public API may change with a minor bump, always with an entry here.

## [Unreleased]

## [0.1.0] - 2026-09-18

First public release. The GARG-AML scoring core, extracted from the research
repository that accompanies the paper, with its behaviour pinned to the
implementation that produced the published results.

### Added

- Project skeleton: packaging, lint/type/test configuration, CI and docs scaffold.
Expand Down Expand Up @@ -67,3 +73,6 @@ is `0.x` the public API may change with a minor bump, always with an entry here.

- The original's bare `except:` around the neighbour statistics is written as the
explicit empty check it always was. Same result, verified by both test layers.

[Unreleased]: https://github.com/VerbekeLab/garg-aml/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/VerbekeLab/garg-aml/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ authors:
affiliation: KU Leuven
repository-code: "https://github.com/VerbekeLab/garg-aml"
license: MIT
version: 0.1.0.dev0
version: 0.1.0
preferred-citation:
type: article
title: >-
Expand Down
30 changes: 28 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,38 @@ badge.
Publishing uses **PyPI Trusted Publishing** from GitHub Actions. There is no API
token anywhere, so nothing has to be rotated or handed over.

### One-time setup (already done for this project)

On PyPI, under the project's *Publishing* settings, a trusted publisher is
registered with:

| Field | Value |
|---|---|
| Owner | `VerbekeLab` |
| Repository | `garg-aml` |
| Workflow | `release.yml` |
| Environment | `release` |

All four must match or PyPI rejects the upload. The `release` environment also
exists in the repository's GitHub settings; it is the natural place to add a
required reviewer if you ever want releases gated.

Before the very first upload of a *new* project name, register it as a *pending*
publisher on PyPI — the project does not exist yet, so there is nothing to
configure it against otherwise.

### Each release

1. Update `CHANGELOG.md`: move `[Unreleased]` entries under the new version.
2. Bump `__version__` in `src/garg_aml/__init__.py` and `version:` in
`CITATION.cff`.
3. Commit, then tag: `git tag v0.1.0 && git push origin main --tags`.
4. The `release` workflow builds and publishes; Zenodo mints a DOI from the
GitHub release.
4. The `release` workflow builds, runs `twine check`, and publishes on the tag.
5. Create a GitHub release from the tag; Zenodo mints a DOI from it.

Dry-run first if anything about the packaging changed: `python -m build` then
`twine check dist/*`, and install the built wheel into an empty virtualenv to
confirm it works with nothing else present.

### If you cannot publish to PyPI

Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,31 @@ are empty and whose off-diagonal parts are dense. GARG-AML scores every account
by exactly that contrast — one number in [-1, 1], computed from local structure
alone, with no training and no labels.

> **Status: pre-release.** The scoring core is being extracted from the
> [research repository](https://github.com/B-Deprez/GARG-AML). The public API
> lands in 0.1.0.
[![PyPI](https://img.shields.io/pypi/v/garg-aml.svg)](https://pypi.org/project/garg-aml/)
[![Python](https://img.shields.io/pypi/pyversions/garg-aml.svg)](https://pypi.org/project/garg-aml/)
[![License: MIT](https://img.shields.io/badge/License-MIT-orange.svg)](LICENSE)

## Install

```bash
pip install garg-aml
```

## Use

```python
import garg_aml as ga

graph, labels = ga.smurfing_graph(n_nodes=100, n_patterns=2, seed=1)
scores = ga.score(graph)["GARGAML"]
scores.sort_values(ascending=False, kind="stable").head(10)
```

Eight of those ten accounts are in an injected pattern, out of 13 among 109 —
with no training, no labels and no tuning.

Full documentation: <https://verbekelab.github.io/garg-aml/>

## Citation

If you use this package, please cite the paper:
Expand Down
2 changes: 1 addition & 1 deletion src/garg_aml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from .scores import score_from_measures, scores_from_measures
from .synthetic import smurfing_graph

__version__ = "0.1.0.dev0"
__version__ = "0.1.0"

# A library attaches no handlers of its own; the application decides.
logging.getLogger(__name__).addHandler(logging.NullHandler())
Expand Down
214 changes: 0 additions & 214 deletions tests/test_equivalence.py

This file was deleted.

Loading
Loading