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
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[*.{yml,yaml,json}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Azure Blob Storage credentials (never commit real values)
AZURE_STORAGE_ACCOUNT_NAME=your-storage-account-name
AZURE_STORAGE_ACCOUNT_KEY=your-storage-account-key

# File extension to totalize (e.g. .vhd, .png, or vhd)
FILE_TYPE=.vhd
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
groups:
dev-dependencies:
dependency-type: development
patterns:
- "*"

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [master, main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12", "3.13"]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools
pip install -r requirements.lock
pip install -e . --no-deps

- name: Ruff lint
run: ruff check .

- name: Ruff format check
run: ruff format --check .

- name: Run tests
run: pytest --cov=filetype_size --cov-report=term-missing
52 changes: 52 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Virtual environments
.venv/
venv/
ENV/

# Testing / coverage
.pytest_cache/
.coverage
htmlcov/
.mypy_cache/
.ruff_cache/

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Secrets / local config
.env
.env.*
!.env.example

# Logs
*.log
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Joel Shearon

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
104 changes: 102 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,104 @@
# FileTypeSizeAzureStoragePython

For helping with determining total byte size of certain file types in Azure Storage
Ex. after providing your storage account name and key, you could type in .vhd and it would return total bytes of all vhd files in your storage account
[![CI](https://github.com/hippi345/FileTypeSizeAzureStoragePython/actions/workflows/ci.yml/badge.svg)](https://github.com/hippi345/FileTypeSizeAzureStoragePython/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Calculate the **total byte size** of blobs matching a file extension across all containers in an Azure Storage account—for example, sum every `.vhd` or `.png` in the account.

The tool uses the current [Azure Storage Blob SDK for Python](https://learn.microsoft.com/en-us/python/api/azure-storage-blob/) (`azure-storage-blob` v12+). Credentials are read from the environment or interactive prompts; they are never stored in the repository.

## Features

- Lists every container and blob (name and size) in the storage account
- Totals sizes for blobs whose names end with your chosen extension (e.g. `.vhd`)
- Supports non-interactive runs via environment variables
- Offline unit tests with mocked Azure clients

## Requirements

- Python **3.12+**
- An Azure Storage account with account name and key (or use a connection string pattern via env vars above)

## Setup

```bash
git clone https://github.com/hippi345/FileTypeSizeAzureStoragePython.git
cd FileTypeSizeAzureStoragePython
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```

Copy `.env.example` to `.env` and fill in values locally (`.env` is gitignored).

## Configuration

| Variable | Description |
| -------- | ----------- |
| `AZURE_STORAGE_ACCOUNT_NAME` | Storage account name |
| `AZURE_STORAGE_ACCOUNT_KEY` | Storage account access key |
| `FILE_TYPE` | Extension to match (e.g. `.vhd` or `vhd`) |

If any variable is missing, the program prompts for it on stdin.

## Usage

**With environment variables:**

```bash
export AZURE_STORAGE_ACCOUNT_NAME=myaccount
export AZURE_STORAGE_ACCOUNT_KEY='...'
export FILE_TYPE=.vhd
python main.py
```

**Console script (after install):**

```bash
filetype-size-azure
```

**Interactive (no env vars):**

```bash
python main.py
```

Example output:

```
Welcome to the file size calculator for Azure Storage on Python
Printing out containers:
my-container:
Blob name: backups/disk.vhd
Blob size: 1073741824
Total size of .vhd files: 1073741824
```

## Running tests

Tests run fully offline with mocked Azure APIs:

```bash
ruff check .
ruff format --check .
pytest --cov=filetype_size
```

## Project structure

```
├── filetype_size/
│ ├── azure_client.py # BlobServiceClient helpers
│ ├── calculator.py # Extension matching and totals
│ └── cli.py # Prompts / env / output
├── main.py # Entry point
├── tests/
├── pyproject.toml # Package metadata and tool config
├── requirements.txt # Runtime pins (source)
└── requirements.lock # Locked versions for CI/reproducible installs
```

## License

MIT License — see [LICENSE](LICENSE) (Copyright © 2026 Joel Shearon).
24 changes: 24 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Security Policy

## Supported versions

| Version | Supported |
| ------- | --------- |
| 2.x | Yes |

## Reporting a vulnerability

If you discover a security issue, please **do not** open a public GitHub issue with sensitive details.

1. Open a private security advisory on GitHub for this repository, or contact the repository owner directly.
2. Include steps to reproduce, impact, and any suggested fix.

## Secrets and credentials

- Never commit storage account keys, connection strings, or SAS tokens to the repository.
- Configure credentials via environment variables (`AZURE_STORAGE_ACCOUNT_NAME`, `AZURE_STORAGE_ACCOUNT_KEY`) or a local `.env` file that is listed in `.gitignore`.
- Rotate any credential that may have been exposed in git history or logs.

## Dependencies

Dependency updates are managed via Dependabot. Keep `azure-storage-blob` and other packages current to receive security fixes from upstream.
3 changes: 3 additions & 0 deletions filetype_size/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Calculate blob sizes by file extension in Azure Blob Storage."""

__version__ = "2.0.0"
35 changes: 35 additions & 0 deletions filetype_size/azure_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Azure Blob Storage client helpers."""

from __future__ import annotations

from collections.abc import Iterator

from azure.storage.blob import BlobServiceClient

from filetype_size.calculator import BlobInfo


class ListedBlob:
"""Adapter exposing name and size for calculator protocols."""

def __init__(self, name: str, size: int) -> None:
self.name = name
self.size = size


def create_blob_service_client(account_name: str, account_key: str) -> BlobServiceClient:
account_url = f"https://{account_name}.blob.core.windows.net"
return BlobServiceClient(account_url=account_url, credential=account_key)


def iter_containers_with_blobs(
client: BlobServiceClient,
) -> Iterator[tuple[str, list[BlobInfo]]]:
"""List every container and its blobs (name + size)."""
for container in client.list_containers():
name = container.name
container_client = client.get_container_client(name)
blobs: list[BlobInfo] = [
ListedBlob(blob.name, blob.size) for blob in container_client.list_blobs()
]
yield name, blobs
Loading
Loading