Skip to content
This repository was archived by the owner on Sep 23, 2023. It is now read-only.

Commit 2b58d71

Browse files
committed
Allow patches to be updated
Fixes #54
1 parent a21e181 commit 2b58d71

File tree

5 files changed

+223
-1
lines changed

5 files changed

+223
-1
lines changed

deduplicate.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ BASEDIR=$(dirname "$0")
1212
# I'd have to figure out how to provision instances using XFS or something.
1313
# (Also, `rdfind` doesn't have an option for that, `rmlint` could be used.)
1414

15-
sudo -u www-data rdfind -makehardlinks true -makeresultsfile false -checksum sha1 $BASEDIR/wikis
15+
# Only loook inside the "w" folder as while we are updating a wiki it is
16+
# called "w-updating" and shouldn't be deduplicated.
17+
find $BASEDIR/wikis -name "w" -type d \
18+
xargs sudo -u www-data rdfind -makehardlinks true -makeresultsfile false -checksum sha1

index.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@
330330

331331
$actions = [];
332332
if ( $canDelete ) {
333+
$actions[] = '<a href="update.php?wiki=' . $wiki . '">Update</a>';
333334
$actions[] = '<a href="delete.php?wiki=' . $wiki . '">Delete</a>';
334335
}
335336
if ( $canCreate ) {

new/applypatch.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ set -ex
33

44
cd $PATCHDEMO/wikis/$NAME/$REPO
55

6+
# Required when updating an existing wiki
7+
git reset --hard origin/master
8+
69
git fetch origin $REF
710

811
# Apply $HASH and its parent commits up to $BASE on top of current HEAD.

new/postupdate.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
set -ex
3+
4+
# run update script (#166, #244)
5+
php $PATCHDEMO/wikis/$NAME/w/maintenance/update.php --quick
6+
7+
# Update Main_Page
8+
MAINPAGETITLE=$( echo 'echo Title::newMainPage()->getPrefixedText();' | php $PATCHDEMO/wikis/$NAME/w/maintenance/eval.php 2> /dev/null )
9+
MAINPAGECURRENT=$( php $PATCHDEMO/wikis/$NAME/w/maintenance/getText.php "$MAINPAGETITLE" )
10+
echo "$MAINPAGECURRENT$MAINPAGE" | php $PATCHDEMO/wikis/$NAME/w/maintenance/edit.php "$MAINPAGETITLE" || echo "Can't edit main page"

update.php

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<?php
2+
3+
use Symfony\Component\Yaml\Yaml;
4+
5+
require_once "includes.php";
6+
7+
include "header.php";
8+
9+
ob_implicit_flush( true );
10+
11+
if ( $useOAuth && !$user ) {
12+
echo oauth_signin_prompt();
13+
die();
14+
}
15+
16+
$wiki = $_GET['wiki'];
17+
$wikiData = get_wiki_data( $wiki );
18+
19+
if ( !can_delete( $wikiData['creator'] ) ) {
20+
die( '<p>You are not allowed to update this wiki.</p>' );
21+
}
22+
23+
function abandon( string $errHtml ) {
24+
die( $errHtml );
25+
}
26+
27+
echo '<p>Updating wiki <a class="wiki" href="wikis/' . $wiki . '/w" title="' . $wiki . '">' . $wiki . '</a>.</p>';
28+
29+
echo '<div class="consoleLog">';
30+
31+
ob_flush();
32+
33+
$baseEnv = [
34+
'PATCHDEMO' => __DIR__,
35+
'NAME' => $wiki,
36+
];
37+
38+
$patchesApplied = [];
39+
$patchesToUpdate = [];
40+
$linkedTasks = [];
41+
$commands = [];
42+
$usedRepos = [];
43+
44+
foreach ( $wikiData['patchList'] as $patch => $patchData ) {
45+
$r = $patchData['r'];
46+
$data = gerrit_query( "changes/?q=change:$r&o=LABELS&o=CURRENT_REVISION", true );
47+
48+
// get the info
49+
$repo = $data[0]['project'];
50+
$base = 'origin/' . $data[0]['branch'];
51+
$revision = $data[0]['current_revision'];
52+
$ref = $data[0]['revisions'][$revision]['ref'];
53+
$id = $data[0]['id'];
54+
55+
$repos = get_repo_data( 'w-updating/' );
56+
if ( !isset( $repos[ $repo ] ) ) {
57+
$repo = htmlentities( $repo );
58+
abandon( "Repository <em>$repo</em> not supported" );
59+
}
60+
$path = $repos[ $repo ];
61+
$usedRepos[] = $repo;
62+
63+
if (
64+
$config[ 'requireVerified' ] &&
65+
( $data[0]['labels']['Verified']['approved']['_account_id'] ?? null ) !== 75
66+
) {
67+
// The patch doesn't have V+2, check if the uploader is trusted
68+
$uploaderId = $data[0]['revisions'][$revision]['uploader']['_account_id'];
69+
$uploader = gerrit_query( 'accounts/' . $uploaderId, true );
70+
if ( !is_trusted_user( $uploader['email'] ) ) {
71+
abandon( "Patch must be approved (Verified+2) by jenkins-bot, or uploaded by a trusted user" );
72+
}
73+
}
74+
75+
$patch = $data[0]['_number'] . ',' . $data[0]['revisions'][$revision]['_number'];
76+
$patchesApplied[] = $patch;
77+
78+
$r = $patchData['r'];
79+
$pOld = (int)$patchData['p'];
80+
$pNew = $data[0]['revisions'][$revision]['_number'];
81+
if ( $pNew > $pOld ) {
82+
echo "<strong>Updating change $r from patchset $pOld to $pNew.</strong>";
83+
} else {
84+
echo "<strong>Change $r is already using the latest patchset ($pOld).</strong>";
85+
continue;
86+
}
87+
88+
$patchesToUpdate[] = $patch;
89+
90+
$commands[] = [
91+
[
92+
'REPO' => $path,
93+
'REF' => $ref,
94+
'BASE' => $base,
95+
'HASH' => $revision,
96+
],
97+
__DIR__ . '/new/applypatch.sh'
98+
];
99+
100+
$relatedChanges = [];
101+
$relatedChanges[] = [ $data[0]['_number'], $data[0]['revisions'][$revision]['_number'] ];
102+
103+
// Look at all commits in this patch's tree for cross-repo dependencies to add
104+
$data = gerrit_query( "changes/$id/revisions/$revision/related", true );
105+
// Ancestor commits only, not descendants
106+
$foundCurr = false;
107+
foreach ( $data['changes'] as $change ) {
108+
if ( $foundCurr ) {
109+
// Querying by change number is allegedly deprecated, but the /related API doesn't return the 'id'
110+
$relatedChanges[] = [ $change['_change_number'], $change['_revision_number'] ];
111+
}
112+
$foundCurr = $foundCurr || $change['commit']['commit'] === $revision;
113+
}
114+
115+
foreach ( $relatedChanges as [ $c, $r ] ) {
116+
$data = gerrit_query( "changes/$c/revisions/$r/commit", true );
117+
118+
preg_match_all( '/^Depends-On: (.+)$/m', $data['message'], $m );
119+
foreach ( $m[1] as $changeid ) {
120+
if ( !in_array( $changeid, $patches, true ) ) {
121+
// The entry we add here will be processed by the topmost foreach
122+
$patches[] = $changeid;
123+
}
124+
}
125+
}
126+
ob_flush();
127+
}
128+
$usedRepos = array_unique( $usedRepos );
129+
130+
if ( !count( $commands ) ) {
131+
abandon( 'No patches to update.' );
132+
}
133+
134+
$error = shell_echo( __DIR__ . '/new/unlinkbefore.sh', $baseEnv );
135+
if ( $error ) {
136+
abandon( "Could not copy wiki files to update." );
137+
}
138+
139+
// The working directory is now w-updating/, which is ignored by the rdfind cron job (deduplicate.sh)
140+
// This means $wgScriptPath is incorrect, so don't try to run any MW scripts.
141+
142+
foreach ( $commands as $i => $command ) {
143+
$error = shell_echo( $command[1], $baseEnv + $command[0] );
144+
if ( $error ) {
145+
abandon( "Could not apply patch {$patchesToUpdate[$i]}" );
146+
}
147+
ob_flush();
148+
}
149+
150+
$composerInstallRepos = Yaml::parse( file_get_contents( __DIR__ . '/repository-lists/composerinstall.yaml' ) );
151+
foreach ( $usedRepos as $repo ) {
152+
if ( in_array( $repo, $composerInstallRepos, true ) ) {
153+
$error = shell_echo( __DIR__ . '/new/composerinstall.sh',
154+
$baseEnv + [
155+
// Variable used by composer itself, not our script
156+
'COMPOSER_HOME' => __DIR__ . '/composer',
157+
'REPO_TARGET' => $repos[$repo],
158+
]
159+
);
160+
if ( $error ) {
161+
abandon( "Could not fetch dependencies for <em>$repo</em>" );
162+
}
163+
ob_flush();
164+
}
165+
}
166+
167+
$error = shell_echo( __DIR__ . '/new/unlinkafter.sh', $baseEnv );
168+
if ( $error ) {
169+
abandon( "Could not overwrite wiki files after updating." );
170+
}
171+
172+
// The working directory is back to w/. Scripts beyond this point should not modify the filesystem.
173+
174+
$mainPage = "\n\nThis wiki was updated on ~~~~~ with the following newer patches:";
175+
foreach ( $patchesToUpdate as $patch ) {
176+
preg_match( '`([0-9]+),([0-9]+)`', $patch, $matches );
177+
list( $t, $r, $p ) = $matches;
178+
179+
$data = gerrit_query( "changes/$r/revisions/$p/commit", true );
180+
if ( $data ) {
181+
$t = $t . ': ' . $data[ 'subject' ];
182+
get_linked_tasks( $data['message'], $linkedTasks );
183+
}
184+
185+
$t = htmlentities( $t );
186+
187+
$mainPage .= "\n:* [{$config['gerritUrl']}/r/c/$r/$p <nowiki>$t</nowiki>]";
188+
}
189+
190+
$error = shell_echo( __DIR__ . '/new/postupdate.sh',
191+
$baseEnv + [
192+
'MAINPAGE' => $mainPage,
193+
]
194+
);
195+
if ( $error ) {
196+
abandon( "Could not update wiki content" );
197+
}
198+
199+
// Update DB record with _all_ patches applied (include those which weren't updated)
200+
wiki_add_patches( $wiki, $patchesApplied );
201+
wiki_update_timestamp( $wiki );
202+
203+
echo "Done!";
204+
205+
echo '</div>';

0 commit comments

Comments
 (0)