// Package nwsync publishes NWSync repository data: blobs and a per-artifact // NSYM manifest at each artifact's birth (emit), and one merged manifest at // module release (assemble). // // The split exists because upstream nwn_nwsync_write wants every hak, the TLK // and the module present in one run on one disk, which our build hosts cannot // hold. Upstream stays the conformance oracle: manifests compare byte for // byte, blobs compare after decompression. package nwsync import ( "flag" "fmt" "io" "strings" ) const ( exitOK = 0 exitUsage = 64 exitInternal = 70 ) // Run executes an nwsync subcommand. args[0] is the subcommand (emit|assemble); // returns the process exit code. func Run(args []string, stdout, stderr io.Writer) int { if len(args) == 0 { printRunUsage(stderr) return exitUsage } switch args[0] { case "emit": return runEmit(args[1:], stdout, stderr) case "assemble": return runAssemble(args[1:], stdout, stderr) case "-h", "--help", "help": printRunUsage(stdout) return exitOK default: fmt.Fprintf(stderr, "nwsync: unknown subcommand %q\n\n", args[0]) printRunUsage(stderr) return exitUsage } } func printRunUsage(w io.Writer) { fmt.Fprint(w, `usage: nwsync emit --out DIR nwsync assemble --order NAMES --entries DIR --out DIR [--group-id N] emit explodes one .hak/.erf or one loose file (the TLK) into NWSync blobs plus a NSYM manifest covering only that artifact. assemble merges those per-artifact manifests into one, reading no bulk data. `) } func runEmit(args []string, stdout, stderr io.Writer) int { fs := flag.NewFlagSet("emit", flag.ContinueOnError) fs.SetOutput(stderr) out := fs.String("out", "", "output directory (blobs plus the per-artifact manifest)") if err := fs.Parse(args); err != nil { return exitUsage } if fs.NArg() != 1 { fmt.Fprintf(stderr, "nwsync emit: exactly one artifact is required\n") return exitUsage } if *out == "" { fmt.Fprintf(stderr, "nwsync emit: --out is required\n") return exitUsage } result, err := Emit(fs.Arg(0), *out) if err != nil { fmt.Fprintf(stderr, "nwsync emit: %v\n", err) return exitInternal } fmt.Fprintf(stdout, "emitted %s: %d resources, %d new blobs, manifest %s\n", result.Name, result.Entries, result.BlobsWritten, result.ManifestPath) return exitOK } func runAssemble(args []string, stdout, stderr io.Writer) int { fs := flag.NewFlagSet("assemble", flag.ContinueOnError) fs.SetOutput(stderr) order := fs.String("order", "", "comma-separated artifact names, highest priority first") entries := fs.String("entries", "", "directory holding the per-artifact .nsym files") out := fs.String("out", "", "repository root; the manifest lands in /manifests") groupID := fs.Int("group-id", 0, "NWSync group id (1 = current, 2 = testing; 0 omits it)") moduleName := fs.String("module-name", "", "module name recorded in the sidecar") description := fs.String("description", "", "description recorded in the sidecar") if err := fs.Parse(args); err != nil { return exitUsage } switch { case *order == "": fmt.Fprintf(stderr, "nwsync assemble: --order is required\n") return exitUsage case *entries == "": fmt.Fprintf(stderr, "nwsync assemble: --entries is required\n") return exitUsage case *out == "": fmt.Fprintf(stderr, "nwsync assemble: --out is required\n") return exitUsage } names := make([]string, 0, 8) for _, name := range strings.Split(*order, ",") { if name = strings.TrimSpace(name); name != "" { names = append(names, name) } } result, err := Assemble(AssembleOptions{ Order: names, EntriesDir: *entries, OutDir: *out, GroupID: *groupID, ModuleName: *moduleName, Description: *description, }) if err != nil { fmt.Fprintf(stderr, "nwsync assemble: %v\n", err) return exitInternal } fmt.Fprintf(stdout, "assembled %s: %d resources from %d artifacts\n", result.SHA1, result.Entries, len(names)) return exitOK }