cuelang.org/go@v0.13.0/encoding/jsonschema/vendor_external.go (about) 1 // Copyright 2024 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build ignore 16 17 // This command copies external JSON Schema tests into the local 18 // repository. It tries to maintain existing test-skip information 19 // to avoid unintentional regressions. 20 21 package main 22 23 import ( 24 "archive/zip" 25 "bytes" 26 "errors" 27 "flag" 28 "fmt" 29 "io" 30 "io/fs" 31 "log" 32 "net/http" 33 "os" 34 "path" 35 "path/filepath" 36 "strings" 37 38 "cuelang.org/go/encoding/jsonschema/internal/externaltest" 39 ) 40 41 const ( 42 testRepo = "https://github.com/json-schema-org/JSON-Schema-Test-Suite.git" 43 testDir = "testdata/external" 44 ) 45 46 func main() { 47 flag.Usage = func() { 48 fmt.Fprintf(os.Stderr, "usage: vendor-external commit\n") 49 os.Exit(2) 50 } 51 log.SetFlags(log.Lshortfile | log.Ltime | log.Lmicroseconds) 52 flag.Parse() 53 if flag.NArg() != 1 { 54 flag.Usage() 55 } 56 if err := doVendor(flag.Arg(0)); err != nil { 57 log.Fatal(err) 58 } 59 } 60 61 func doVendor(commit string) error { 62 // Fetch a commit from GitHub via their archive ZIP endpoint, which is a lot faster 63 // than git cloning just to retrieve a single commit's files. 64 // See: https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#download-a-repository-archive-zip 65 zipURL := fmt.Sprintf("https://github.com/json-schema-org/JSON-Schema-Test-Suite/archive/%s.zip", commit) 66 log.Printf("fetching %s", zipURL) 67 resp, err := http.Get(zipURL) 68 if err != nil { 69 return err 70 } 71 defer resp.Body.Close() 72 zipBytes, err := io.ReadAll(resp.Body) 73 if err != nil { 74 return err 75 } 76 77 log.Printf("reading old test data") 78 oldTests, err := externaltest.ReadTestDir(testDir) 79 if err != nil && !errors.Is(err, externaltest.ErrNotFound) { 80 return err 81 } 82 83 log.Printf("copying files to %s", testDir) 84 testSubdir := filepath.Join(testDir, "tests") 85 if err := os.RemoveAll(testSubdir); err != nil { 86 return err 87 } 88 zipr, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) 89 if err != nil { 90 return err 91 } 92 // Note that GitHub produces archives with a top-level directory representing 93 // the name of the repository and the version which was retrieved. 94 fsys, err := fs.Sub(zipr, fmt.Sprintf("JSON-Schema-Test-Suite-%s/tests", commit)) 95 if err != nil { 96 return err 97 } 98 err = fs.WalkDir(fsys, ".", func(filename string, d fs.DirEntry, err error) error { 99 if err != nil { 100 return err 101 } 102 // Exclude draft-next (too new) and draft3 (too old). 103 if d.IsDir() && (filename == "draft-next" || filename == "draft3") { 104 return fs.SkipDir 105 } 106 // Exclude symlinks and directories 107 if !d.Type().IsRegular() { 108 return nil 109 } 110 if !strings.HasSuffix(filename, ".json") { 111 return nil 112 } 113 if err := os.MkdirAll(filepath.Join(testSubdir, path.Dir(filename)), 0o777); err != nil { 114 return err 115 } 116 data, err := fs.ReadFile(fsys, filename) 117 if err != nil { 118 return err 119 } 120 if err := os.WriteFile(filepath.Join(testSubdir, filename), data, 0o666); err != nil { 121 return err 122 } 123 return nil 124 }) 125 if err != nil { 126 return err 127 } 128 129 // Read the test data back that we've just written and attempt 130 // to populate skip data from the original test data. 131 // As indexes are not necessarily stable (new test cases 132 // might be inserted in the middle of an array), we try 133 // first to look up the skip info by JSON data, and then 134 // by test description. 135 byJSON := make(map[skipKey]externaltest.Skip) 136 byDescription := make(map[skipKey]externaltest.Skip) 137 138 for filename, schemas := range oldTests { 139 for _, schema := range schemas { 140 byJSON[skipKey{filename, string(schema.Schema), ""}] = schema.Skip 141 byDescription[skipKey{filename, schema.Description, ""}] = schema.Skip 142 for _, test := range schema.Tests { 143 byJSON[skipKey{filename, string(schema.Schema), string(test.Data)}] = test.Skip 144 byDescription[skipKey{filename, schema.Description, test.Description}] = schema.Skip 145 } 146 } 147 } 148 149 newTests, err := externaltest.ReadTestDir(testDir) 150 if err != nil { 151 return err 152 } 153 154 for filename, schemas := range newTests { 155 for _, schema := range schemas { 156 skip, ok := byJSON[skipKey{filename, string(schema.Schema), ""}] 157 if !ok { 158 skip, _ = byDescription[skipKey{filename, schema.Description, ""}] 159 } 160 schema.Skip = skip 161 for _, test := range schema.Tests { 162 skip, ok := byJSON[skipKey{filename, string(schema.Schema), string(test.Data)}] 163 if !ok { 164 skip, _ = byDescription[skipKey{filename, schema.Description, test.Description}] 165 } 166 test.Skip = skip 167 } 168 } 169 } 170 if err := externaltest.WriteTestDir(testDir, newTests); err != nil { 171 return err 172 } 173 log.Printf("finished") 174 return nil 175 } 176 177 type skipKey struct { 178 filename string 179 schema string 180 test string 181 }