pinner 0.0.13

Secure CI/CD workflows by pinning mutable tags to immutable SHA-1 hashes. A high-performance Rust CLI that preserves YAML formatting and comments. Supports GitHub, GitLab, Bitbucket, Forgejo, and Docker image pinning.
Documentation
# Pinner: Developer & Extension Guide

This guide describes how to compile, test, maintain, and extend the `pinner` utility.

---

## Technical Stack & Tooling

*   **Language**: Rust (2021 Edition).
*   **Concurrency**: `tokio` for async orchestration; `rayon` for data-parallel AST scanning.
*   **AST Parsing**: `tree-sitter` and `tree-sitter-yaml` for syntax tree traversal.
*   **Networking**: `reqwest` + `reqwest-middleware` + `reqwest-retry`.
*   **Unit Mocking**: `mockall` (generates mock implementations of traits).
*   **HTTP Interception**: `mockito` (starts local HTTP servers to intercept backend API calls).
*   **Hooks**: `cargo-husky` automatically configures local Git hooks during build.

---

## Workflow Commands

```bash
# Build the binary
cargo build

# Run the CLI locally
cargo run -- --workflows .github/workflows pin

# Run all unit and integration tests
cargo test

# Run linter checks
cargo clippy -- -D warnings

# Check code formatting
cargo fmt -- --check
```

### Git Hooks (via `cargo-husky`)
*   **Pre-commit hook**: Checks/formats code with `cargo fmt`.
*   **Pre-push hook**: Executes `cargo clippy` and `cargo audit` to detect dependency vulnerabilities.

---

## Testing Architecture

`pinner` has a strict test suite achieving high logical coverage:

### 1. Unit Tests
*   Located directly inside the respective source files within a `tests` module block (e.g., at the end of `src/resolver/provider.rs`).
*   Mock providers are auto-generated using `#[cfg_attr(test, mockall::automock)]` annotations on traits like `RemoteProvider`.

### 2. Integration Tests
*   Located under the `tests/` directory (`tests/cli.rs` and `tests/integration.rs`).
*   **Mockito HTTP Mocks**: Starts standard async servers using `mockito::Server::new_async()`. The mock server URLs are injected into the CLI configuration args (e.g. `--github-url`) to redirect remote requests.
*   **Sandbox Isolation**: Uses the `tempfile::tempdir` crate to create temporary workspace environments, avoiding interference with actual system configuration files.
*   **Sequential Run**: Integration tests are annotated with `#[serial_test::serial]` to prevent port conflicts or overlapping resources during async execution.

---

## How to Extend Pinner

### 1. Adding a new CI Provider (Scanner Phase)
If you want `pinner` to parse a new CI platform's configuration files:
1.  **Add Enum Variant**: In `src/core/dependency.rs`, add the new platform to `CiProvider`.
2.  **Add File Path Heuristic**: In `src/scanner/parser.rs`, update `CiProvider::from_path` to map the file directory layout (e.g., `.circleci` -> `CiProvider::CircleCI`).
3.  **Define YAML Keys**: Update `CiProvider::supports_key` to define which keys contain dependency tags (e.g. `uses`, `image`).
4.  **Extend AST Query**: If the new keys are not handled by the default `USES_QUERY` matching expression, update the tree-sitter query string in `src/scanner/parser.rs`.

### 2. Adding a new Remote Resolution Provider (Resolver Phase)
If you want to resolve tags on a new repository platform (e.g., Gitea, Sourcehut):
1.  **Create Provider Module**: Create a struct (e.g., `ReqwestMyPlatformProvider`) inside `src/resolver/`.
2.  **Implement Trait**: Implement the `RemoteProvider` trait for your struct (from `src/resolver/provider.rs`).
3.  **Register Provider**:
    *   Add your provider to `ProviderRegistry::new` inside `src/resolver/provider.rs`.
    *   Configure its routing rules in `ProviderTypeInfo` by specifying the target **domain names** (e.g. `myplatform.org`) and unique **YAML keys** it supports.