diff --git a/bundles/org.eclipse.releng.tools/META-INF/MANIFEST.MF b/bundles/org.eclipse.releng.tools/META-INF/MANIFEST.MF index 8d8240e..27745a7 100644 --- a/bundles/org.eclipse.releng.tools/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.releng.tools/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %PluginName Bundle-SymbolicName: org.eclipse.releng.tools; singleton:=true -Bundle-Version: 4.2.0.qualifier +Bundle-Version: 4.2.100.qualifier Bundle-Activator: org.eclipse.releng.tools.RelEngPlugin Bundle-Vendor: %PluginProvider Bundle-Localization: plugin diff --git a/bundles/org.eclipse.releng.tools/pom.xml b/bundles/org.eclipse.releng.tools/pom.xml index fe2b1e4..29ff457 100644 --- a/bundles/org.eclipse.releng.tools/pom.xml +++ b/bundles/org.eclipse.releng.tools/pom.xml @@ -18,7 +18,7 @@ org.eclipse.releng org.eclipse.releng.tools - 4.2.0-SNAPSHOT + 4.2.100-SNAPSHOT eclipse-plugin true diff --git a/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/TouchBundles.java b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/TouchBundles.java new file mode 100644 index 0000000..348ba59 --- /dev/null +++ b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/TouchBundles.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2024 Andrey Loskutov and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Andrey Loskutov - initial API and implementation + *******************************************************************************/ +package org.eclipse.releng.tools; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +@SuppressWarnings("nls") +public class TouchBundles { + + final static String FQU_FILE = "forceQualifierUpdate.txt"; + static String releaseTag; + static String ticketMessage; + + /** + * Tries to map & touch all bundles that are mentioned in artifact + * comparisons. Automatically bumps versions if needed. + * + * @param args + *
    + *
  1. path to the extracted artifactcomparisons.zip + *
  2. path to the root directory with all SDK repositories + *
  3. last SDK release tag + *
  4. the ticket message (can be empty) + *
+ * @throws Exception + */ + public static void main(String[] args) throws Exception { + if (args.length != 4) { + System.out.println("Arguments: " + + "1) path to the extracted artifactcomparisons.zip, \n" + + "2) path to the root directory with all SDK repositories, \n" + + "3) last SDK release tag, like 'R4_31', \n" + + "4) the ticket message (can be empty)" + ); + System.exit(1); + } + Path artifactsPath = Paths.get(args[0]); + File[] badDirs = artifactsPath.toFile().listFiles(); + if (badDirs == null) { + System.out.println("No files found in " + artifactsPath); + System.exit(1); + } + + Path rootReposDir = Paths.get(args[1]); + File[] gitDirs = rootReposDir.toFile().listFiles(); + if (gitDirs == null) { + System.out.println("No files found in " + rootReposDir); + System.exit(1); + } + + releaseTag = args[2].strip(); + if (releaseTag.isBlank()) { + System.out.println("Last SDK release tag (like 'R4_31') is missing"); + System.exit(1); + } + + ticketMessage = args[3].strip(); + if (ticketMessage.isBlank()) { + ticketMessage = "Touching " + FQU_FILE + " to force bundle rebuild\n"; + } + + Map badDirMap = Arrays.asList(badDirs).stream().filter(File::isDirectory) + .collect(Collectors.toMap(File::getName, f -> f)); + + Map gitDirMap = Arrays.asList(gitDirs).stream().filter(File::isDirectory) + .collect(Collectors.toMap(File::getName, f -> f)); + + if (gitDirMap.containsKey("eclipse.pde.ui")) { + gitDirMap.put("eclipse.pde", gitDirMap.get("eclipse.pde.ui")); + } + if (gitDirMap.containsKey("eclipse.platform.releng.aggregator")) { + gitDirMap.put("eclipse.platform.releng", + new File(gitDirMap.get("eclipse.platform.releng.aggregator"), "eclipse.platform.releng")); + } + + for (Entry entry : badDirMap.entrySet()) { + String dirName = entry.getKey(); + File badDir = entry.getValue(); + File repoDir = gitDirMap.get(dirName); + if (repoDir == null) { + System.err.println("Repo " + dirName + " not found in " + rootReposDir); + continue; + } + + if (repoDir.isDirectory()) { + updateRepo(badDir, repoDir); + } else { + System.err.println("Repo " + repoDir + " is not a directory in " + rootReposDir); + } + } + } + + private static void updateRepo(File badDir, File repoDir) { + System.out.println("Checking " + badDir + " -> " + repoDir); + File[] dirs = badDir.listFiles(); + if (dirs == null) { + System.err.println("No children at " + badDir); + return; + } + for (File dir : dirs) { + File gitDir = new File(repoDir, dir.getName()); + if (isBundleWithChanges(dir)) { + updateFQU(gitDir); + } else { + updateRepo(new File(badDir, dir.getName()), gitDir); + } + } + } + + private static void updateFQU(File bundleRoot) { + if (!bundleRoot.isDirectory()) { + System.err.println("\tCan't update non existing directory " + bundleRoot); + return; + } + System.out.println("\tUpdating " + bundleRoot); + boolean versionBumped = VersionBump.run(bundleRoot.toPath(), releaseTag); + if (versionBumped) { + System.out.println("No need to update " + FQU_FILE); + return; + } + File fquFile = new File(bundleRoot, FQU_FILE); + try { + boolean created; + if (!fquFile.exists()) { + created = true; + Files.createFile(fquFile.toPath()); + } else { + created = false; + } + Path path = fquFile.toPath(); + String content = Files.readString(path); + if (content.endsWith(ticketMessage)) { + // already updated + System.out.println("\t\tAlready updated: " + fquFile); + return; + } + if (created) { + System.out.println("\t\tWill create new file: " + fquFile); + } else { + System.out.println("\t\tWill update file: " + fquFile); + } + if (content.endsWith("\n")) { + Files.write(path, ticketMessage.getBytes(), StandardOpenOption.APPEND); + } else { + Files.write(path, ("\n" + ticketMessage).getBytes(), StandardOpenOption.APPEND); + } + } catch (Exception e) { + System.err.println("Failed to update file " + fquFile); + } + } + + private static boolean isBundleWithChanges(File dir) { + return dir.isDirectory() && new File(dir, "target").isDirectory(); + } + + +} diff --git a/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/VersionBump.java b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/VersionBump.java new file mode 100644 index 0000000..85d45ad --- /dev/null +++ b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/VersionBump.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2024 Andrey Loskutov and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Andrey Loskutov - initial API and implementation + *******************************************************************************/ +package org.eclipse.releng.tools; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jgit.errors.RevisionSyntaxException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.osgi.framework.Version; + +@SuppressWarnings("nls") +public class VersionBump { + + /** + * Checks if the version bump needed for given bundle, and if needed, bumps + * micro segment in manifest and pom + * + * @param args first argument is bundle root path to check, second version SDK + * release tag like 'R4_31' + */ + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Arguments: first is path to the bundle root, \n" + "second is the release tag"); + System.exit(1); + } + run(Paths.get(args[0]), args[1]); + } + + /** + * + * @param bundleRoot + * @param releaseTag + * @return true if manifest and pom (if exists) were updated + */ + public static boolean run(Path bundleRoot, String releaseTag) { + if (!Files.isDirectory(bundleRoot)) { + System.err.println("Bundle root is not a directory: " + bundleRoot); + return false; + } + if (releaseTag == null || releaseTag.isBlank()) { + System.err.println("Release tag missing"); + return false; + } + + String manifestPath = "META-INF/MANIFEST.MF"; + String pomPath = "pom.xml"; + + String oldManifestContent = readFileAtTag(bundleRoot, manifestPath, releaseTag); + String oldTagVersion = getVersionFromManifest(oldManifestContent); + System.out.println("Version at tag " + releaseTag + ": " + oldTagVersion); + + String currentManifestContent = readCurrentFile(bundleRoot, manifestPath); + String currentVersion = getVersionFromManifest(currentManifestContent); + System.out.println("Version checked out : " + currentVersion); + + if (currentVersion != null && Objects.equals(oldTagVersion, currentVersion)) { + Version cv = new Version(currentVersion); + Version updated = new Version(cv.getMajor(), cv.getMinor(), cv.getMicro() + 100, cv.getQualifier()); + System.out.println("Update to: " + updated); + String newManifest = oldManifestContent.replace("Bundle-Version: " + oldTagVersion, + "Bundle-Version: " + updated); + + writeFile(bundleRoot, manifestPath, newManifest); + System.out.println("Updated META-INF/MANIFEST.MF in " + bundleRoot); + + String currentPomContent = readCurrentFile(bundleRoot, pomPath); + if (currentPomContent != null) { + String newPom = currentPomContent.replace("" + oldTagVersion, "" + updated); + writeFile(bundleRoot, pomPath, newPom); + System.out.println("Updated pom.xml in " + bundleRoot); + } + return true; + } else { + System.out.println("No version bump needed"); + return false; + } + + } + + private static void writeFile(Path bundleRoot, String manifestPath, String newManifest) { + try { + Files.write(bundleRoot.resolve(manifestPath), newManifest.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static String readCurrentFile(Path bundleRoot, String relativePath) { + try { + return Files.readString(bundleRoot.resolve(relativePath), StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private static Pattern VERSION_PATTERN = Pattern.compile(".*Bundle-Version: (\\d+\\.\\d+\\.\\d+).*", + Pattern.DOTALL); + + private static String getVersionFromManifest(String oldManifestContent) { + if (oldManifestContent == null) { + return null; + } + Matcher matcher = VERSION_PATTERN.matcher(oldManifestContent); + if (matcher.matches()) { + return matcher.group(1); + } + return null; + } + + private static String readFileAtTag(Path filePath, String fileInBundle, String releaseTag) { + try { + FileRepositoryBuilder repoBuilder = new FileRepositoryBuilder(); + repoBuilder.findGitDir(filePath.toFile()); + try (Repository repository = repoBuilder.build()) { + Path workDir = repository.getWorkTree().toPath(); + Path repoRelativePath = workDir.relativize(filePath.resolve(fileInBundle)); + String pathInGit = repoRelativePath.toString().replace('\\', '/'); + + ObjectId treeId = repository.resolve(releaseTag); + if (treeId == null) { + System.err.println("Unable to find tag " + releaseTag + " in repo " + workDir); + return null; + } + try (RevWalk revWalk = new RevWalk(repository)) { + RevCommit commit = revWalk.parseCommit(treeId); + System.out.println( + "Checking repo " + workDir + " at commit " + commit.getName() + " : '" + + commit.getShortMessage() + "'"); + TreeWalk treeWalk = TreeWalk.forPath(repository, pathInGit, commit.getTree()); + ObjectId blobId = treeWalk.getObjectId(0); + ObjectLoader objectLoader = loadObject(blobId, repository); + byte[] bytes = objectLoader.getBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } + } catch (IllegalStateException | RevisionSyntaxException | IOException e) { + e.printStackTrace(); + } + return null; + } + + private static ObjectLoader loadObject(ObjectId objectId, Repository repository) throws IOException { + try (ObjectReader objectReader = repository.newObjectReader()) { + return objectReader.open(objectId); + } + } +}