From fa693702959e48aa03f9e2465d891ad30077d5d3 Mon Sep 17 00:00:00 2001 From: Paul Jolly Date: Sat, 30 Sep 2023 05:12:15 +0100 Subject: [PATCH] internal/ci: use writefs Signed-off-by: Paul Jolly Change-Id: Ia9203e2138ad74947ba49614df3298d84b7aa860 Dispatch-Trailer: {"type":"trybot","CL":1170145,"patchset":1,"ref":"refs/changes/45/1170145/1","targetBranch":"alpha"} --- internal/ci/github/workflows.cue | 34 ++++++++++++ internal/cmd/writefs/main.go | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 internal/cmd/writefs/main.go diff --git a/internal/ci/github/workflows.cue b/internal/ci/github/workflows.cue index 1173474672..c5376f5b3a 100644 --- a/internal/ci/github/workflows.cue +++ b/internal/ci/github/workflows.cue @@ -15,6 +15,12 @@ // package github declares the workflows for this project. package github +import ( + "encoding/yaml" + + "github.com/cue-lang/cuelang.org/internal/ci/base" +) + // Note: the name of the workflows (and hence the corresponding .yml filenames) // correspond to the environment variable names for gerritstatusupdater. // Therefore, this filename must only be change in combination with also @@ -55,3 +61,31 @@ _linuxWorkflow: { dummyDispatch: _repo.#dispatch & { type: _repo.trybot.key } + +// #writefs mirrors the type of the arguments expected by +// internal/cmd/writefs +#writefs: { + Remove: [...string] + Create: [string]: { + Type: "symlink" | *"file" + Contents: *"" | string + } +} + +fs: #writefs & { + // TODO: do not hardcode this to ci_tool + let donotedit = base.doNotEditMessage & {#generatedBy: "internal/ci/ci_tool.cue", _} + + Remove: [ + "../../.github/workflows/*.yml", + ] + + Create: { + for _name, _workflow in workflows { + "\(_name).yml": { + Contents: "# \(donotedit)\n\n\(yaml.Marshal(_workflow))" + } + } + "../../codereview.cfg": + } +} diff --git a/internal/cmd/writefs/main.go b/internal/cmd/writefs/main.go new file mode 100644 index 0000000000..26182008ea --- /dev/null +++ b/internal/cmd/writefs/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "bytes" + "encoding/json" + "log" + "os" + "path/filepath" +) + +type file struct { + Type string + + // If typ == "symlink" this is the filepath of the target + // otherwise it is the Contents of the file + Contents string +} + +type args struct { + // Remove is a list of globs of files to remove. File + // removal happens before file creation. + Remove []string + + // Create is a map of file path (in Unix format) to contents. + Create map[string]file +} + +func main() { + var todo args + dec := json.NewDecoder(os.Stdin) + if err := dec.Decode(&todo); err != nil { + log.Fatalf("failed to decode arguments from stdin: %v", err) + } + for _, glob := range todo.Remove { + files, err := filepath.Glob(glob) + if err != nil { + log.Fatalf("failed to glob %q: %v", glob, err) + } + if len(files) == 0 { + continue + } + for _, f := range files { + if err := os.Remove(f); err != nil { + log.Fatalf("failed to remove %s: %v", f, err) + } + } + } + for fp, f := range todo.Create { + fp = filepath.FromSlash(fp) + dir := filepath.Dir(fp) + if err := os.MkdirAll(dir, 0o777); err != nil { + log.Fatalf("failed to mkdir %s: %v", dir, err) + } + switch f.Type { + case "symlink": + target := filepath.FromSlash(f.Contents) + actualTarget, err := os.Readlink(fp) + if err == nil && actualTarget == target { + continue + } + if err := os.Symlink(target, fp); err != nil { + log.Fatalf("failed to symlink %s -> %s: %v", fp, target, err) + } + case "file": + // If we have a .json file, normalise the contents first + // as a special case. + // + // TODO: this actually belongs as an option in CUE. + contents := []byte(f.Contents) + if filepath.Ext(fp) == ".json" { + var i any + err := json.Unmarshal(contents, &i) + if err == nil { + var b bytes.Buffer + enc := json.NewEncoder(&b) + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + if err := enc.Encode(i); err == nil { + // Add a trailing newline + contents = b.Bytes() + } + } + } + if err := os.WriteFile(fp, []byte(contents), 0o666); err != nil { + log.Fatalf("failed to write file %s: %v", fp, err) + } + } + } +}