generated from nhcarrigan/template
ae081cb54c
CI / dependency-pin-check-typescript (pull_request) Successful in 5s
CI / dependency-pin-check-python (pull_request) Successful in 4s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 57s
CI / python (pull_request) Successful in 9m33s
CI / typescript (pull_request) Successful in 9m41s
Add Getting Started sections and correct usage commands to all category READMEs (TypeScript, Python, Bash). Update top-level README.md and CLAUDE.md to reflect the Bash language, correct project structure, and accurate make run instructions. Remove completed DOCS_TODO.md.
10 KiB
10 KiB
Ephemere Project Guidelines
This document contains project-specific instructions for working with the Ephemere codebase.
Project Overview
Ephemere is a collection of ephemeral scripts for various tasks, written in TypeScript, Python, and Bash. It contains utilities for:
- S3 operations (upload, bulk upload, delete, content type correction)
- Discord bot utilities
- Discourse forum management
- Gitea/GitHub operations
- Security analysis tools
- Music-related scripts
- Cohort programme management (Python + Bash)
- YubiKey SSH key and permission utilities (Bash)
Project Structure
ephemere/
├── typescript/ # TypeScript scripts
│ └── src/
│ ├── s3/ # S3 operations (upload, delete, bulk operations)
│ ├── discord/ # Discord bot and utilities
│ ├── discourse/ # Discourse forum management
│ ├── gitea/ # Gitea API interactions
│ ├── github/ # GitHub API interactions
│ ├── security/ # Security analysis tools
│ ├── music/ # Music-related utilities
│ └── utils/ # Shared utilities
├── python/
│ └── cohort/ # Cohort programme management scripts
├── bash/
│ ├── cohort/ # GitHub team management for cohorts
│ └── yubikey/ # YubiKey SSH key and permission utilities
├── data/ # Input/output data files (gitignored)
└── prod.env # 1Password vault references (safe to commit)
Development Standards
TypeScript Scripts
- All TypeScript scripts must follow Naomi's Node.js project standards
- Use
@nhcarrigan/typescript-configand@nhcarrigan/eslint-config - Run scripts using the interactive runner:
make run(select TypeScript → category → script) - Interactive scripts should use
@inquirer/promptsfor user input
Python Scripts
- Use
uvfor package management - Linting and formatting with
ruff - Scripts live in subdirectories of
python/(e.g.python/cohort/) - Run scripts using the interactive runner:
make run(select Python → category → script)
Bash Scripts
- Scripts live in subdirectories of
bash/(e.g.bash/cohort/,bash/yubikey/) - Run scripts using the interactive runner:
make run(select Bash → category → script) - Or run directly:
bash bash/<category>/<script>.sh - Bash scripts do not use 1Password injection (they use
ghCLI auth or system tools)
S3 Scripts Specifics
All S3 scripts in typescript/src/s3/ follow these patterns:
- Use environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,S3_ENDPOINT - Interactive prompts using
@inquirer/prompts - Progress bars using
cli-progressfor bulk operations - Proper error handling with success/error counts
- ESLint disable comments for AWS SDK naming conventions
- No CLI arguments - everything is prompted interactively
Running Scripts
Always use the interactive runner to run scripts (it handles 1Password integration for TypeScript and Python):
make run # Interactive menu to select language, category, and script
Or run directly:
# TypeScript (from project root)
cd typescript && op run --env-file=../prod.env -- pnpm tsx src/<category>/<script>.ts
# Python (from project root)
cd python && op run --env-file=../prod.env -- uv run python <category>/<script>.py
# Bash (no 1Password needed)
bash bash/<category>/<script>.sh
Script Patterns
TypeScript Script Template
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { input, confirm, select } from "@inquirer/prompts";
// Environment variable checks
const requiredVar = process.env.REQUIRED_VAR;
if (requiredVar === undefined) {
throw new Error("REQUIRED_VAR is not set");
}
// Interactive prompts
const userInput = await input({
message: "Enter your input:",
validate: (value) => {
if (!value.trim()) {
return "Input cannot be empty";
}
return true;
},
});
// Main logic here
console.log("Processing...");
// Always provide feedback
console.log("✅ Operation completed successfully!");
Python Script Template
#!/usr/bin/env python3
"""
Script description here.
@copyright NHCarrigan
@license Naomi's Public License
@author Naomi Carrigan
"""
import os
import sys
from typing import Optional
def main() -> None:
"""Main function."""
# Environment variable checks
required_var = os.getenv("REQUIRED_VAR")
if not required_var:
print("Error: REQUIRED_VAR is not set")
sys.exit(1)
# Interactive input (if needed)
user_input = input("Enter your input: ").strip()
if not user_input:
print("Error: Input cannot be empty")
sys.exit(1)
# Main logic here
print("Processing...")
# Always provide feedback
print("✅ Operation completed successfully!")
if __name__ == "__main__":
main()
Environment Variables
All secrets are managed through 1Password and referenced in prod.env:
- AWS credentials for S3 operations
- Discord bot tokens
- GitHub/Gitea API tokens
- Discourse API credentials
The prod.env file contains 1Password vault references (like op://Private/Hetzner/S3 Endpoint) and is safe to commit.
Important Notes
- No hardcoded values - All configuration should come from environment variables
- Interactive scripts - Scripts should prompt for all required input, not use CLI arguments
- Progress feedback - Long-running operations should show progress bars
- Error handling - Always track and report success/failure counts
- Consistent patterns - Follow the existing script patterns in each category
Best Practices
Error Handling
- Always validate environment variables at script start
- Provide clear error messages that help users fix the issue
- Use try-catch blocks for external API calls
- Track success/failure counts for bulk operations
- Exit with appropriate status codes (0 for success, 1 for errors)
User Experience
- Use emojis in console output for visual feedback (✅ ❌ ⚠️ 🚀 📦 etc.)
- Show progress bars for operations with multiple items
- Confirm destructive operations (require typing confirmation + yes/no)
- Provide summaries at the end of bulk operations
- Keep output concise but informative
Code Quality
- Add JSDoc comments for TypeScript functions
- Use type annotations in Python
- Follow the established linting rules (no overrides without good reason)
- Keep functions focused and single-purpose
- Extract reusable logic to utility functions
Security
- Never log sensitive information (tokens, passwords, keys)
- Validate all user inputs
- Use parameterized queries for any database operations
- Follow the principle of least privilege for API tokens
- Sanitize file paths and names
Common Tasks
Adding a new S3 script
- Create the script in
typescript/src/s3/ - Follow the pattern of existing S3 scripts (see deleteContents.ts)
- Use environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,S3_ENDPOINT - Use @inquirer/prompts for all user input (no CLI arguments)
- Include proper error handling and progress bars for bulk operations
- Add ESLint disable comments for AWS SDK naming conventions
Adding a new Discord script
- Create the script in
typescript/src/discord/ - Use environment variables:
DISCORD_TOKEN,DISCORD_CLIENT_ID, etc. - Follow Discord.js best practices
- Use @inquirer/prompts for any configuration input
- Handle rate limits and API errors gracefully
Adding a new GitHub/Gitea script
- Create the script in
typescript/src/github/ortypescript/src/gitea/ - Use environment variables:
GITHUB_TOKENorGITEA_TOKEN - Use the Octokit library for GitHub operations
- Include proper pagination for list operations
- Handle API rate limits appropriately
Adding a new Discourse script
- Create the script in
typescript/src/discourse/ - Use environment variables:
DISCOURSE_URL,DISCOURSE_API_KEY,DISCOURSE_API_USERNAME - Follow the Discourse API documentation
- Use @inquirer/prompts for interactive inputs
- Handle API errors and rate limits
Adding a new Security script
- Create the script in
typescript/src/security/ - Use environment variables for any API tokens (e.g.,
DOJO_TOKENfor DefectDojo) - Follow security best practices - never log sensitive data
- Include proper validation for all inputs
- Consider adding audit logs for security operations
Adding a new Python script
- Create the script in the appropriate subdirectory of
python/(e.g.python/cohort/) - Use type hints for all functions and variables
- Follow PEP 8 style guide (enforced by ruff)
- Add the script to
requirements.txtif it needs new dependencies - Use
argparsefor CLI arguments if needed (though prefer interactive) - Include docstrings for all functions and classes
Adding a new utility function
- TypeScript utilities go in
typescript/src/utils/ - Python utilities can be imported from a shared module
- Utilities should be pure functions when possible
- Include comprehensive JSDoc/docstring documentation
- Add unit tests if the utility is complex
Adding a new script category
- Create a new directory under
typescript/src/,python/, orbash/as appropriate - Follow the naming convention (lowercase, descriptive)
- Create at least one example script showing the pattern
- Create a
README.mdin the new directory documenting each script - Update this CLAUDE.md with specific guidelines for the category
- Add any new environment variables to prod.env with 1Password references
Testing
Before committing:
make lint # Run all linters
make build # Type check TypeScript
make test # Run tests (if any)
Troubleshooting
- If a script fails with "not set" errors, ensure you're running through the Makefile or with
op run - For S3 scripts, verify your 1Password vault has the correct S3 endpoint, access key, and secret key
- TypeScript import errors: ensure you use
.jsextensions in imports (even for.tsfiles)