我们在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)任何可以加速迁移的工具?
答案 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);
}
}
项目可能包含可能在Artifactory或MAVEN central中无法使用的自定义jar。在这种情况下,生成的excel表将与AD团队共享以获取相应的jar详细信息。 jar将安装在Artifactory中,AD团队将使用groupId,arfactId,version更新excel表。
一旦excel表具有所有jar详细信息,JAVA实用程序将通过读取相应的MAVEN详细信息生成pom.xml。 &#34; copy-rename-maven-plugin&#34;插件用于复制所有maven下载的jar,来自&#34; target / dependency&#34;文件夹到各个项目文件夹。 这个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);
}
}
接下来的步骤是清理:它将删除项目中的所有罐子,并将跟踪罐子的位置。
创建了一个shell脚本,它将连接bitbucket服务器并使用git命令推送代码。我正在使用&#34; GIT Bash&#34;执行脚本。注意,我在推送代码之前使用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
用于将项目推送到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
使用这种方法,我们实现了80%的迁移活动自动化。