diff --git a/cli/diff-changelog.php b/cli/diff-changelog.php index 1d6551d..ae19727 100755 --- a/cli/diff-changelog.php +++ b/cli/diff-changelog.php @@ -1,215 +1,310 @@ #!/usr/bin/php setConduitToken( CONDUIT_API_TOKEN ); + // All the arguments must be Differential IDs $diff_ids = $argv; // Remove the first argument that is just the command name array_shift( $diff_ids ); -// no diff ID no party -if( !$diff_ids ) { - echo "Please specify at least one Differential ID like D66\n"; - exit( 1 ); -} +if( $diff_ids ) { -$client = new ConduitClient( PHABRICATOR_HOME ); -$client->setConduitToken( CONDUIT_API_TOKEN ); + // Get Diff IDs but in numeric form (for API requests) + $diff_ids_numeric = []; + foreach( $diff_ids as $diff_id ) { + $diff_ids_numeric[] = (int)ltrim( $diff_id, 'D' ); + } + + // Start from Diff IDs (numeric) and get Diff PHIDs (string) + // https://we.phorge.it/conduit/method/differential.revision.search/ + $diff_search_results = $client->callMethodSynchronous( 'differential.revision.search', [ + 'constraints' => [ + 'ids' => $diff_ids_numeric, + ], + ] ); + foreach( $diff_search_results['data'] as $diff_search_result ) { + $diff_phid = $diff_search_result['phid']; + $diff_phids[] = $diff_phid; + $diff_data_by_phid[ $diff_phid ] = $diff_search_result; + } + +} else { + + // If no Diff was specified, let's try guessing them, but that is a long journey + $git_commits_after_last_tag = []; + + // Find last git tag + // Thanks https://stackoverflow.com/a/12083016/3451846 + $git_last_tag = null; + exec( 'git describe --tags --abbrev=0', $git_last_tag ); + $git_last_tag = $git_last_tag[0] ?? null; + + // No tag, no party. + if( !$git_last_tag ) { + echo "Unable to find last git tag\n"; + exit( 1 ); + } + // Find git commits since last tag + $git_log_cmd = sprintf( + 'git log --pretty=format:%s %s..HEAD', + '%H', + escapeshellarg( $git_last_tag ) + ); + exec( $git_log_cmd, $git_commits_after_last_tag ); + + // No commits, no party. + if( !$git_commits_after_last_tag ) { + echo "Unable to list some git commits since last Tag.\n"; + echo "Please manually specify some Diff ID like D123 etc.\n"; + exit( 1 ); + } + +$git_commits_after_last_tag = [ + 'b47ebb358319c5c2967bb4caa0b0830fc3792144', + 'f4dc0a60f750fad499938ceaf0f3da8e8faf13da', +]; + + // Find PHID of each commit hash + $diffusion_commit_phids = []; + $diffusion_commit_search = $client->callMethodSynchronous( 'diffusion.commit.search', [ + 'constraints' => [ + 'identifiers' => $git_commits_after_last_tag, + ], + ] ); + foreach( $diffusion_commit_search['data'] as $diffusion_commit_data ) { + $diffusion_commit_phids[] = $diffusion_commit_data['phid']; + } + + // Find Differential revisions from git commits + // Start from commit hash and get Diff PHIDs + // https://we.phorge.it/conduit/method/edge.search/ + $commit_revisions = $client->callMethodSynchronous( 'edge.search', [ + 'sourcePHIDs' => $git_commits_after_last_tag, + 'types' => [ + 'commit.revision', + ], + ] ); + foreach( $commit_revisions['data'] as $commit_revision ) { + $diff_phid = $commit_revision['destinationPHID']; + $diff_phids[] = $diff_phid; + } +} + +// Initialize some stuff $tasks = []; $tasks_phid = []; - -// cache with users by phids +$tasks_phids_from_diff_phid = []; $USERS_BY_PHID = []; +// Start from Diff IDs and get Task PHIDs // https://gitpull.it/conduit/method/edge.search/ $edge_api_parameters = [ // apparently this does not support only PHIDs but also Monograms - 'sourcePHIDs' => $diff_ids, + 'sourcePHIDs' => $diff_phids, 'types' => [ 'revision.task' ], ]; -// find Tasks attached to Diff patch +// Get Task PHIDs from Differential Revision PHIDs +// https://we.phorge.it/conduit/method/edge.search/ $edge_result = $client->callMethodSynchronous( 'edge.search', $edge_api_parameters ); foreach( $edge_result['data'] as $data ) { - $tasks_phid[] = $data['destinationPHID']; + $diff_phid = $data['sourcePHID']; + $task_phid = $data['destinationPHID']; + $tasks_phid[] = $task_phid; + $tasks_phids_from_diff_phid[$diff_phid][] = $task_phid; +} + +// Show Diffs that have no Tasks so you can fix on Phorge +foreach( $diff_phids as $diff_phid ) { + if( empty( $tasks_phids_from_diff_phid[$diff_phid] ) ) { + echo "[WARN] Skipped Diff without Tasks: $diff_phid\n"; + } } // https://gitpull.it/conduit/method/maniphest.search/ $maniphest_api_parameters = [ 'constraints' => [ 'phids' => $tasks_phid, ], ]; // query Tasks info $maniphest_result = $client->callMethodSynchronous( 'maniphest.search', $maniphest_api_parameters ); foreach( $maniphest_result['data'] as $task ) { // append in known Tasks $tasks[] = $task; // remember User PHIDs since we will need to get their extra info $phid_task_author = $task['fields']['authorPHID']; $phid_task_owner = $task['fields']['ownerPHID']; $USERS_BY_PHID[ $phid_task_author ] = null; $USERS_BY_PHID[ $phid_task_owner ] = null; $phid_task_reporter = $task['fields'][PHABRICATOR_MANIPHEST_CUSTOM_FIELD_REPORTER] ?? null; if( $phid_task_reporter ) { $phid_task_reporter_entry = $phid_task_reporter[0]; $USERS_BY_PHID[ $phid_task_reporter_entry ] = null; } } // get users info from their PHID identifiers $users_phid = array_keys( $USERS_BY_PHID ); $users_api_parameters = [ 'constraints' => [ 'phids' => $users_phid, ], ]; $users_result = $client->callMethodSynchronous( 'user.search', $users_api_parameters ); foreach( $users_result['data'] as $user_data ) { $phid_user = $user_data['phid']; $USERS_BY_PHID[ $phid_user ] = $user_data; } // for each language foreach( $I18N as $lang => $msg ) { $changelog_blocks = []; // NOTE: Phabricator has custom fields that can be populated to retrieve the changelog // in the specified language $phab_maniphest_custom_field_changelog = sprintf( PHABRICATOR_MANIPHEST_CUSTOM_FIELD_CHANGELOG, $lang ); // for each Task foreach( $tasks as $task ) { $task_id = $task['id']; $task_name = $task['fields']['name']; $task_descr = $task['fields']['description']; $phid_task_author = $task['fields']['authorPHID']; $phid_task_owner = $task['fields']['ownerPHID']; $phid_reporter = $task['fields'][PHABRICATOR_MANIPHEST_CUSTOM_FIELD_REPORTER] ?? null; $author = $USERS_BY_PHID[ $phid_task_author ]; $owner = $USERS_BY_PHID[ $phid_task_owner ]; $reporter = null; if($phid_reporter) { $phid_reporter_entry = $phid_reporter[0]; $reporter = $USERS_BY_PHID[ $phid_reporter_entry ]; if($reporter) { $author = $reporter; } } $username_author = $author['fields']['username']; $username_owner = $owner ['fields']['username']; $realname_author = $author['fields']['realName'] ?? null; $realname_owner = $owner ['fields']['realName'] ?? null; $task_url = PHABRICATOR_HOME . "T{$task_id}"; $url_author = PHABRICATOR_HOME . 'p/' . $username_author; $url_owner = PHABRICATOR_HOME . 'p/' . $username_owner; // get the most appropriate changelog field or the Task name $changelog_title = $task['fields'][$phab_maniphest_custom_field_changelog] ?? $task_name; // just try to show something useful for a F-Droid changelog $changelog_lines = []; // Task name and URL $changelog_lines[] = $changelog_title; // reporter by (author) // Avoid to repeat the same user twice if( $username_author !== $username_owner ) { $changelog_lines[] = sprintf( $msg['reportedByName'], $username_author ); } // resolved by (owner) $changelog_lines[] = sprintf( $msg['resolvedByName'], $username_owner ); $changelog_lines[] = $task_url; $changelog_block = implode( "\n", $changelog_lines ); $changelog_blocks[] = $changelog_block; } // print all changelog blocks $changelog_content = implode( "\n\n", $changelog_blocks ); // expected changelog file $changelog_path = REPO_PATH . "/metadata/{$lang}/changelogs/{$version_code}.txt"; // save file_put_contents( $changelog_path, $changelog_content ); }