将项目从 StarTeam 迁移到 Bitbucket

Migrating projects from StarTeam to Bitbucket

我们在 StartTeam 中管理着大约 5000 个项目(各种技术)。客户端移动到新堆栈 Bitbucket、JFrog、Bamboo 和 UrbanCode。

Bitbucket: 它将用于源代码控制,底层 GIT 将用作版本控制系统。

JFrog: 将管理 maven 存储库的二进制文件。

Bamboo: 将用于构建服务器。

UrbanCode: 它将用于自动化代码部署过程。

我正在以 JAVA 项目作为我的问题的参考。目前 StartTeam 项目包含源代码以及所有必需的二进制文件,它不是 Maven 项目。 ANT 脚本用于项目构建。

要求以最小的努力将这样的项目迁移到 Bitbucket。 Bitbucket 不应包含任何二进制文件,它只会管理源代码。客户还设置了一个人工制品 JFrog,它将管理 maven 的二进制文件。

作为此迁移的一部分,我正在考虑一种混合方法,例如:

第 1 步:将从 StartTeam 下载项目。

第 2 步:所有二进制文件都将添加到新的 pom.xml 作为依赖项

第 3 步:代码将签入 Bibucket

第 4 步:在 bamboo 构建服务器中,构建将分两步进行配置

一个。首先,它会通过执行 pom.xml

将所有需要的 jar 下载到一个文件夹中

b。然后将调用现有的 ant 脚本来构建项目,方法是将上一步下载的所有 jar 添加到 CLASSPATH

第 5 步:UrbanCode 将配置为自动化部署过程

已经使用这种方法迁移了几个项目。

如果时间允许,我们可能会考虑在导入到 Bitbucket 之前首先完全对项目进行 mavenized(次要方法)。

问题

1) 大约有 5000 个项目需要迁移,所以我正在寻找专家建议如何继续使用方法 1(混合方法)?

2) 请建议是否有其他方法可以更轻松地完成此迁移?

3) 任何可以加速此迁移的工具?

最后,我通过以下方式尽可能地自动化了这个迁移过程:

1) 从 StarTeam 下载项目

2) 我开发了一个 JAVA 实用程序,它将扫描项目工作区并将所有 jar 详细信息转储到 excel sheet 中。对于每个 jar,它将使用以下代码计算校验和 (SHA-1)

public static String calculateChecksum(File javaJarFile) {

    String filepath = javaJarFile.getAbsolutePath();
    StringBuilder sb = new StringBuilder();
    FileInputStream fis = null;

    try {
        MessageDigest md = MessageDigest.getInstance("SHA-1"); //40 character checksum
        fis = new FileInputStream(filepath);
        byte[] dataBytes = new byte[1024];
        int nread = 0;

        while ((nread = fis.read(dataBytes)) != -1)
            md.update(dataBytes, 0, nread);

        byte[] mdbytes = md.digest();

        for (int i = 0; i < mdbytes.length; i++)
            sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16)
                    .substring(1));

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();

    } catch (IOException e) {
        e.printStackTrace();

    } finally {
        try {
            if (fis != null)
                fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return sb.toString();
}

然后它将使用校验和值查询 Artifactory 以获取匹配的 jar 详细信息,如 groupId、artifactId、version 等...如果在 Artifactory 中找不到 jar 详细信息,则它将查询 MAVEN 中央存储库以获取匹配的 jar细节。最后,所有现有项目 jar 详细信息和相应的 MAVEN 兼容 jar 详细信息将转储到 excel sheet.

查询MAVEN中央仓库的示例代码

        CloseableHttpClient httpClient = HttpClients.custom().build();
        HttpPost getRequest = new HttpPost("http://search.maven.org/solrsearch/select?q=1:<JAR CHECKSUM>&rows=1&wt=json");
        getRequest.addHeader("accept", "application/json");

        HttpResponse response = httpClient.execute(getRequest);

        if (response.getStatusLine().getStatusCode() != 200) {
            throw new RuntimeException("Failed : HTTP error code : "
                    + response.getStatusLine().getStatusCode());
        }

        BufferedReader br = new BufferedReader(new InputStreamReader(
                (response.getEntity().getContent())));

        String output;
        StringBuffer outputBuffer = new StringBuffer("");
        while ((output = br.readLine()) != null) {
            outputBuffer.append(output);
        }

        JSONObject jsonObj = new JSONObject(outputBuffer.toString());
        LOGGER.info("MAVEN Compatible Dependency Found: " + jsonObj.getJSONObject ("response").getInt("numFound"));

        if (jsonObj.getJSONObject ("response").getInt("numFound") > 1) {
            JSONArray jsonArray = jsonObj.getJSONObject ("response").getJSONArray("docs");
            JSONObject object = (JSONObject) jsonArray.get(0);
            LOGGER.info(object.getString("g"));
            LOGGER.info(object.getString("a"));
            LOGGER.info(object.getString("v"));
        }

查询Artifactory的示例代码

String checkSumUri = "https://anupg.jfrog.io/anupg/api/search/checksum?sha1=" + checkSum;
HttpResponse rtFactResponse = callService (checkSumUri, true);
BufferedReader br = new BufferedReader(new InputStreamReader((rtFactResponse.getEntity().getContent())));

String output;
StringBuffer outputBuffer = new StringBuffer("");
while ((output = br.readLine()) != null) {
    outputBuffer.append(output);
}

String uri = null;
if (!outputBuffer.toString().trim().equals("")) {
    JSONObject jsonObj = new JSONObject(outputBuffer.toString());
    JSONArray jsonArray = jsonObj.getJSONArray("results");

    for (int i=0; i < jsonArray.length(); i++) {
        JSONObject jsonUriObject = (JSONObject) jsonArray.get(i);
        System.out.println("URI---------->" + jsonUriObject.getString("uri").replace(".jar", ".pom"));
        uri = jsonUriObject.getString("uri").replace(".jar", ".pom");
    }
}

if (uri != null) {

    String downloadURI = null;
    HttpResponse uriResponse = callService (uri);

    BufferedReader uriBR = new BufferedReader(new InputStreamReader(
            (uriResponse.getEntity().getContent())));

    String uriOutput;
    StringBuffer uriOutputBuffer = new StringBuffer("");
    while ((uriOutput = uriBR.readLine()) != null) {
        uriOutputBuffer.append(uriOutput);
    }

    if (!uriOutputBuffer.toString().trim().equals("")) {
        JSONObject jsonUriObject = new JSONObject(uriOutputBuffer.toString());
        System.out.println("Download URI---------->" + jsonUriObject.getString("downloadUri"));
        downloadURI = jsonUriObject.getString("downloadUri");
    }

    HttpResponse downloadUriResponse = callService (downloadURI, true);

    if (downloadUriResponse.getStatusLine().getStatusCode() != 200) {
        throw new RuntimeException("Failed : HTTP error code : "
                + downloadUriResponse.getStatusLine().getStatusCode());
    }   

    InputStream is = downloadUriResponse.getEntity().getContent();
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    Document doc = null;

    try {
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        doc = dBuilder.parse(is);
        doc.getDocumentElement().normalize();
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } finally {
        if (is != null) {
            is.close();
        }
    }

    System.out.println("root of xml file: " + doc.getDocumentElement().getNodeName());
    Element element = doc.getDocumentElement();

    if (getNodeValue("groupId", element) != null && 
            getNodeValue("artifactId", element) != null && 
            getNodeValue("version", element) != null) {
        metadata.setGroupId(getNodeValue("groupId", element));
        metadata.setArtifactId(getNodeValue("artifactId", element));
        metadata.setVersion(getNodeValue("version", element));
        metadata.setRTfactoryFound(true);
    }
}
  1. 项目可能包含自定义 jar,这些 jar 在 Artifactory 或 MAVEN 中心可能不可用。在这种情况下,生成的 excel sheet 将与 AD 团队共享以获取相应的 jar 详细信息。该 jar 将安装在 Artifactory 中,AD 团队将使用组 ID、arfactId、版本更新 excel sheet。

  2. 一旦 excel sheet 拥有所有 jar 详细信息,JAVA 实用程序将通过读取相应的 MAVEN 详细信息生成 pom.xml。 "copy-rename-maven-plugin" 插件在这里用于将所有 Maven 下载的 jar 从 "target/dependency" 文件夹复制到相应的项目文件夹。 此 pom 将配置为 Bamboo 服务器中的第一个构建步骤,然后是将构建项目的 build.xml。注意我在构建过程中使用了混合方法。

下面是相同的代码片段

    Writer w = null;
    MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();

    try {
        List<JarDetails> uniquejarDetails = removeDuplicateJars (jarDetails);

        w = WriterFactory.newWriter (new File(location + "pom.xml"), "UTF-8");

        Model model = new Model();
        model.setGroupId("com.projectname");
        model.setArtifactId("project-analyzer");
        model.setVersion("1.0");
        model.setModelVersion("4.0.0");

        List<Dependency> dependencies = new ArrayList<Dependency>();            

        Plugin copyDependency = new Plugin();
        copyDependency.setGroupId("org.apache.maven.plugins");
        copyDependency.setArtifactId("maven-dependency-plugin");
        copyDependency.setVersion("2.10");

        PluginExecution copyDependencyPluginExecution = new PluginExecution();
        copyDependencyPluginExecution.setId("copy-dependencies");
        copyDependencyPluginExecution.setPhase("generate-sources");

        List<String> copyDependencyGoalsList = new ArrayList<String>();
        copyDependencyGoalsList.add("copy-dependencies");
        copyDependencyPluginExecution.setGoals(copyDependencyGoalsList);


        Plugin plugin = new Plugin();
        plugin.setGroupId("com.coderplus.maven.plugins");
        plugin.setArtifactId("copy-rename-maven-plugin");
        plugin.setVersion("1.0.1");

        PluginExecution pluginExecution = new PluginExecution();
        pluginExecution.setId("copy-jars");
        pluginExecution.setPhase("generate-sources");

        List<String> goalsList = new ArrayList<String>();
        goalsList.add("copy");
        pluginExecution.setGoals(goalsList);

        String domString = "<configuration><fileSets>";

        for (int jarCount = 0; jarCount < uniquejarDetails.size(); jarCount++) {
            if (uniquejarDetails.get(jarCount).getDependencyMetadata().getGroupId() != null) {

            Dependency dependency = new Dependency();

            dependency.setGroupId(uniquejarDetails.get(jarCount).getDependencyMetadata().getGroupId());
            dependency.setArtifactId(uniquejarDetails.get(jarCount).getDependencyMetadata().getArtifactId());
            dependency.setVersion(uniquejarDetails.get(jarCount).getDependencyMetadata().getVersion());
            dependencies.add(dependency);

            //Add copy-rename-maven-plugin configurations
            String mavenJarName = uniquejarDetails.get(jarCount).getDependencyMetadata().getArtifactId() + "-"
                    + uniquejarDetails.get(jarCount).getDependencyMetadata().getVersion() + ".jar";
            String mavenJar = "target/dependency/" + mavenJarName;

            domString += "<fileSet><sourceFile>" + mavenJar + "</sourceFile>";
            domString += "<destinationFile>" + new File(uniquejarDetails.get(jarCount).getJarRelativePath() + 
                    "/" + mavenJarName) + "</destinationFile></fileSet>";
            }
        }

        domString += "</fileSets></configuration>";

        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(domString));
        Xpp3Dom dom = Xpp3DomBuilder.build(new StringReader(domString));

        pluginExecution.setConfiguration(dom);

        List<PluginExecution> pluginExecuionList = new ArrayList<PluginExecution>();
        pluginExecuionList.add(pluginExecution);

        List<PluginExecution> copyDependencyPluginExecuionList = new ArrayList<PluginExecution>();
        copyDependencyPluginExecuionList.add(copyDependencyPluginExecution);

        plugin.setExecutions (pluginExecuionList);
        copyDependency.setExecutions (copyDependencyPluginExecuionList);

        List<Plugin> pluginList = new ArrayList<Plugin> ();
        pluginList.add(copyDependency);
        pluginList.add(plugin);

        Build build = new Build();
        build.setPlugins(pluginList);

        model.setDependencies(dependencies);
        model.setBuild(build);

        mavenXpp3Writer.write(w, model);

    } catch (UnsupportedEncodingException e) {
        LOGGER.error(e.getMessage(), e);

    } catch (FileNotFoundException e) {
        LOGGER.error(e.getMessage(), e);

    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);

    } catch (FactoryConfigurationError e) {
        e.printStackTrace();

    } catch (XmlPullParserException e) {
        e.printStackTrace();

    } finally {
        try {
            if (w != null)
                w.close();
        } catch (IOException e) {
            LOGGER.error (e.getMessage(), e);
        }
    }
  1. 下一步是清理:它将从项目中删除所有 jar,并将跟踪 jar 的位置。

  2. 创建了一个 shell 脚本,它将连接 bitbucket 服务器并使用 git 命令推送代码。我正在使用 "GIT Bash" 来执行脚本。请注意,在推送代码之前,我正在使用 Bitbucket rest API 远程创建项目和存储库。

用于创建项目的 REST 服务详细信息:

curl -X POST -v -u $bitbucket_user:${bitbucket_password} -H "Content-Type: application/json" "http://hostname:7990/rest/api/1.0/projects" -d "{\"key\": \"$project_key\",\"name\": \"$project_name\", \"description\": \"$project_desc\"}" > response.json

在上述项目下创建存储库的 REST 服务详细信息

curl -X POST -v -u $bitbucket_user:${bitbucket_password} -H "Content-Type: application/json" "http://hostname:7990/rest/api/1.0/projects/$project_key/repos" -d "{\"name\": \"$repository_name\", \"scmId\": \"git\", \"forkable\":true}" > repo-response.json

Git 将项目推送到 Bitbucket 的命令

git init
git add .
git commit -m "Initial commit"
git remote add origin  http://$bitbucket_user:${bitbucket_password}@hostname:7990/scm/${project_key}/${repository_name}.git
git push -u origin --all
  1. 下一步是 bamboo 服务器和 UrbanCode 配置,我正在手动进行配置,因为 bamboo 服务器不公开任何配置 REST API。 REST 服务仅用于只读操作。

使用这种方法,我们已经自动化了 80% 的迁移 activity。