/*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.pombuilder;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Set;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sh.isaac.api.util.NumericUtils;
import sh.isaac.provider.sync.git.SyncServiceGIT;
import sh.isaac.provider.sync.git.gitblit.GitBlitUtils;
//~--- classes ----------------------------------------------------------------
/**
* {@link GitPublish}.
*
* @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
*/
public class GitPublish {
/** The Constant LOG. */
private static final Logger LOG = LogManager.getLogger();
//~--- methods -------------------------------------------------------------
/**
* Take in a URL such as https://git.isaac.sh/git/ or https://git.isaac.sh/git and turn it into
* https://git.isaac.sh/git/r/contentConfigurations.git
*
* If a full repo URL is passed in, such as https://git.isaac.sh/git/r/contentConfigurations.git, this does no processing
* and returns the passed in value.
*
* @param gitblitBaseURL a URL like https://git.isaac.sh/git
* @return the full git URL to a contentConfigurations repository.
* @throws IOException Signals that an I/O exception has occurred.
*/
public static String constructChangesetRepositoryURL(String gitblitBaseURL)
throws IOException {
if (gitblitBaseURL.matches("(?i)https?:\\/\\/[a-zA-Z0-9\\.\\-_]+:?\\d*\\/[a-zA-Z0-9\\-_]+\\/?$")) {
return gitblitBaseURL + (gitblitBaseURL.endsWith("/") ? ""
: "/") + "r/contentConfigurations.git";
} else if (
gitblitBaseURL.matches(
"(?i)https?:\\/\\/[a-zA-Z0-9\\.\\-_]+:?\\d*\\/[a-zA-Z0-9\\-_]+\\/r\\/[a-zA-Z0-9\\-_]+\\.git$")) {
return gitblitBaseURL;
} else {
LOG.info("Failing constructChangesetRepositoryURL {}", gitblitBaseURL);
throw new IOException("Unexpected gitblit server pattern");
}
}
/**
* Creates the repository if necessary.
*
* @param gitRepository the git repository
* @param gitUserName the git user name
* @param gitPassword the git password
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void createRepositoryIfNecessary(String gitRepository,
String gitUserName,
char[] gitPassword)
throws IOException {
final String baseUrl = GitBlitUtils.parseBaseRemoteAddress(gitRepository);
final Set<String> repos = GitBlitUtils.readRepositories(baseUrl, gitUserName, gitPassword);
final String repoName = gitRepository.substring(gitRepository.lastIndexOf("/") + 1);
if (!repos.contains(repoName)) {
LOG.info("Requested repository '" + gitRepository + "' does not exist - creating");
GitBlitUtils.createRepository(baseUrl,
repoName,
"Configuration Storage Repository",
gitUserName,
gitPassword,
true);
} else {
LOG.info("Requested repository '" + gitRepository + "' exists");
}
}
/**
* This routine will check out the project from the repository (which should have an empty master branch) - then locally
* commit the changes to master, then tag it - then push the tag (but not the changes to master) so the upstream repo only
* receives the tag.
*
* Calls {@link #constructChangesetRepositoryURL(String) to adjust the URL as necessary
*
* @param folderWithProject the folder with project
* @param gitRepository the git repository
* @param gitUserName the git user name
* @param gitPassword the git password
* @param tagToCreate the tag to create
* @throws Exception the exception
*/
public static void publish(File folderWithProject,
String gitRepository,
String gitUserName,
char[] gitPassword,
String tagToCreate)
throws Exception {
LOG.debug("Publishing '{}' to '{}' using tag '{}'",
folderWithProject.getAbsolutePath(),
gitRepository,
tagToCreate);
final String correctedURL = constructChangesetRepositoryURL(gitRepository);
createRepositoryIfNecessary(correctedURL, gitUserName, gitPassword);
final SyncServiceGIT svc = new SyncServiceGIT();
svc.setReadmeFileContent(
"ISAAC Dataprocessing Configuration Storage\n====\nIt is highly recommended you do not manually interact with this repository.");
svc.setGitIgnoreContent("");
final boolean ignoreExists = new File(folderWithProject, ".gitignore").exists();
final boolean readmeExists = new File(folderWithProject, "README.md").exists();
svc.setRootLocation(folderWithProject);
svc.linkAndFetchFromRemote(correctedURL, gitUserName, gitPassword);
svc.branch(folderWithProject.getName());
// linkAndFetch creates these in master, but I don't want them in my branch (if they didn't exist before I linked / fetched).
if (!ignoreExists) {
new File(folderWithProject, ".gitignore").delete();
}
if (!readmeExists) {
new File(folderWithProject, "README.md").delete();
}
svc.addUntrackedFiles();
svc.commitAndTag("publishing conversion project", tagToCreate);
svc.pushTag(tagToCreate, gitUserName, gitPassword);
// Notice, I do NOT push the updates to the branch
}
/**
* This will return -1 if no tag was found matching the tagWithoutRevNumber.
* This will return 0 if a tag was found matching the tagWithoutRefNumber (but no tag was found with a revision number)
* This will return X > 0 if one or more tags were found with a revision number - returning the highest value.
*
* @param existingTags the existing tags
* @param tagWithoutRevNumber the tag without rev number
* @return the int
*/
public static int readHighestRevisionNumber(ArrayList<String> existingTags, String tagWithoutRevNumber) {
int highestBuildRevision = -1;
for (final String s: existingTags) {
if (s.equals("refs/tags/" + tagWithoutRevNumber)) {
if (0 > highestBuildRevision) {
highestBuildRevision = 0;
}
} else if (s.startsWith("refs/tags/" + tagWithoutRevNumber + "-")) {
final String revNumber = s.substring(("refs/tags/" + tagWithoutRevNumber + "-").length(), s.length());
if (NumericUtils.isInt(revNumber)) {
final int parsed = Integer.parseInt(revNumber);
if (parsed > highestBuildRevision) {
highestBuildRevision = parsed;
}
}
}
}
return highestBuildRevision;
}
/**
* Calls {@link #constructChangesetRepositoryURL(String) to adjust the URL as necessary.
*
* @param gitRepository the git repository
* @param gitUserName the git user name
* @param gitPassword the git password
* @return the array list
* @throws Exception the exception
*/
public static ArrayList<String> readTags(String gitRepository,
String gitUserName,
char[] gitPassword)
throws Exception {
final String correctedURL = constructChangesetRepositoryURL(gitRepository);
createRepositoryIfNecessary(correctedURL, gitUserName, gitPassword);
final SyncServiceGIT svc = new SyncServiceGIT();
final File tempFolder = Files.createTempDirectory("tagRead")
.toFile();
svc.setRootLocation(tempFolder);
svc.linkAndFetchFromRemote(correctedURL, gitUserName, gitPassword);
final ArrayList<String> temp = svc.readTags(gitUserName, gitPassword);
try {
FileUtil.recursiveDelete(tempFolder);
} catch (final Exception e) {
LOG.error("Problem cleaning up temp folder " + tempFolder, e);
}
return temp;
}
}