Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateExtensionJsonSchema
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 4
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 updateRequiredMwVersion
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 updateTo2
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2
3use Composer\Semver\VersionParser;
4use MediaWiki\Json\FormatJson;
5use MediaWiki\Maintenance\Maintenance;
6use MediaWiki\Registration\ExtensionRegistry;
7use Wikimedia\ArrayUtils\ArrayUtils;
8
9// @codeCoverageIgnoreStart
10require_once __DIR__ . '/Maintenance.php';
11// @codeCoverageIgnoreEnd
12
13class UpdateExtensionJsonSchema extends Maintenance {
14
15    public function __construct() {
16        parent::__construct();
17        $this->addDescription( 'Updates extension.json files to the latest manifest_version' );
18        $this->addArg( 'path', 'Location to the extension.json or skin.json you wish to convert',
19            /* $required = */ true );
20    }
21
22    public function execute() {
23        $filename = $this->getArg( 0 );
24        if ( !is_readable( $filename ) ) {
25            $this->fatalError( "Error: Unable to read $filename" );
26        }
27
28        $json = FormatJson::decode( file_get_contents( $filename ), true );
29        if ( !is_array( $json ) ) {
30            $this->fatalError( "Error: Invalid JSON" );
31        }
32
33        if ( !isset( $json['manifest_version'] ) ) {
34            $json['manifest_version'] = 1;
35        }
36
37        if ( $json['manifest_version'] == ExtensionRegistry::MANIFEST_VERSION ) {
38            $this->output( "Already at the latest version: {$json['manifest_version']}\n" );
39            return;
40        }
41
42        while ( $json['manifest_version'] !== ExtensionRegistry::MANIFEST_VERSION ) {
43            $json['manifest_version']++;
44            $func = "updateTo{$json['manifest_version']}";
45            $this->$func( $json );
46        }
47
48        $this->updateRequiredMwVersion( $json );
49
50        file_put_contents( $filename, FormatJson::encode( $json, "\t", FormatJson::ALL_OK ) . "\n" );
51        $this->output( "Updated to {$json['manifest_version']}...\n" );
52    }
53
54    /**
55     * @param array &$json
56     */
57    protected function updateRequiredMwVersion( &$json ) {
58        if ( !isset( $json['requires'] ) ) {
59            $json['requires'] = [];
60        }
61
62        $needNewVersion = true;
63
64        // When version is set, parse it and compare against requirement for new manifest
65        if ( isset( $json['requires'][ExtensionRegistry::MEDIAWIKI_CORE] ) ) {
66            $versionParser = new VersionParser();
67            $currentRequired = $versionParser->parseConstraints(
68                // @phan-suppress-next-line PhanTypeInvalidDimOffset,PhanTypeMismatchArgument isset check exists
69                $json['requires'][ExtensionRegistry::MEDIAWIKI_CORE]
70            );
71            $newRequired = $versionParser->parseConstraints(
72                // The match works only when using an equal comparision
73                str_replace( '>=', '==', ExtensionRegistry::MANIFEST_VERSION_MW_VERSION )
74            );
75            if ( !$currentRequired->matches( $newRequired ) ) {
76                $needNewVersion = false;
77            }
78        }
79
80        if ( $needNewVersion ) {
81            // Set or update a requirement on the MediaWiki version
82            // that the current MANIFEST_VERSION was introduced in.
83            $json['requires'][ExtensionRegistry::MEDIAWIKI_CORE] =
84                ExtensionRegistry::MANIFEST_VERSION_MW_VERSION;
85        }
86    }
87
88    protected function updateTo2( array &$json ) {
89        if ( isset( $json['config'] ) ) {
90            $config = $json['config'];
91            $json['config'] = [];
92            if ( isset( $config['_prefix'] ) ) {
93                $json = ArrayUtils::insertAfter( $json, [
94                    'config_prefix' => $config['_prefix']
95                ], 'config' );
96                unset( $config['_prefix'] );
97            }
98
99            foreach ( $config as $name => $value ) {
100                if ( $name[0] !== '@' ) {
101                    $json['config'][$name] = [ 'value' => $value ];
102                    if ( isset( $value[ExtensionRegistry::MERGE_STRATEGY] ) ) {
103                        $json['config'][$name]['merge_strategy'] = $value[ExtensionRegistry::MERGE_STRATEGY];
104                        unset( $json['config'][$name]['value'][ExtensionRegistry::MERGE_STRATEGY] );
105                    }
106                    if ( isset( $config["@$name"] ) ) {
107                        // Put 'description' first for better human-legibility.
108                        $json['config'][$name] = array_merge(
109                            [ 'description' => $config["@$name"] ],
110                            $json['config'][$name]
111                        );
112                    }
113                }
114            }
115        }
116
117        // Re-maps top level keys under attributes
118        $attributes = [
119            'CodeMirrorPluginModules' => [ 'CodeMirror', 'PluginModules' ],
120            'CodeMirrorTagModes' => [ 'CodeMirror', 'TagModes' ],
121            'EventLoggingSchemas' => [ 'EventLogging', 'Schemas' ],
122            'SyntaxHighlightModels' => [ 'SyntaxHighlight', 'Models' ],
123            'VisualEditorAvailableContentModels' => [ 'VisualEditor', 'AvailableContentModels' ],
124            'VisualEditorAvailableNamespaces' => [ 'VisualEditor', 'AvailableNamespaces' ],
125            'VisualEditorPreloadModules' => [ 'VisualEditor', 'PreloadModules' ],
126            'VisualEditorPluginModules' => [ 'VisualEditor', 'PluginModules' ],
127        ];
128
129        foreach ( $attributes as $name => $value ) {
130            if ( !isset( $json[$name] ) ) {
131                continue;
132            }
133
134            $json['attributes'][$value[0]][$value[1]] = $json[$name];
135            unset( $json[$name] );
136        }
137    }
138}
139
140// @codeCoverageIgnoreStart
141$maintClass = UpdateExtensionJsonSchema::class;
142require_once RUN_MAINTENANCE_IF_MAIN;
143// @codeCoverageIgnoreEnd