Skip to main content

Issues-FS

Project description

Issues-FS

PyPI version Python 3.12+ License: MIT

A file-system based issue tracking library with hierarchical structure and pluggable storage backends.

Issues-FS stores issues as JSON files in a folder structure, enabling version control with Git, offline access, and flexible deployment options. It provides the core data layer for building issue tracking applications.

Key Features

  • File-Based Storage — Issues stored as issue.json files, easily version-controlled with Git
  • Hierarchical Structure — Nest issues inside other issues via issues/ subfolders
  • Pluggable Backends — Works with memory, local disk, S3, SQLite, or ZIP via Memory-FS
  • MGraph-DB Integration — Efficient graph queries for visualization and traversal
  • Type-Safe — Built with osbot-utils Type_Safe patterns for runtime validation
  • Recursive Discovery — Find issues at any depth in the hierarchy

Installation

pip install issues-fs

Quick Start

from memory_fs.Memory_FS                          import Memory_FS
from memory_fs.storage_fs.Storage_FS__Memory      import Storage_FS__Memory
from memory_fs.storage_fs.Storage_FS__Local_Disk  import Storage_FS__Local_Disk
from issues_fs.graph.Graph__Repository            import Graph__Repository
from issues_fs.graph.Node__Service                import Node__Service
from issues_fs.graph.Type__Service                import Type__Service
from issues_fs.storage.Path__Handler__Graph_Node  import Path__Handler__Graph_Node

# Setup with in-memory storage (great for testing)
storage_fs   = Storage_FS__Memory()
memory_fs    = Memory_FS(storage_fs=storage_fs)
path_handler = Path__Handler__Graph_Node(base_path='')
repository   = Graph__Repository(memory_fs=memory_fs, path_handler=path_handler)

# Or use local disk storage
storage_fs   = Storage_FS__Local_Disk(root_path='.issues')
memory_fs    = Memory_FS(storage_fs=storage_fs)
path_handler = Path__Handler__Graph_Node(base_path='')
repository   = Graph__Repository(memory_fs=memory_fs, path_handler=path_handler)

# Initialize services
type_service = Type__Service(repository=repository)
node_service = Node__Service(repository=repository)

# Initialize default types
type_service.initialize_default_types()

Core Operations

Create an Issue

from issues_fs.schemas.Schema__Node__Create__Request import Schema__Node__Create__Request

request = Schema__Node__Create__Request(
    node_type   = 'task',
    title       = 'Implement user authentication',
    description = 'Add OAuth2 support',
    status      = 'backlog',
    tags        = ['security', 'backend']
)

response = node_service.create_node(request)

if response.success:
    print(f"Created: {response.node.label}")  # Task-1

Load an Issue

from issues_fs.schemas.Safe_Str__Graph_Types import Safe_Str__Node_Type, Safe_Str__Node_Label

node = repository.node_load(
    node_type = Safe_Str__Node_Type('task'),
    label     = Safe_Str__Node_Label('Task-1')
)

print(node.title)   # "Implement user authentication"
print(node.status)  # "backlog"

Find All Issues (Recursive)

# Find all issues, including nested ones
all_nodes = repository.nodes_list_all()

for info in all_nodes:
    print(f"{info.label} ({info.node_type}) at {info.path}")

# Filter by root path
project_nodes = repository.nodes_list_all(
    root_path=Safe_Str__File__Path('data/project/Project-1')
)

Load by Path

# Load issue by folder path
node = repository.node_load_by_path(
    Safe_Str__File__Path('data/project/Project-1/issues/Version-1/issues/Task-1')
)

# Find path for a label
path = repository.node_find_path_by_label(Safe_Str__Node_Label('Task-1'))

Update an Issue

from issues_fs.schemas.Schema__Node__Update__Request import Schema__Node__Update__Request

request = Schema__Node__Update__Request(
    status = 'in-progress',
    tags   = ['security', 'backend', 'urgent']
)

response = node_service.update_node(
    node_type = Safe_Str__Node_Type('task'),
    label     = Safe_Str__Node_Label('Task-1'),
    request   = request
)

Delete an Issue

response = node_service.delete_node(
    node_type = Safe_Str__Node_Type('task'),
    label     = Safe_Str__Node_Label('Task-1')
)

MGraph-DB Integration

For efficient graph queries and visualization:

from issues_fs.mgraph.MGraph__Issues__Sync__Service import MGraph__Issues__Sync__Service
from issues_fs.mgraph.MGraph__Issues__Domain        import MGraph__Issues__Domain

# Create sync service
sync_service = MGraph__Issues__Sync__Service(
    repository   = repository,
    path_handler = path_handler
)

# Lazy load graph (syncs from filesystem)
graph = sync_service.ensure_loaded()

# Query by label
node = graph.get_node_by_label('Task-1')

# Get children
children = graph.get_children(node.node_id)

# Get ancestors (path to root)
ancestors = graph.get_ancestors(node.node_id)

# Force resync after external changes
sync_service.full_sync()

Storage Structure

Issues-FS uses a simple, human-readable folder structure:

.issues/
├── config/
│   ├── node-types.json          # Issue type definitions
│   └── link-types.json          # Link type definitions
├── data/
│   └── {type}/
│       └── {Label}/
│           ├── issue.json       # Issue data
│           └── issues/          # Child issues
│               └── {Child-Label}/
│                   └── issue.json
└── indexes/
    └── issues.mgraph.json       # MGraph-DB cache

Example Structure

.issues/
├── data/
│   └── project/
│       └── Project-1/
│           ├── issue.json
│           └── issues/
│               ├── Version-1/
│               │   ├── issue.json
│               │   └── issues/
│               │       ├── Task-1/
│               │       │   └── issue.json
│               │       └── Bug-2/
│               │           └── issue.json
│               └── Version-2/
│                   └── issue.json

Issue JSON Schema

{
  "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "node_type": "task",
  "node_index": 1,
  "label": "Task-1",
  "title": "Implement user authentication",
  "description": "Add OAuth2 support for user login",
  "status": "in-progress",
  "created_at": 1706745600000,
  "updated_at": 1706832000000,
  "tags": ["security", "backend"],
  "links": [
    {
      "verb": "blocks",
      "target_label": "Task-2"
    }
  ],
  "properties": {
    "priority": "high",
    "estimate": "3d"
  }
}

Node Types

Default node types are created on initialization:

type_service.initialize_default_types()
{
  "types": [
    {"name": "git-repo", "display_name": "Git Repo", "color": "#6e5494", "default_status": "active"},
    {"name": "project", "display_name": "Project", "color": "#4A90D9", "default_status": "active"},
    {"name": "version", "display_name": "Version", "color": "#7B68EE", "default_status": "planned"},
    {"name": "task", "display_name": "Task", "color": "#50C878", "default_status": "backlog"},
    {"name": "bug", "display_name": "Bug", "color": "#FF6B6B", "default_status": "open"},
    {"name": "feature", "display_name": "Feature", "color": "#FFD700", "default_status": "backlog"},
    {"name": "user-story", "display_name": "User Story", "color": "#20B2AA", "default_status": "backlog"}
  ]
}

Custom Types

from issues_fs.schemas.Schema__Node__Type import Schema__Node__Type

custom_type = Schema__Node__Type(
    name           = 'epic',
    display_name   = 'Epic',
    color          = '#9B59B6',
    icon           = 'rocket',
    statuses       = ['draft', 'active', 'completed'],
    default_status = 'draft'
)

type_service.create_node_type(custom_type)

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Services                              │
│       Node__Service  │  Type__Service  │  Link__Service      │
└────────────────────────────┬────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────┐
│                    Graph__Repository                         │
│            (Data access layer with Type_Safe)                │
└────────────────────────────┬────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────┐
│                        Memory-FS                             │
│     Storage_FS__Memory │ Storage_FS__Local_Disk │ S3 │ ...  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    MGraph Integration                        │
│  MGraph__Issues__Sync__Service  →  MGraph__Issues__Domain   │
└─────────────────────────────────────────────────────────────┘

Key Components

Component Purpose
Graph__Repository Data access layer for nodes, types, indexes
Node__Service Business logic for CRUD operations
Type__Service Manage node and link type definitions
Link__Service Manage links between issues
MGraph__Issues__Sync__Service Syncs filesystem to MGraph-DB
MGraph__Issues__Domain Graph operations and indexes
Path__Handler__Graph_Node Generate filesystem paths

Development

Setup

git clone https://github.com/owasp-sbot/Issues-FS.git
cd Issues-FS
pip install -e ".[dev]"

Run Tests

pytest

Code Style

This project follows the OSBot Python Formatting Guide:

  • Type_Safe inheritance on all classes
  • Safe_* primitives for type validation
  • @type_safe decorator on public methods
  • Aligned imports/parameters at column 70-80
  • Explicit is True / is False for booleans

Dependencies

Use Cases

  • Git-native issue tracking — Store issues alongside code, branch/merge with PRs
  • Offline-first workflows — Full functionality without network access
  • Custom integrations — Simple JSON format for easy tooling
  • Hierarchical project management — Organize epics → features → tasks → subtasks
  • Embedded issue tracking — Add issue tracking to any Python application

License

MIT License — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

issues_fs-0.3.0.tar.gz (8.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

issues_fs-0.3.0-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file issues_fs-0.3.0.tar.gz.

File metadata

  • Download URL: issues_fs-0.3.0.tar.gz
  • Upload date:
  • Size: 8.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for issues_fs-0.3.0.tar.gz
Algorithm Hash digest
SHA256 c2eb750e8282171b16995789b0fa100e26ce31fa1cdce05afbd0c0e5079a06f0
MD5 82657ae647f9a22fe98efe250e8f7a29
BLAKE2b-256 08fa0a58c3cde07a410da6e961536f0ded22696bbd31b763aa32851ff2e2a73c

See more details on using hashes here.

File details

Details for the file issues_fs-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: issues_fs-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 9.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for issues_fs-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3b394f5516ac81f1fd5b3190e763c9b9da6f93be492313407e0851968fba5ff
MD5 877c8b9bb1d75b0124848a3dc656133c
BLAKE2b-256 d10b468dd01b98f954cc12e386e9c8d268e9cf7ffe9398f039e759b9b24a730c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page