Skip to content

Commit

Permalink
Merged in one-tag-at-a-time (pull request #1)
Browse files Browse the repository at this point in the history
Process one tag at a time.
  • Loading branch information
greg-1-anderson authored and Ryan Aslett committed Nov 21, 2019
2 parents e6aae03 + 3271eff commit 5d81a6f
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 57 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Ignore the configuration file
subtree-split.config
config.yml
config_secrets.yml

# Ignore the upstream repository
# Ignore the upstream repository caches
upstream
upstream-current
upstream-split-source

# Composer
vendor
Expand Down
2 changes: 2 additions & 0 deletions config.yml → config.yml.dist
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Copy this file to `config.yml` and customize to suit.

# This is the upstream project that we plan to split
project_to_split: drupal
# Github User
Expand Down
4 changes: 4 additions & 0 deletions config_secrets.yml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copy this file to `config_secrets.yml` and customize to suit.

github_api_token: 12345...
packagist_api_token: 12345...
2 changes: 1 addition & 1 deletion src/AppConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected function configure() {

$this->beginCommand('split')
->addArgument('branch', Argument::REQUIRED)
->addArgument('config', Argument::REQUIRED)
->addArgument('config', Argument::OPTIONAL)
->setHandler(new SplitCommand());
}

Expand Down
168 changes: 113 additions & 55 deletions src/SplitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,29 @@ class SplitCommand {
public function handle(Args $args, IO $io) {
$yaml = new Yaml();

$configfile = $args->getArgument('config');
$configfile = $this->getConfigFilePath($args);

$config_secrets = str_replace(".yml", "_secrets.yml", $configfile);
if ($content = file_get_contents($configfile)) {
$config = $yaml->parse($content);
}
if ($content = file_get_contents($config_secrets)) {
$config = array_merge($config, $yaml->parse($content));
}

$this->github_username = $config['github_username'];
$this->github_orgname = $config['github_orgname'];
$this->github_teamid = $config['github_teamid'];
$this->project = $config['project_to_split'];

$this->alterPath();

$this->upstream = "https://git.drupalcode.org/project/{$this->project}.git";


$packagist_api_client = new GuzzleClient(['base_uri' => 'https://packagist.org/api/']);
$directory = 'upstream-current';

$github_apikey = $config['github_api_token'];
$packagist_apitoken = $config['packagist_api_token'];

// Update/checkout local repository.
$this->updateRepository($directory, $args->getArgument('branch'));
Expand All @@ -59,6 +61,10 @@ public function handle(Args $args, IO $io) {
// That contains a composer.json is a split candidate.
//
$subtrees = $this->getSubtrees($directory);

// Make sure all 'core-' subtrees are ordered first.
uksort($subtrees, [$this, 'comparePackageNames']);

// For each subtree,
// 1. determine if a github repo exists for it
// 2. if not, create a github repo
Expand All @@ -75,68 +81,90 @@ public function handle(Args $args, IO $io) {
$paginator = new ResultPager($client);
$parameters = array($this->github_orgname);
$repositories = $paginator->fetchAll($userApi, 'repositories', $parameters);
$reponames = [];
foreach ($repositories as $repo) {
$reponames[] = $repo['name'];
}
$packagist_client = new PackagistClient();
foreach ($subtrees as $subtree_name => $subtree_data) {
$io->writeLine("-----------------------------------------------------");
$io->writeLine("Processing ${subtree_name}");
if (!in_array($subtree_name, $reponames)) {
$io->writeLine("Creating ${subtree_name}");
$client->api('repo')
->create($subtree_name, $subtree_data['description'], "http://drupal.org/project/{$this->project}", TRUE, $this->github_orgname, FALSE, FALSE, TRUE, $this->github_teamid, FALSE);
}
// Update branch.
$this->splitBranch($directory, $args->getArgument('branch'), $github_apikey, $subtree_data['path'], $subtree_name);
// Update tags.
exec('git ls-remote --tags ' . $this->upstream, $upstream_tags);
exec("git ls-remote --tags https://{$this->github_username}:{$github_apikey}@github.com/{$this->github_orgname}/{$subtree_name}.git", $downstream_tags);

$upstream_tags = Utility::filterValidTags($upstream_tags, $args->getArgument('branch'));
$downstream_tags = Utility::filterValidTags($downstream_tags, $args->getArgument('branch'));

// Tags which are not in the downstream repo.
$tags = array_diff($upstream_tags, $downstream_tags);
$walkdata = [
'directory' => $directory,
'name' => $subtree_name,
'path' => $subtree_data['path'],
'key' => $github_apikey,
];
array_walk($tags, function ($tag) use ($walkdata,$github_apikey) {
$this->splitTag($walkdata['directory'], $tag, $walkdata['key'], $walkdata['path'], $walkdata['name']);
});

// Check if package exists:
$packagename = 'drupal/' . $subtree_name;
$body = '{"repository":{"url":"git@github.com:' . $this->github_orgname . '/' . $subtree_name . '.git"}}';
try {
$packagist_client->get($packagename);
$endpoint = 'update-package';
}
catch (Exception $e) {
// Package doesnt exist at packagist: needs to be created.
$endpoint = 'create-package';
}

try {
$packagist_api_client->request('POST', $endpoint, [
'body' => $body,
'headers' => ['Content-Type' => 'application/json'],
'query' => [
'username' => "$this->github_username",
'apiToken' => $packagist_apitoken,
],
]);
$ref = $args->getArgument('branch');
if (preg_match('#\.x$#', $ref)) {
$io->writeLine("Update branch $ref");
// Update branch.
$this->splitBranch($directory, $ref, $github_apikey, $subtree_data['path'], $subtree_name);
}
catch (GuzzleException $e) {
// There. handled.
else {
$io->writeLine("Update tag $ref");
// Update tag.
$this->splitTag($directory, $ref, $github_apikey, $subtree_data['path'], $subtree_name);
}

$this->updatePackagist($config, $subtree_name);
}
passthru("rm -rf {$directory}");
}

protected function updatePackagist($config, $subtree_name) {
if (!isset($config['packagist_api_token'])) {
return;
}

$packagist_client = new PackagistClient();
$packagist_apitoken = $config['packagist_api_token'];

// Check if package exists:
$packagename = 'drupal/' . $subtree_name;
$body = '{"repository":{"url":"git@github.com:' . $this->github_orgname . '/' . $subtree_name . '.git"}}';
try {
$packagist_client->get($packagename);
$endpoint = 'update-package';
}
catch (Exception $e) {
// Package doesnt exist at packagist: needs to be created.
$endpoint = 'create-package';
}

$packagist_api_client = new GuzzleClient(['base_uri' => 'https://packagist.org/api/']);

try {
$packagist_api_client->request('POST', $endpoint, [
'body' => $body,
'headers' => ['Content-Type' => 'application/json'],
'query' => [
'username' => "$this->github_username",
'apiToken' => $packagist_apitoken,
],
]);
}
catch (GuzzleException $e) {
// There. handled.
}
}

protected function getConfigFilePath($args) {
return $args->getArgument('config')
?? $this->checkFileExists('config.yml')
?? 'config.yml.dist';
}

protected function checkFileExists($path) {
return file_exists($path) ? $path : NULL;
}

protected function alterPath() {
// We run whatever splitsh-lite we can find in the $PATH. Add the current
// directory to the end of the path so that we will always find the one
// at the project root if there isn't one already in the path.
$path = getenv('PATH');
putenv("PATH=$path:.");
}

protected function updateRepository($directory, $branch) {
if (!file_exists($directory)) {
passthru("git clone {$this->upstream} {$directory}");
Expand All @@ -146,15 +174,19 @@ protected function updateRepository($directory, $branch) {

protected function splitBranch($directory, $ref, $token, $prefix = 'core', $name = 'core') {
passthru("cd {$directory} && git checkout --force {$ref} && git reset --hard origin/{$ref}");
passthru("./splitsh-lite --progress --prefix={$prefix}/ --origin=origin/{$ref} --path={$directory} --target=HEAD");
passthru("splitsh-lite --progress --prefix={$prefix}/ --origin=origin/{$ref} --path={$directory} --target=HEAD");
passthru("cd {$directory} && git push https://{$this->github_username}:{$token}@github.com/{$this->github_orgname}/{$name}.git HEAD:{$ref}");
}

protected function splitTag($directory, $ref, $token, $prefix = 'core', $name = 'core') {
passthru("cd {$directory} && git fetch --tags");
passthru("./splitsh-lite --progress --prefix={$prefix}/ --origin=tags/{$ref} --path={$directory} --target=tags/{$ref}");
passthru("cd {$directory} && git push --delete https://{$this->github_username}:{$token}@github.com/{$this->github_orgname}/{$name}.git {$ref}");
passthru("cd {$directory} && git push https://{$this->github_username}:{$token}@github.com/{$this->github_orgname}/{$name}.git {$ref}");
$split_directory = 'upstream-split-source';
passthru("git clone $directory $split_directory");
passthru("cd {$split_directory} && git fetch --tags");
passthru("splitsh-lite --progress --prefix={$prefix}/ --origin=tags/{$ref} --path={$split_directory} --target=tags/{$ref}");
passthru("cd {$split_directory} && git push --delete https://{$this->github_username}:{$token}@github.com/{$this->github_orgname}/{$name}.git {$ref}");
passthru("cd {$split_directory} && git push https://{$this->github_username}:{$token}@github.com/{$this->github_orgname}/{$name}.git {$ref}");
passthru("cd {$split_directory} && git tag --delete $ref");
passthru("rm -rf {$split_directory}");
}

/**
Expand All @@ -174,10 +206,36 @@ protected function getSubtrees($directory) {
if ($file->getRelativePath() == "") {
continue;
};
$subtree_projectname = str_replace("drupal/", "", json_decode($file->getContents())->name);
$subtrees[$subtree_projectname] = ['path' => $file->getRelativePath(), 'description' => json_decode($file->getContents())->description];
$json = json_decode($file->getContents());
$subtree_projectname = str_replace("drupal/", "", $json->name);
$subtrees[$subtree_projectname] = ['path' => $file->getRelativePath(), 'description' => $json->description];
}
return $subtrees;
}

protected function comparePackageNames($a, $b) {
return strcasecmp($this->packageNameForComparison($a), $this->packageNameForComparison($b));
}

/**
* Prepends two spaces for 'core-' and one for 'core' itself.
*
* @param string $name
* Package name to compare.
*
* @return string
* Prefix to add before comparing.
*/
protected function packageNameForComparison($name) {
$name = trim($name);
$space = ' ';
if (substr($name, 0, 5) == 'core-') {
return "$space$space$name";
}
if (substr($name, 0, 4) == 'core') {
return "$space$name";
}
return $name;
}

}

0 comments on commit 5d81a6f

Please sign in to comment.