diff --git a/delete.php b/delete.php
index 0a2e3f1f..c14d0177 100644
--- a/delete.php
+++ b/delete.php
@@ -28,7 +28,7 @@
'
' . substr( $wiki, 0, 10 ) . ' | ' .
'' . $patches . ' | ' .
'' . $linkedTasks . ' | ' .
- '' . date( 'Y-m-d H:i:s', $wikiData[ 'created' ] ) . ' | ' .
+ '' . date( 'Y-m-d H:i:s', $wikiData[ 'updated' ] ) . ' | ' .
( $useOAuth ? '' . ( $creator ? user_link( $creator ) : '?' ) . ' | ' : '' ) .
'' .
'';
diff --git a/editcounts.php b/editcounts.php
index 519bf3dd..0c429c6e 100644
--- a/editcounts.php
+++ b/editcounts.php
@@ -47,7 +47,7 @@
],
];
-$results = $mysqli->query( 'SELECT wiki FROM wikis WHERE !deleted ORDER BY created DESC' );
+$results = $mysqli->query( 'SELECT wiki FROM wikis WHERE !deleted ORDER BY updated DESC' );
if ( !$results ) {
die( $mysqli->error );
}
diff --git a/includes.php b/includes.php
index 9ef3eba3..0d368ec3 100644
--- a/includes.php
+++ b/includes.php
@@ -36,13 +36,21 @@ function insert_wiki_data( string $wiki, string $creator, int $created, string $
global $mysqli;
$stmt = $mysqli->prepare( '
INSERT INTO wikis
- (wiki, creator, created, branch)
- VALUES(?, ?, FROM_UNIXTIME(?), ?)
+ (wiki, creator, created, updated, branch)
+ VALUES(?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?)
' );
if ( !$stmt ) {
echo $mysqli->error;
}
- $stmt->bind_param( 'ssis', $wiki, $creator, $created, $branch );
+ $stmt->bind_param( 'ssis', $wiki, $creator, $created, $created, $branch );
+ $stmt->execute();
+ $stmt->close();
+}
+
+function wiki_update_timestamp( string $wiki ) {
+ global $mysqli;
+ $stmt = $mysqli->prepare( 'UPDATE wikis SET updated = NOW() WHERE wiki = ?' );
+ $stmt->bind_param( 's', $wiki );
$stmt->execute();
$stmt->close();
}
@@ -77,7 +85,7 @@ function get_wiki_data( string $wiki ): array {
global $mysqli;
$stmt = $mysqli->prepare( '
- SELECT wiki, creator, UNIX_TIMESTAMP( created ) created, patches, branch, announcedTasks, timeToCreate, deleted
+ SELECT wiki, creator, UNIX_TIMESTAMP( created ) created, UNIX_TIMESTAMP( updated ) updated, patches, branch, announcedTasks, timeToCreate, deleted
FROM wikis WHERE wiki = ?
' );
if ( !$stmt ) {
diff --git a/index.php b/index.php
index fca03f31..cde7b0c3 100644
--- a/index.php
+++ b/index.php
@@ -281,10 +281,10 @@
$username = $user ? $user->username : null;
$stmt = $mysqli->prepare( '
- SELECT wiki, creator, UNIX_TIMESTAMP( created ) created, patches, branch, announcedTasks, timeToCreate, deleted
+ SELECT wiki, creator, UNIX_TIMESTAMP( updated ) updated, patches, branch, announcedTasks, timeToCreate, deleted
FROM wikis
WHERE !deleted
- ORDER BY IF( creator = ?, 1, 0 ) DESC, created DESC
+ ORDER BY IF( creator = ?, 1, 0 ) DESC, updated DESC
' );
if ( !$stmt ) {
die( $mysqli->error );
@@ -330,6 +330,7 @@
$actions = [];
if ( $canDelete ) {
+ $actions[] = 'Update';
$actions[] = 'Delete';
}
if ( $canCreate ) {
@@ -346,7 +347,7 @@
'' .
'' . $patches . ' | ' .
'' . $linkedTasks . ' | ' .
- '' . date( 'Y-m-d H:i:s', $wikiData[ 'created' ] ) . ' | ' .
+ '' . date( 'Y-m-d H:i:s', $wikiData[ 'updated' ] ) . ' | ' .
( $useOAuth ? '' . ( $creator ? user_link( $creator ) : '?' ) . ' | ' : '' ) .
( $canAdmin ? '' . ( $wikiData['timeToCreate'] ? $wikiData['timeToCreate'] . 's' : '' ) . ' | ' : '' ) .
( count( $actions ) ?
diff --git a/new/applypatch.sh b/new/applypatch.sh
index c8101cb1..1d56b8f6 100755
--- a/new/applypatch.sh
+++ b/new/applypatch.sh
@@ -3,6 +3,9 @@ set -ex
cd $PATCHDEMO/wikis/$NAME/$REPO
+# Required when updating an existing wiki
+git reset --hard origin/master
+
git fetch origin $REF
# Apply $HASH and its parent commits up to $BASE on top of current HEAD.
diff --git a/sql/patchdemo.sql b/sql/patchdemo.sql
index c78762b9..df63ae66 100644
--- a/sql/patchdemo.sql
+++ b/sql/patchdemo.sql
@@ -42,3 +42,9 @@ ALTER TABLE `tasks`
ALTER TABLE `wikis`
ADD COLUMN IF NOT EXISTS `branch` VARCHAR(64) NOT NULL AFTER `patches`;
+
+ALTER TABLE `wikis`
+ ADD COLUMN `updated` DATETIME NOT NULL AFTER `created`,
+ ADD INDEX `updated` (`updated`);
+
+UPDATE wikis SET updated = created;
diff --git a/update.php b/update.php
new file mode 100644
index 00000000..7a4ce6f6
--- /dev/null
+++ b/update.php
@@ -0,0 +1,190 @@
+You are not allowed to update this wiki.' );
+}
+
+function abandon( string $errHtml ) {
+ die( $errHtml );
+}
+
+echo 'Updating wiki ' . $wiki . '.
';
+
+echo '';
+
+$patchesApplied = [];
+$patchesToUpdate = [];
+$linkedTasks = [];
+$commands = [];
+$usedRepos = [];
+
+foreach ( $wikiData['patchList'] as $patch => $patchData ) {
+ $r = $patchData['r'];
+ $data = gerrit_query( "changes/?q=change:$r&o=LABELS&o=CURRENT_REVISION", true );
+
+ // get the info
+ $repo = $data[0]['project'];
+ $base = 'origin/' . $data[0]['branch'];
+ $revision = $data[0]['current_revision'];
+ $ref = $data[0]['revisions'][$revision]['ref'];
+ $id = $data[0]['id'];
+
+ $repos = get_repo_data();
+ if ( !isset( $repos[ $repo ] ) ) {
+ $repo = htmlentities( $repo );
+ abandon( "Repository $repo not supported" );
+ }
+ $path = $repos[ $repo ];
+ $usedRepos[] = $repo;
+
+ if (
+ $config[ 'requireVerified' ] &&
+ ( $data[0]['labels']['Verified']['approved']['_account_id'] ?? null ) !== 75
+ ) {
+ // The patch doesn't have V+2, check if the uploader is trusted
+ $uploaderId = $data[0]['revisions'][$revision]['uploader']['_account_id'];
+ $uploader = gerrit_query( 'accounts/' . $uploaderId, true );
+ if ( !is_trusted_user( $uploader['email'] ) ) {
+ abandon( "Patch must be approved (Verified+2) by jenkins-bot, or uploaded by a trusted user" );
+ }
+ }
+
+ $patch = $data[0]['_number'] . ',' . $data[0]['revisions'][$revision]['_number'];
+ $patchesApplied[] = $patch;
+
+ $r = $patchData['r'];
+ $pOld = (int)$patchData['p'];
+ $pNew = $data[0]['revisions'][$revision]['_number'];
+ if ( $pNew > $pOld ) {
+ echo "Updating change $r from patchset $pOld to $pNew.";
+ } else {
+ echo "Change $r is already using the latest patchset ($pOld).";
+ continue;
+ }
+
+ $patchesToUpdate[] = $patch;
+
+ $commands[] = [
+ [
+ 'REPO' => $path,
+ 'REF' => $ref,
+ 'BASE' => $base,
+ 'HASH' => $revision,
+ ],
+ __DIR__ . '/new/applypatch.sh'
+ ];
+
+ $relatedChanges = [];
+ $relatedChanges[] = [ $data[0]['_number'], $data[0]['revisions'][$revision]['_number'] ];
+
+ // Look at all commits in this patch's tree for cross-repo dependencies to add
+ $data = gerrit_query( "changes/$id/revisions/$revision/related", true );
+ // Ancestor commits only, not descendants
+ $foundCurr = false;
+ foreach ( $data['changes'] as $change ) {
+ if ( $foundCurr ) {
+ // Querying by change number is allegedly deprecated, but the /related API doesn't return the 'id'
+ $relatedChanges[] = [ $change['_change_number'], $change['_revision_number'] ];
+ }
+ $foundCurr = $foundCurr || $change['commit']['commit'] === $revision;
+ }
+
+ foreach ( $relatedChanges as [ $c, $r ] ) {
+ $data = gerrit_query( "changes/$c/revisions/$r/commit", true );
+
+ preg_match_all( '/^Depends-On: (.+)$/m', $data['message'], $m );
+ foreach ( $m[1] as $changeid ) {
+ if ( !in_array( $changeid, $patches, true ) ) {
+ // The entry we add here will be processed by the topmost foreach
+ $patches[] = $changeid;
+ }
+ }
+ }
+}
+$usedRepos = array_unique( $usedRepos );
+
+$baseEnv = [
+ 'PATCHDEMO' => __DIR__,
+ 'NAME' => $wiki,
+];
+
+if ( !count( $commands ) ) {
+ abandon( 'No patches to update.' );
+}
+
+$error = shell_echo( __DIR__ . '/new/unlink.sh', $baseEnv );
+if ( $error ) {
+ abandon( "Could not un-duplicate wiki." );
+}
+
+foreach ( $commands as $i => $command ) {
+ $error = shell_echo( $command[1], $baseEnv + $command[0] );
+ if ( $error ) {
+ abandon( "Could not apply patch {$patchesToUpdate[$i]}" );
+ }
+}
+
+$composerInstallRepos = Yaml::parse( file_get_contents( __DIR__ . '/repository-lists/composerinstall.yaml' ) );
+foreach ( $usedRepos as $repo ) {
+ if ( in_array( $repo, $composerInstallRepos, true ) ) {
+ $error = shell_echo( __DIR__ . '/new/composerinstall.sh',
+ $baseEnv + [
+ // Variable used by composer itself, not our script
+ 'COMPOSER_HOME' => __DIR__ . '/composer',
+ 'REPO_TARGET' => $repos[$repo],
+ ]
+ );
+ if ( $error ) {
+ abandon( "Could not fetch dependencies for $repo" );
+ }
+ }
+}
+
+$mainPage = "\n\nThis wiki was updated on ~~~~~ with the following newer patches:";
+foreach ( $patchesToUpdate as $patch ) {
+ preg_match( '`([0-9]+),([0-9]+)`', $patch, $matches );
+ list( $t, $r, $p ) = $matches;
+
+ $data = gerrit_query( "changes/$r/revisions/$p/commit", true );
+ if ( $data ) {
+ $t = $t . ': ' . $data[ 'subject' ];
+ get_linked_tasks( $data['message'], $linkedTasks );
+ }
+
+ $t = htmlentities( $t );
+
+ $mainPage .= "\n:* [{$config['gerritUrl']}/r/c/$r/$p $t]";
+}
+
+$error = shell_echo( __DIR__ . '/new/postupdate.sh',
+ $baseEnv + [
+ 'MAINPAGE' => $mainPage,
+ ]
+);
+if ( $error ) {
+ abandon( "Could not update wiki content" );
+}
+
+// Update DB record with _all_ patches applied (include those which weren't updated)
+wiki_add_patches( $wiki, $patchesApplied );
+wiki_update_timestamp( $wiki );
+
+echo "Done!";
+
+echo '
';