将项目从StarTeam迁移到Bitbucket

时间:2016-09-08 19:14:16

标签: java git maven bitbucket starteam

我们在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下载到一个文件夹中

湾然后通过将上一步中下载的所有jar添加到CLASSPATH中来调用现有的ant脚本来构建项目

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

已经使用这种方法迁移了一些项目。

如果时间允许,我们可能会考虑在导入Bitbucket之前首先对项目进行全面复制(二级方法)。

问题

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

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

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

1 个答案:

答案 0 :(得分:0)

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

1)从StarTeam下载项目

2)我开发了一个JAVA实用程序,它将扫描项目工作区并将所有jar细节转储到excel表中。对于每个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();
}

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

查询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. 项目可能包含可能在Artifactory或MAVEN central中无法使用的自定义jar。在这种情况下,生成的excel表将与AD团队共享以获取相应的jar详细信息。 jar将安装在Artifactory中,AD团队将使用groupId,arfactId,version更新excel表。

  2. 一旦excel表具有所有jar详细信息,JAVA实用程序将通过读取相应的MAVEN详细信息生成pom.xml。 &#34; copy-rename-maven-plugin&#34;插件用于复制所有maven下载的jar,来自&#34; target / dependency&#34;文件夹到各个项目文件夹。 这个pom将被配置为Bamboo服务器中的第一个构建步骤,后面是build.xml,它将构建项目。注意我在构建过程中使用混合方法。

  3. 以下是相同

    的代码段
        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. 接下来的步骤是清理:它将删除项目中的所有罐子,并将跟踪罐子的位置。

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

    3. 用于创建项目的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
      

      用于将项目推送到Bitbucket的Git命令

      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. 下一步是竹子服务器和UrbanCode配置我手动做的竹子服务器没有暴露任何配置REST API。只有只读操作才有REST服务。
      2. 使用这种方法,我们实现了80%的迁移活动自动化。