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
+ *
+ * - path to the extracted artifactcomparisons.zip
+ *
- path to the root directory with all SDK repositories
+ *
- last SDK release tag
+ *
- 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);
+ }
+ }
+}