forked from mirrors/pkg-proxy
216 lines
5.5 KiB
Markdown
216 lines
5.5 KiB
Markdown
# Contributing to git-pkgs proxy
|
|
|
|
Thank you for your interest in contributing. This document covers how to get started, the code structure, and guidelines for submitting changes.
|
|
|
|
## Development Setup
|
|
|
|
```bash
|
|
# Clone the repository
|
|
git clone https://github.com/git-pkgs/proxy.git
|
|
cd proxy
|
|
|
|
# Install dependencies
|
|
go mod download
|
|
|
|
# Run tests
|
|
go test ./...
|
|
|
|
# Build
|
|
go build -o proxy ./cmd/proxy
|
|
|
|
# Run locally
|
|
./proxy
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
proxy/
|
|
├── cmd/
|
|
│ └── proxy/
|
|
│ └── main.go # CLI entry point
|
|
├── internal/
|
|
│ ├── config/ # Configuration loading
|
|
│ │ └── config.go
|
|
│ ├── database/ # SQLite cache metadata
|
|
│ │ ├── database.go # DB connection, open/create
|
|
│ │ ├── schema.go # Table definitions
|
|
│ │ ├── types.go # Go structs for tables
|
|
│ │ └── queries.go # CRUD operations
|
|
│ ├── storage/ # Artifact file storage
|
|
│ │ ├── storage.go # Storage interface
|
|
│ │ └── filesystem.go # Local filesystem impl
|
|
│ ├── upstream/ # Upstream registry clients
|
|
│ │ ├── fetcher.go # HTTP artifact fetching
|
|
│ │ └── resolver.go # Download URL resolution
|
|
│ ├── handler/ # Protocol handlers
|
|
│ │ ├── handler.go # Base proxy + cache logic
|
|
│ │ ├── npm.go # npm protocol
|
|
│ │ └── cargo.go # Cargo protocol
|
|
│ └── server/ # HTTP server
|
|
│ └── server.go # Router, middleware
|
|
├── docs/
|
|
│ └── architecture.md # Internal architecture
|
|
├── README.md # End user documentation
|
|
├── CONTRIBUTING.md # This file
|
|
├── go.mod
|
|
└── go.sum
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
The proxy is built from several independent packages:
|
|
|
|
### `internal/database`
|
|
|
|
SQLite-based cache metadata. Tracks packages, versions, and artifacts. Uses `modernc.org/sqlite` for a pure-Go SQLite driver (no CGO).
|
|
|
|
Key types:
|
|
- `Package` - Package metadata (purl, ecosystem, name)
|
|
- `Version` - Version metadata (version number, integrity hash)
|
|
- `Artifact` - Cached file info (storage path, size, hash, hit count)
|
|
|
|
### `internal/storage`
|
|
|
|
Artifact file storage abstraction. Currently implements local filesystem storage. Designed to allow future backends (S3, GCS).
|
|
|
|
Interface:
|
|
```go
|
|
type Storage interface {
|
|
Store(ctx, path string, r io.Reader) (size int64, hash string, err error)
|
|
Open(ctx, path string) (io.ReadCloser, error)
|
|
Exists(ctx, path string) (bool, error)
|
|
Delete(ctx, path string) error
|
|
Size(ctx, path string) (int64, error)
|
|
UsedSpace(ctx context.Context) (int64, error)
|
|
}
|
|
```
|
|
|
|
### `internal/upstream`
|
|
|
|
Fetches artifacts from upstream registries. Two components:
|
|
|
|
- `Fetcher` - HTTP client with retries, streams artifacts
|
|
- `Resolver` - Determines download URLs for each ecosystem
|
|
|
|
### `internal/handler`
|
|
|
|
HTTP protocol handlers. Each registry protocol has its own handler:
|
|
|
|
- `NPMHandler` - npm registry protocol
|
|
- `CargoHandler` - Cargo sparse index protocol
|
|
|
|
Handlers use a shared `Proxy` type that coordinates caching.
|
|
|
|
### `internal/server`
|
|
|
|
HTTP server setup, router configuration, middleware.
|
|
|
|
### `internal/config`
|
|
|
|
Configuration loading from files, environment variables, and flags.
|
|
|
|
## Adding a New Registry
|
|
|
|
To add support for a new package registry:
|
|
|
|
1. **Add URL resolution** in `internal/upstream/resolver.go`:
|
|
|
|
```go
|
|
case "newregistry":
|
|
url = fmt.Sprintf("https://registry.example.com/%s/%s/download", name, version)
|
|
filename = fmt.Sprintf("%s-%s.tar.gz", name, version)
|
|
```
|
|
|
|
2. **Create handler** in `internal/handler/newregistry.go`:
|
|
|
|
```go
|
|
type NewRegistryHandler struct {
|
|
proxy *Proxy
|
|
proxyURL string
|
|
}
|
|
|
|
func NewNewRegistryHandler(proxy *Proxy, proxyURL string) *NewRegistryHandler {
|
|
return &NewRegistryHandler{proxy: proxy, proxyURL: proxyURL}
|
|
}
|
|
|
|
func (h *NewRegistryHandler) Routes() http.Handler {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("GET /{name}/{version}", h.handleDownload)
|
|
return mux
|
|
}
|
|
```
|
|
|
|
3. **Mount handler** in `internal/server/server.go`:
|
|
|
|
```go
|
|
newHandler := handler.NewNewRegistryHandler(proxy, s.cfg.BaseURL)
|
|
mux.Handle("/newregistry/", http.StripPrefix("/newregistry", newHandler.Routes()))
|
|
```
|
|
|
|
4. **Add tests** in `internal/handler/newregistry_test.go`
|
|
|
|
5. **Update documentation** in README.md
|
|
|
|
## Code Style
|
|
|
|
- Follow standard Go conventions
|
|
- Run `go fmt` before committing
|
|
- Run `go vet` to catch common issues
|
|
- Keep functions short and focused
|
|
- Write tests for new functionality
|
|
- Document exported types and functions
|
|
|
|
## Testing
|
|
|
|
Run all tests:
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
Run with verbose output:
|
|
|
|
```bash
|
|
go test -v ./...
|
|
```
|
|
|
|
Run specific package:
|
|
|
|
```bash
|
|
go test -v ./internal/handler/
|
|
```
|
|
|
|
Run with coverage:
|
|
|
|
```bash
|
|
go test -cover ./...
|
|
```
|
|
|
|
## Pull Request Process
|
|
|
|
1. Fork the repository
|
|
2. Create a branch for your feature (`git checkout -b feature/my-feature`)
|
|
3. Make your changes
|
|
4. Add tests for new functionality
|
|
5. Run `go test ./...` and ensure all tests pass
|
|
6. Run `go fmt ./...` and `go vet ./...`
|
|
7. Commit with a clear message
|
|
8. Push to your fork
|
|
9. Open a pull request
|
|
|
|
### Commit Messages
|
|
|
|
Write clear, concise commit messages:
|
|
|
|
```
|
|
Add PyPI registry support
|
|
|
|
- Add URL resolution for PyPI packages
|
|
- Create PyPI protocol handler
|
|
- Add tests for wheel and sdist downloads
|
|
```
|
|
|
|
## Questions?
|
|
|
|
Open an issue on GitHub if you have questions or need help getting started.
|