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