diff options
| author | Jeff Carr <[email protected]> | 2025-10-05 06:27:50 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-05 06:27:50 -0500 |
| commit | b65fe9b53c549ba63ec390b2fb2950345ed1fdb9 (patch) | |
| tree | 8c4cb87671f766d0e860ae42988b244b3fe9acfe | |
day1v0.0.1
| -rw-r--r-- | Makefile | 41 | ||||
| -rwxr-xr-x | build | 4 | ||||
| -rw-r--r-- | control | 12 | ||||
| -rw-r--r-- | main.go | 115 | ||||
| -rw-r--r-- | stuff.go | 226 | ||||
| -rw-r--r-- | wit-sid.asc | 41 | ||||
| -rw-r--r-- | wit.list | 4 |
7 files changed, 443 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e253ca --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +.PHONY: build + +VERSION = $(shell git describe --tags) +GUIVERSION = $(shell git describe --tags) +# BUILDTIME = $(shell date +%Y.%m.%d) +BUILDTIME = $(shell date +%s) + +all: install + +go-build: goimports + GO111MODULE=off go build \ + -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" + +verbose: + GO111MODULE=off go build -v -x \ + -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" + +install: goimports + GO111MODULE=off go install \ + -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" + +vet: + GO111MODULE=off go vet +# this is how the mirrors.wit.com debian package is created + +go-deb: + go-deb --repo . + +goimports: + goimports -w *.go + # // to globally reset paths: + # // gofmt -w -r '"go.wit.com/gui/gadgets" -> "go.wit.com/lib/gadgets"' *.go + +clean: + rm -f go.* + +gpl: + wit-test --witcom + +check-git-clean: + @git diff-index --quiet HEAD -- || (echo "Git repository is dirty, please commit your changes first"; exit 1) @@ -0,0 +1,4 @@ +#!/bin/bash -x + +mkdir -p files/usr/share/bash-completion/completions/ +mirrors --bash > files/usr/share/bash-completion/completions/mirrors @@ -0,0 +1,12 @@ +Source: +Package: mirrors.wit.com +Build-Depends: +Maintainer: Jeff Carr <[email protected]> +Packager: Jeff Carr <[email protected]> +Architecture: all +Depends: +URL: https://mirrors.wit.com/ +Recommends: +Version: 0.0.7 +Description: apt keys and source file + This was packaged with go-deb from go.wit.com @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" +) + +// --- Configuration --- +// !!! IMPORTANT: Set your GPG Key ID here! +// Find it with: gpg --list-secret-keys --keyid-format=long +const gpgKeyID = "5D7C9BE47836D2FA48F83C2B4A854AEAF7E0E16D" + +const dist = "sid" +const component = "main" +const poolDir = "pool" +const distsDir = "dists" + +var architectures = []string{"amd64", "riscv64", "arm64", "all"} + +// DebInfo holds the control information for a single .deb package. +type DebInfo struct { + ControlData map[string]string + Filename string + Size int64 + MD5Sum string + SHA1Sum string + SHA256Sum string +} + +func main() { + log.Println("--- Starting Debian repository generation in Go ---") + + if gpgKeyID == "YOUR_GPG_KEY_ID" || gpgKeyID == "" { + log.Fatal("ERROR: Please set the 'gpgKeyID' constant at the top of the script.") + } + + // 1. Clean and create directory structure + distPath := filepath.Join(distsDir, dist) + log.Printf("Cleaning up old dists directory: %s", distPath) + if err := os.RemoveAll(distPath); err != nil { + log.Fatalf("Failed to remove old dists directory: %v", err) + } + + log.Println("Creating new directory structure...") + for _, arch := range architectures { + binPath := filepath.Join(distPath, component, "binary-"+arch) + if err := os.MkdirAll(binPath, 0755); err != nil { + log.Fatalf("Failed to create directory %s: %v", binPath, err) + } + } + + // 2. Scan pool directory for .deb files and gather info + log.Printf("Scanning for .deb files in %s/ જુ", poolDir) + debInfos, err := scanDebs(poolDir) + if err != nil { + log.Fatalf("Failed to scan .deb files: %v", err) + } + log.Printf("Found %d total .deb packages.", len(debInfos)) + + // 3. Group packages by architecture + debsByArch := make(map[string][]DebInfo) + for _, deb := range debInfos { + arch := deb.ControlData["Architecture"] + debsByArch[arch] = append(debsByArch[arch], deb) + } + // Add the 'all' packages to each specific architecture list as well, as is standard. + for _, arch := range architectures { + if arch != "all" { + debsByArch[arch] = append(debsByArch[arch], debsByArch["all"]...) + } + } + + // 4. Generate Packages files + log.Println("Generating Packages files...") + for _, arch := range architectures { + binPath := filepath.Join(distPath, component, "binary-"+arch) + packagesFile := filepath.Join(binPath, "Packages") + + var content strings.Builder + for _, deb := range debsByArch[arch] { + for key, val := range deb.ControlData { + fmt.Fprintf(&content, "%s: %s\n", key, val) + } + fmt.Fprintf(&content, "Filename: %s\n", deb.Filename) + fmt.Fprintf(&content, "Size: %d\n", deb.Size) + fmt.Fprintf(&content, "MD5sum: %s\n", deb.MD5Sum) + fmt.Fprintf(&content, "SHA1: %s\n", deb.SHA1Sum) + fmt.Fprintf(&content, "SHA256: %s\n", deb.SHA256Sum) + fmt.Fprintln(&content) + } + + if err := os.WriteFile(packagesFile, []byte(content.String()), 0644); err != nil { + log.Fatalf("Failed to write Packages file for %s: %v", arch, err) + } + + // Compress the Packages file + if err := compressFile(packagesFile, "gz"); err != nil { + log.Fatalf("Failed to gzip Packages file for %s: %v", arch, err) + } + if err := compressFile(packagesFile, "bz2"); err != nil { + log.Fatalf("Failed to bzip2 Packages file for %s: %v", arch, err) + } + } + + // 5. Generate and sign the Release file + log.Println("Generating and signing Release file...") + if err := generateAndSignReleaseFile(distPath); err != nil { + log.Fatalf("Failed to generate or sign Release file: %v", err) + } + + log.Println("--- Repository generation complete! ---") +} diff --git a/stuff.go b/stuff.go new file mode 100644 index 0000000..b1e8283 --- /dev/null +++ b/stuff.go @@ -0,0 +1,226 @@ +package main + +import ( + "bufio" + "bytes" + "compress/gzip" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "time" +) + +// scanDebs finds all .deb files and extracts their metadata. +func scanDebs(root string) ([]DebInfo, error) { + var debs []DebInfo + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(info.Name(), ".deb") { + log.Printf(" -> Processing %s", path) + + // Get control info + cmd := exec.Command("dpkg-deb", "-I", path) + var out bytes.Buffer + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to run dpkg-deb on %s: %v", path, err) + } + + controlData := parseControlData(out.String()) + + // Get checksums + md5sum, sha1sum, sha256sum, err := getChecksums(path) + if err != nil { + return err + } + + relativePath, err := filepath.Rel(".", path) + if err != nil { + return err + } + + debs = append(debs, DebInfo{ + ControlData: controlData, + Filename: relativePath, + Size: info.Size(), + MD5Sum: md5sum, + SHA1Sum: sha1sum, + SHA256Sum: sha256sum, + }) + } + return nil + }) + return debs, err +} + +// parseControlData converts the text output of dpkg-deb into a map. +func parseControlData(data string) map[string]string { + control := make(map[string]string) + scanner := bufio.NewScanner(strings.NewReader(data)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + parts := strings.SplitN(line, ":", 2) + if len(parts) == 2 { + control[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + return control +} + +// getChecksums calculates all required hashes for a file. +// FIX 1: Renamed return variables to avoid shadowing package names. +func getChecksums(filePath string) (md5sum, sha1sum, sha256sum string, err error) { + file, err := os.Open(filePath) + if err != nil { + return "", "", "", err + } + defer file.Close() + + hMD5 := md5.New() + hSHA1 := sha1.New() + hSHA256 := sha256.New() + + // TeeReader allows writing to multiple hashers at once + multiWriter := io.MultiWriter(hMD5, hSHA1, hSHA256) + if _, err := io.Copy(multiWriter, file); err != nil { + return "", "", "", err + } + + return fmt.Sprintf("%x", hMD5.Sum(nil)), + fmt.Sprintf("%x", hSHA1.Sum(nil)), + fmt.Sprintf("%x", hSHA256.Sum(nil)), + nil +} + +// compressFile creates a compressed version of a file (gz or bz2). +func compressFile(sourcePath, format string) error { + if format == "gz" { + sourceFile, err := os.Open(sourcePath) + if err != nil { + return err + } + defer sourceFile.Close() + + destPath := sourcePath + ".gz" + destFile, err := os.Create(destPath) + if err != nil { + return err + } + defer destFile.Close() + + writer := gzip.NewWriter(destFile) + defer writer.Close() + + _, err = io.Copy(writer, sourceFile) + return err + } else if format == "bz2" { + // FIX 2: Shell out to the bzip2 command for compression. + destPath := sourcePath + ".bz2" + cmd := exec.Command("bzip2", "-c", sourcePath) + + outfile, err := os.Create(destPath) + if err != nil { + return fmt.Errorf("failed to create destination file for bzip2: %v", err) + } + defer outfile.Close() + cmd.Stdout = outfile + + return runCommand(cmd) + } + + return fmt.Errorf("unsupported compression format: %s", format) +} + +// generateAndSignReleaseFile creates the main Release file and signs it. +func generateAndSignReleaseFile(distPath string) error { + releasePath := filepath.Join(distPath, "Release") + var content strings.Builder + + // Add headers + fmt.Fprintf(&content, "Origin: WIT.COM Debian Sid\n") + fmt.Fprintf(&content, "Label: WIT.COM Debian Sid\n") + fmt.Fprintf(&content, "Suite: %s\n", dist) + fmt.Fprintf(&content, "Codename: %s\n", dist) + fmt.Fprintf(&content, "Date: %s\n", time.Now().UTC().Format(time.RFC1123Z)) + fmt.Fprintf(&content, "Architectures: %s\n", strings.Join(architectures, " ")) + fmt.Fprintf(&content, "Components: %s\n", component) + fmt.Fprintf(&content, "Description: Tooling for RiscV, Semiconductor Designs & Private Clouds\n") + + // Add checksums + addChecksums := func(name string) { + fmt.Fprintf(&content, "%s:\n", name) + err := filepath.Walk(distPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && info.Name() != "Release" && info.Name() != "InRelease" && info.Name() != "Release.gpg" { + relativePath, _ := filepath.Rel(distPath, path) + + fileBytes, err := os.ReadFile(path) + if err != nil { + return err + } + + var sum string + switch name { + case "MD5Sum": + sum = fmt.Sprintf("%x", md5.Sum(fileBytes)) + case "SHA1": + sum = fmt.Sprintf("%x", sha1.Sum(fileBytes)) + case "SHA256": + // FIX 3: Use the correct sha256.Sum256 function. + sum = fmt.Sprintf("%x", sha256.Sum256(fileBytes)) + } + fmt.Fprintf(&content, " %s %d %s\n", sum, info.Size(), relativePath) + } + return nil + }) + if err != nil { + log.Printf("Warning: could not walk path for checksums: %v", err) + } + } + + addChecksums("MD5Sum") + addChecksums("SHA1") + addChecksums("SHA256") + + if err := os.WriteFile(releasePath, []byte(content.String()), 0644); err != nil { + return fmt.Errorf("failed to write Release file: %v", err) + } + + // Sign the file + log.Println("Signing with GPG key:", gpgKeyID) + + // Create InRelease + cmdClearSign := exec.Command("gpg", "--default-key", gpgKeyID, "--clearsign", "-o", filepath.Join(distPath, "InRelease"), releasePath) + if err := runCommand(cmdClearSign); err != nil { + return fmt.Errorf("failed to create InRelease: %v", err) + } + + // Create Release.gpg + cmdDetachedSign := exec.Command("gpg", "--default-key", gpgKeyID, "-abs", "-o", filepath.Join(distPath, "Release.gpg"), releasePath) + if err := runCommand(cmdDetachedSign); err != nil { + return fmt.Errorf("failed to create Release.gpg: %v", err) + } + + return nil +} + +func runCommand(cmd *exec.Cmd) error { + var stderr bytes.Buffer + cmd.Stderr = &stderr + err := cmd.Run() + if err != nil { + return fmt.Errorf("command '%s' failed: %v\nStderr: %s", cmd.String(), err, stderr.String()) + } + return nil +} diff --git a/wit-sid.asc b/wit-sid.asc new file mode 100644 index 0000000..8648d6c --- /dev/null +++ b/wit-sid.asc @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +yGA= +=tw/F +-----END PGP PUBLIC KEY BLOCK----- diff --git a/wit.list b/wit.list new file mode 100644 index 0000000..f1b6db6 --- /dev/null +++ b/wit.list @@ -0,0 +1,4 @@ +deb http://mirrors.wit.com/wit/ sid main +# deb-src http://mirrors.wit.com/wit/ sid main + +# cp apt-wit.list /etc/apt/sources.list.d/ |
