diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..450eb91 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig: https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,sh,bash}] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{yml,yaml,json,toml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d86eaa2 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# Copy this file to .env and fill in values as needed. +# .env is ignored by git (see .gitignore). + +# Example: set a custom Python interpreter +# PYTHON=python3 + +# Example: disable pre-commit hooks in CI environments +# PRE_COMMIT_ALLOW_NO_CONFIG=1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2f2a2ef --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + +jobs: + lint: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + if: ${{ hashFiles('.pre-commit-config.yaml') != '' }} + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install pre-commit + if: ${{ hashFiles('.pre-commit-config.yaml') != '' }} + run: pip install pre-commit + + - name: Install shellcheck + run: sudo apt-get install -y shellcheck + + - name: Make scripts executable + run: chmod +x scripts/* bin/* 2>/dev/null || true + + - name: Run lint + run: make lint diff --git a/.gitignore b/.gitignore index 82fd733..8d461f3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__/ *.egg-info/ dist/ build/ +.env diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4ceddc4 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +.PHONY: bootstrap doctor lint format test ci + +# Run all first-time setup steps +bootstrap: + @scripts/bootstrap + +# Print environment diagnostic info +doctor: + @scripts/doctor + +# Run linters on shell scripts (and pre-commit if configured) +lint: + @scripts/lint + +# Auto-format shell scripts (and pre-commit if configured) +format: + @scripts/format + +# Run tests (placeholder — add test commands here as the repo grows) +test: + @echo "No automated tests configured yet." + @echo "Add test commands to this target as needed." + +# Run lint — suitable as a CI check +ci: lint diff --git a/README.md b/README.md index 379e4b9..d3cf6e1 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,25 @@ A collection of miscellaneous scripts, tools, and experiments. Topics covered include cryptography, Bitcoin/blockchain tooling, security research, networking utilities, system administration, mathematics, and general utilities. +## Quickstart + +```bash +# First-time setup (checks tools, makes scripts executable, creates .env) +make bootstrap + +# Check your environment +make doctor + +# Run linters +make lint +``` + ## Directory Layout ``` misc/ +├── bin/ User-facing commands (added to PATH) +├── scripts/ Internal helper scripts (bootstrap, doctor, lint, format) ├── crypto/ Cryptographic algorithms, primitives, and tests │ (AES, RSA, ECDSA, DH, TOTP, PRNG, hashes, encoding) ├── bitcoin/ Bitcoin and blockchain tools @@ -25,7 +40,27 @@ misc/ └── examples/ Example images used in demonstrations ``` -## Usage +### Conventions + +| Directory | Purpose | +|------------|---------| +| `bin/` | User-facing commands intended to be on `PATH` | +| `scripts/` | Internal repo helpers (bootstrap, doctor, lint, format) | + +## Tooling + +All common tasks are driven by the `Makefile`. The scripts live in `scripts/`. + +| Target | Description | +|-------------------|-------------| +| `make bootstrap` | Verify required tools, make scripts executable, create `.env` | +| `make doctor` | Print versions and warn about missing tools | +| `make lint` | Run shellcheck / shfmt (or pre-commit if configured) | +| `make format` | Auto-format shell scripts with shfmt (or pre-commit) | +| `make test` | Run tests (placeholder — add commands as repo grows) | +| `make ci` | Alias for `make lint` — used in CI | + +## Running Scripts Most scripts are standalone and can be run directly: diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000..e2469af --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# scripts/bootstrap — first-time setup for this repository. +# +# Usage: scripts/bootstrap +# or: make bootstrap +# +# What it does: +# 1. Verifies required tools are present (git). +# 2. Ensures all scripts in scripts/ and bin/ are executable. +# 3. Creates .env from .env.example if .env does not already exist. +# 4. Optionally installs / checks pre-commit if Python is available. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" + +# ── helpers ────────────────────────────────────────────────────────────────── +info() { printf '\033[1;34m==> %s\033[0m\n' "$*"; } +ok() { printf '\033[1;32m ✓ %s\033[0m\n' "$*"; } +warn() { printf '\033[1;33mwarn: %s\033[0m\n' "$*"; } +die() { printf '\033[1;31merror: %s\033[0m\n' "$*" >&2; exit 1; } + +# ── 1. required tools ───────────────────────────────────────────────────────── +info "Checking required tools…" +command -v git >/dev/null 2>&1 || die "git is required but not found. Install git and re-run." +ok "git found: $(git --version)" + +# ── 2. make scripts executable ─────────────────────────────────────────────── +info "Ensuring scripts are executable…" +for dir in scripts bin; do + if [ -d "$dir" ]; then + find "$dir" -type f ! -name "*.*" -exec chmod +x {} \; + ok "$dir/ scripts marked executable" + fi +done + +# ── 3. create .env if missing ───────────────────────────────────────────────── +if [ -f ".env.example" ] && [ ! -f ".env" ]; then + info "Creating .env from .env.example…" + cp ".env.example" ".env" + ok ".env created (review and customise as needed)" +elif [ -f ".env" ]; then + ok ".env already exists" +fi + +# ── 4. pre-commit (optional) ────────────────────────────────────────────────── +if command -v python3 >/dev/null 2>&1; then + info "Python found — checking pre-commit…" + if ! command -v pre-commit >/dev/null 2>&1; then + warn "pre-commit not found. Install it with: pip install pre-commit" + warn "Then run: pre-commit install" + else + ok "pre-commit found: $(pre-commit --version)" + if [ -f ".pre-commit-config.yaml" ]; then + info "Installing pre-commit hooks…" + pre-commit install + ok "pre-commit hooks installed" + else + warn "No .pre-commit-config.yaml found — skipping hook install." + fi + fi +else + warn "python3 not found — skipping pre-commit setup." + warn "Install Python 3 and run: pip install pre-commit && pre-commit install" +fi + +info "Bootstrap complete." diff --git a/scripts/doctor b/scripts/doctor new file mode 100755 index 0000000..1f7e7db --- /dev/null +++ b/scripts/doctor @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# scripts/doctor — print diagnostic info and check for common tools. +# +# Usage: scripts/doctor +# or: make doctor +# +# Exit codes: +# 0 all critical tools present +# 1 one or more critical tools missing + +set -euo pipefail + +# ── helpers ────────────────────────────────────────────────────────────────── +ok() { printf '\033[1;32m ✓ %-12s %s\033[0m\n' "$1" "$2"; } +warn() { printf '\033[1;33m ✗ %-12s %s\033[0m\n' "$1" "$2"; } +hdr() { printf '\n\033[1m%s\033[0m\n' "$*"; } + +MISSING_CRITICAL=0 + +check() { + local name="$1" + local critical="${2:-false}" + if command -v "$name" >/dev/null 2>&1; then + local ver + # Some tools use 'version' sub-command rather than --version + case "$name" in + go) ver=$(go version 2>&1 | head -1) || true ;; + *) ver=$("$name" --version 2>&1 | head -1) || true ;; + esac + ok "$name" "$ver" + else + if [ "$critical" = "true" ]; then + warn "$name" "NOT FOUND (required)" + MISSING_CRITICAL=1 + else + warn "$name" "not found (optional)" + fi + fi +} + +# ── environment info ────────────────────────────────────────────────────────── +hdr "Environment" +printf ' OS: %s\n' "$(uname -sr)" +printf ' Host: %s\n' "$(uname -n)" +printf ' User: %s\n' "${USER:-unknown}" +printf ' PWD: %s\n' "$(pwd)" + +# ── critical tools ──────────────────────────────────────────────────────────── +hdr "Critical tools" +check git true +check bash true + +# ── optional tools ──────────────────────────────────────────────────────────── +hdr "Optional tools" +check python3 +check node +check go +check pre-commit +check shellcheck +check shfmt + +printf '\n' +if [ "$MISSING_CRITICAL" -ne 0 ]; then + printf '\033[1;31mDoctor found missing critical tools. Please install them.\033[0m\n' >&2 + exit 1 +fi +printf '\033[1;32mAll critical tools present.\033[0m\n' diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000..41327f3 --- /dev/null +++ b/scripts/format @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# scripts/format — auto-format shell scripts (and pre-commit if configured). +# +# Usage: scripts/format +# or: make format +# +# Priority: +# 1. pre-commit run --all-files (if pre-commit and config are present) +# 2. shfmt -w (write) on scripts/* and bin/* +# If none of the above tools are present, the script exits 0 with a notice. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" || exit 1 + +# ── helpers ────────────────────────────────────────────────────────────────── +info() { printf '\033[1;34m==> %s\033[0m\n' "$*"; } +ok() { printf '\033[1;32m ✓ %s\033[0m\n' "$*"; } +warn() { printf '\033[1;33mwarn: %s\033[0m\n' "$*"; } + +# ── collect shell scripts ───────────────────────────────────────────────────── +shell_scripts=() +for dir in scripts bin; do + if [ -d "$dir" ]; then + while IFS= read -r -d '' f; do + shell_scripts+=("$f") + done < <(find "$dir" -type f ! -name "*.*" -print0 2>/dev/null) + fi +done + +# ── 1. pre-commit ───────────────────────────────────────────────────────────── +if command -v pre-commit >/dev/null 2>&1 && [ -f ".pre-commit-config.yaml" ]; then + info "Running pre-commit run --all-files…" + if pre-commit run --all-files; then + ok "pre-commit passed" + else + exit 1 + fi + exit 0 +fi + +# ── 2. shfmt (write mode) ───────────────────────────────────────────────────── +if command -v shfmt >/dev/null 2>&1; then + if [ "${#shell_scripts[@]}" -gt 0 ]; then + info "Running shfmt -w on shell scripts…" + shfmt -w "${shell_scripts[@]}" + ok "shfmt: formatting applied" + else + warn "No shell scripts found to format with shfmt." + fi +else + warn "shfmt not found — skipping. See https://github.com/mvdan/sh for install instructions." +fi diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..884ec4a --- /dev/null +++ b/scripts/lint @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# scripts/lint — run linters on shell scripts (and pre-commit if configured). +# +# Usage: scripts/lint +# or: make lint +# +# Priority: +# 1. pre-commit run --all-files (if pre-commit and config are present) +# 2. shellcheck on scripts/* and bin/* +# 3. shfmt -d (diff / dry-run) on scripts/* and bin/* +# If none of the above tools are present, the script exits 0 with a notice. + +set -uo pipefail +# Note: -e is intentionally omitted so all lint tools run and report before exiting. + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" || exit 1 + +# ── helpers ────────────────────────────────────────────────────────────────── +info() { printf '\033[1;34m==> %s\033[0m\n' "$*"; } +ok() { printf '\033[1;32m ✓ %s\033[0m\n' "$*"; } +warn() { printf '\033[1;33mwarn: %s\033[0m\n' "$*"; } + +FAIL=0 + +# ── collect shell scripts ───────────────────────────────────────────────────── +shell_scripts=() +for dir in scripts bin; do + if [ -d "$dir" ]; then + while IFS= read -r -d '' f; do + shell_scripts+=("$f") + done < <(find "$dir" -type f ! -name "*.*" -print0 2>/dev/null) + fi +done + +# ── 1. pre-commit ───────────────────────────────────────────────────────────── +if command -v pre-commit >/dev/null 2>&1 && [ -f ".pre-commit-config.yaml" ]; then + info "Running pre-commit run --all-files…" + if pre-commit run --all-files; then + ok "pre-commit passed" + else + FAIL=1 + fi + exit "$FAIL" +fi + +# ── 2. shellcheck ───────────────────────────────────────────────────────────── +if command -v shellcheck >/dev/null 2>&1; then + if [ "${#shell_scripts[@]}" -gt 0 ]; then + info "Running shellcheck on shell scripts…" + if shellcheck "${shell_scripts[@]}"; then + ok "shellcheck passed" + else + FAIL=1 + fi + else + warn "No shell scripts found to check with shellcheck." + fi +else + warn "shellcheck not found — skipping. Install with: sudo apt-get install shellcheck" +fi + +# ── 3. shfmt (check mode) ───────────────────────────────────────────────────── +if command -v shfmt >/dev/null 2>&1; then + if [ "${#shell_scripts[@]}" -gt 0 ]; then + info "Running shfmt -d (diff check) on shell scripts…" + if shfmt -d "${shell_scripts[@]}"; then + ok "shfmt: no formatting issues" + else + FAIL=1 + warn "shfmt found formatting issues. Run 'make format' to fix them." + fi + else + warn "No shell scripts found to check with shfmt." + fi +else + warn "shfmt not found — skipping. See https://github.com/mvdan/sh for install instructions." +fi + +if [ "$FAIL" -eq 0 ] && ! command -v shellcheck >/dev/null 2>&1 && ! command -v shfmt >/dev/null 2>&1; then + warn "No lint tools found (pre-commit, shellcheck, shfmt). Install at least one to enable linting." +fi + +exit "$FAIL"