Files
routewatch/cmd/asinfo-gen/main.go
T
clawbot 0b71832569
check / check (push) Canceled after 0s
Re-vendor the shared files from sneak/prompts at 0653579 (closes #52)
Takes .golangci.yml, .dockerignore, .gitignore, .editorconfig, the
workflow and REPO_POLICIES.md from sneak/prompts 0653579, with this
repository's own entries after the shared content. Lint (golangci-lint
v2.14.0) and test are Dockerfile phases that script/lint and script/test
build alone with a new CACHEBUST value, keeping Go's and golangci-lint's
caches; the image build depends on both. make fmt and make fmt-check also
format the markdown with prettier, pinned in yarn.lock. The agent
guidance moves to AGENTS.md. The Go code is fixed for what the new
configuration finds, and the AS and prefix detail reads now return the
rows' error after their loops.

Model: opus-5-5
2026-10-19 10:00:20 +00:00

113 lines
2.3 KiB
Go

// Package main generates the embedded AS info data for the asinfo package
package main
import (
"context"
"encoding/csv"
"encoding/json"
"errors"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
)
const asCsvURL = "https://github.com/ipverse/asn-info/raw/refs/heads/master/as.csv"
// ASInfo represents information about an Autonomous System
type ASInfo struct {
ASN int `json:"asn"`
Handle string `json:"handle"`
Description string `json:"description"`
}
func main() {
// Configure logger to write to stderr
log.SetOutput(os.Stderr)
// Fetch the CSV data
log.Println("Fetching AS CSV data...")
req, err := http.NewRequestWithContext(
context.Background(), http.MethodGet, asCsvURL, nil)
if err != nil {
log.Fatalf("Failed to fetch CSV: %v", err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("Failed to fetch CSV: %v", err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Failed to fetch CSV: HTTP %d", resp.StatusCode)
}
// Parse CSV
reader := csv.NewReader(resp.Body)
// Read header
header, err := reader.Read()
if err != nil {
log.Fatalf("Failed to read CSV header: %v", err)
}
if len(header) != 3 || header[0] != "asn" || header[1] != "handle" ||
header[2] != "description" {
log.Fatalf("Unexpected CSV header: %v", header)
}
// Read all records
var asInfos []ASInfo
for {
record, err := reader.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Printf("Error reading CSV record: %v", err)
continue
}
const expectedFields = 3
if len(record) != expectedFields {
log.Printf("Skipping invalid record: %v", record)
continue
}
asn, err := strconv.Atoi(record[0])
if err != nil {
log.Printf("Invalid ASN %q: %v", record[0], err)
continue
}
asInfos = append(asInfos, ASInfo{
ASN: asn,
Handle: strings.TrimSpace(record[1]),
Description: strings.TrimSpace(record[2]),
})
}
// Closed here rather than deferred: log.Fatalf exits without running
// deferred calls.
_ = resp.Body.Close()
log.Printf("Parsed %d AS records", len(asInfos))
// Convert to JSON and output to stdout
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", "\t")
err = encoder.Encode(asInfos)
if err != nil {
log.Fatalf("Failed to encode JSON: %v", err)
}
}