-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathupdate-schemas
More file actions
153 lines (127 loc) · 6.01 KB
/
update-schemas
File metadata and controls
153 lines (127 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env perl
# vim: set ts=8 sts=2 sw=2 tw=100 et ft=perl :
use strict;
use warnings;
use 5.020;
no autovivification warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
no if "$]" >= 5.041009, feature => 'smartmatch';
no feature 'switch';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use Mojo::File 'path';
use Mojo::UserAgent;
use Digest::MD5 'md5_hex';
use JSON::Schema::Modern 0.624;
use Test::File::ShareDir -share => { -dist => { 'OpenAPI-Modern' => 'share' } };
use lib 'lib'; # make sure we load the newly-patched version of our modules
use OpenAPI::Modern::Utilities qw(OAS_SCHEMAS OAS_VERSIONS);
# { download URL -> filename (in share) }
my %files = (
(OpenAPI::Modern::Utilities::_BUNDLED_SCHEMAS->%{ map @$_, values OAS_SCHEMAS->%* }),
'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/LICENSE' => 'oas/LICENSE',
);
my $web_url = 'https://spec.openapis.org/oas';
my $ua = Mojo::UserAgent->new(max_redirects => 3);
say "# fetching $web_url" if $ENV{DEBUG};
my $res = $ua->get($web_url)->result;
die "Failed to fetch $web_url", $res->code, " ", $res->message if $res->is_error;
# keys are existing OAS schema URLs, as listed in OpenAPI::Modern::Utilities
# values is the list of all schemas found on the website of the same type
my %new_schemas;
# check the website and find all files that are newer than what we've got
my $links;
foreach my $e ($res->dom->find('a[href]')->each) {
my $link = $e->{href};
next if $link !~ m{^/oas/(?:${\ join('|', map quotemeta($_), OAS_VERSIONS->@*)})/[a-z-]+/\d{4}-\d{2}-\d{2}\z};
++$links;
$link = Mojo::URL->new($web_url)->path($link)->to_string; # get full URL, normalizing slashes
if (my ($existing) = grep !/github/ && $link =~ m{^\Q${\substr($_, 0, -10)}\E}, keys %files) {
$new_schemas{$existing} //= [];
push $new_schemas{$existing}->@*, $link if $existing lt $link;
}
}
die "no links found on $web_url?!" if not $links;
# identify outdated schema files, and replace all references to them in the repository
if (my @outdated_schemas = grep $new_schemas{$_}->@*, reverse sort keys %new_schemas) {
warn join("\n", 'these outdated entries will be replaced everywhere they are found (including in this script itself):',
map "$_ -> $new_schemas{$_}->[0]", @outdated_schemas), "\n\n";
# now we just keep track of the newest version of each..
%new_schemas = map +($_ => $new_schemas{$_}->[0]), @outdated_schemas;
if ($ENV{DEBUG}) {
foreach my $outdated (@outdated_schemas) {
say 'replacing "'.$outdated.'" with "'.$new_schemas{$outdated}.'"';
}
}
foreach my $file (split("\n", `git ls-files`) ) {
next if $file =~ m{^share/oas/}; # skip references in the files we are replacing
$file = path($file);
my $content = $file->slurp;
$content =~ s/$_/$new_schemas{$_}/g foreach @outdated_schemas;
$file->spew($content);
}
# update the values of %files for all schemas that are outdated
# we want to download even unchanged files to make sure the checksum matches
foreach my $outdated (@outdated_schemas) {
$files{$new_schemas{$outdated}} = delete $files{$outdated};
}
}
my $json_decoder = JSON::Schema::Modern::_JSON_BACKEND()->new->utf8(1);
my $js = JSON::Schema::Modern->new;
my %checksums;
# download fresh copies of all files, validate against their schemas
foreach my $uri (sort keys %files) {
my $target = $files{$uri};
say "# fetching $uri -> share/$target" if $ENV{DEBUG};
my $res = $ua->get($uri)->result;
die "Failed to fetch $uri", $res->code, " ", $res->message if $res->is_error;
$target = path('share', $target);
$target->dirname->make_path;
my $exists = -e $target;
$target->spew(my $content = $res->body);
system('git add '.$target) if not $exists;
$checksums{$target} = md5_hex($content);
next if $target->basename eq 'LICENSE';
# perform a simple validation, which should use a metaschema that is already preloaded into the
# JSON::Schema::Modern instance
my $schema = $json_decoder->decode($content);
say '# validating ', $schema->{'$id'}, ' -> ', $target if $ENV{DEBUG};
my $result = $js->validate_schema($schema, { strict => 1, validate_formats => 1 });
die $result->dump if not $result->valid;
}
# update checksums for other dirty files
if (keys %checksums) {
foreach my $file (glob('share/3.*/*')) {
system(qw(git diff --quiet --stat), $file);
$checksums{$file} = md5_hex(path($file)->slurp) if $?;
}
}
# compute checksums and record them in the test, preserving unaffected entries
my $checksums_file = path('t/checksums.t');
my $checksums_content = $checksums_file->slurp;
$checksums_content =~ m/^__DATA__$/mg;
%checksums = ((split /\s/, substr($checksums_content, pos($checksums_content)+1)), %checksums);
$checksums_file->spew(substr($checksums_content, 0, pos($checksums_content)+1)
.join("\n", map $_.' '.$checksums{$_}, sort keys %checksums)."\n");
# only edit Changes file if there are updated files to be committed
my @changed_versions = grep +(
system('git diff --quiet --stat share/oas/'.$_) >> 8
), OAS_VERSIONS->@*;
if (@changed_versions) {
my ($seen_next, $seen_blank);
my $versions = (@changed_versions > 1 ? 's ' : ' ').join(', ', @changed_versions);
say '# edited Changes file for OAS version'.$versions if $ENV{DEBUG};
my $changes_file = path('Changes');
my $changes_content = $changes_file->slurp('UTF-8');
$changes_content =~ m/^\{\{\$NEXT\}\}$/mg;
$changes_content =~ m/^$/mg; # find first empty line after {{$NEXT}}, and insert before it.
my $leader = ' 'x10;
$changes_content =~ s/\G/$leader- updated bundled schemas for OAS version${versions} to their latest\n$leader published versions\n/m;
$changes_file->spew($changes_content, 'UTF-8');
exec 'git commit -m"update bundled schemas for OAS '.join(', ', @changed_versions).' to latest versions" Changes lib share t';
}
else {
say '# no changes to bundled schemas';
}