| // Copyright 2023 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'dart:io'; | |
| import 'package:cocoon_server/logging.dart'; | |
| import 'package:github/github.dart'; | |
| import 'git_cli.dart'; | |
| import 'utilities.dart'; | |
| class GitRepositoryManager { | |
| final RepositorySlug slug; | |
| final String workingDirectory; | |
| String? cloneToDirectory; | |
| final GitCli gitCli; | |
| late String targetCloneDirectory; | |
| /// RepositoryManager will perform clone, revert and delete on the repository | |
| /// in the working directory that is cloned to [cloneToDirectory]. | |
| /// | |
| /// If the clonedToDirectory is not provided then the name of the repository | |
| /// will be used as the cloneToDirectory. | |
| GitRepositoryManager({ | |
| required this.slug, | |
| //path/to/working/directory | |
| required this.workingDirectory, | |
| //reponame_commitSha | |
| this.cloneToDirectory, | |
| required this.gitCli, | |
| }) { | |
| cloneToDirectory ??= slug.name; | |
| targetCloneDirectory = '$workingDirectory/$cloneToDirectory'; | |
| } | |
| /// Clone the repository identified by the slug. | |
| /// | |
| /// Throw out rather than decomposing the return codes from the ProcessResult. | |
| Future<void> cloneRepository() async { | |
| if (Directory(targetCloneDirectory).existsSync()) { | |
| // Blow the directory away instead of trying to update it. | |
| Directory(targetCloneDirectory).deleteSync(recursive: true); | |
| } | |
| // Checking out a sparse copy will not checkout source files but will still | |
| // allow a revert since we only care about the commitSha. | |
| // Source: https://git-scm.com/docs/git-clone | |
| await gitCli.cloneRepository( | |
| slug: slug, | |
| workingDirectory: workingDirectory, | |
| targetDirectory: targetCloneDirectory, | |
| options: ['--sparse'], | |
| ); | |
| } | |
| Future<void> setupConfig() async { | |
| await gitCli.setupUserConfig( | |
| slug: slug, | |
| workingDirectory: targetCloneDirectory, | |
| ); | |
| await gitCli.setupUserEmailConfig( | |
| slug: slug, | |
| workingDirectory: targetCloneDirectory, | |
| ); | |
| } | |
| /// Revert a commit in the current repository. | |
| /// | |
| /// The [baseBranchName] is the branch we want to branch from. In this case it | |
| /// will almost always be the default branch name. The target branch is | |
| /// preformatted with the commitSha. | |
| Future<void> revertCommit( | |
| String baseBranchName, | |
| String commitSha, | |
| RepositorySlug slug, | |
| String token, | |
| ) async { | |
| final revertBranchName = GitRevertBranchName(commitSha); | |
| // Working directory for these must be repo checkout directory. | |
| // Check out the baseBranchName before doing anything. | |
| await gitCli.createBranch( | |
| newBranchName: revertBranchName.branch, | |
| workingDirectory: targetCloneDirectory, | |
| useCheckout: true, | |
| ); | |
| await gitCli.setUpstream( | |
| slug: slug, | |
| workingDirectory: targetCloneDirectory, | |
| branchName: revertBranchName.branch, | |
| token: token, | |
| ); | |
| await gitCli.revertChange( | |
| commitSha: commitSha, | |
| workingDirectory: targetCloneDirectory, | |
| ); | |
| await gitCli.pushBranch( | |
| branchName: revertBranchName.branch, | |
| workingDirectory: targetCloneDirectory, | |
| ); | |
| } | |
| /// Delete the repository managed by this instance. | |
| Future<void> deleteRepository() async { | |
| log.info('Deleting clone directory $targetCloneDirectory'); | |
| if (Directory(targetCloneDirectory).existsSync()) { | |
| Directory(targetCloneDirectory).deleteSync(recursive: true); | |
| } | |
| } | |
| } |