mirror of https://github.com/jenkinsci/jenkins.git
[JENKINS-75465] Delete RunIdMigrator as it has been 10 years since this migration (#10456)
This commit is contained in:
commit
dd0d68d5e4
|
@ -97,7 +97,6 @@ import jenkins.model.JenkinsLocationConfiguration;
|
|||
import jenkins.model.ModelObjectWithChildren;
|
||||
import jenkins.model.PeepholePermalink;
|
||||
import jenkins.model.ProjectNamingStrategy;
|
||||
import jenkins.model.RunIdMigrator;
|
||||
import jenkins.model.lazy.LazyBuildMixIn;
|
||||
import jenkins.scm.RunWithSCM;
|
||||
import jenkins.security.HexStringConfidentialKey;
|
||||
|
@ -192,10 +191,6 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
|
|||
// this should have been DescribableList but now it's too late
|
||||
protected CopyOnWriteList<JobProperty<? super JobT>> properties = new CopyOnWriteList<>();
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
@SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Preserve API compatibility")
|
||||
public transient RunIdMigrator runIdMigrator;
|
||||
|
||||
protected Job(ItemGroup parent, String name) {
|
||||
super(parent, name);
|
||||
}
|
||||
|
@ -206,20 +201,19 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
|
|||
holdOffBuildUntilSave = holdOffBuildUntilUserSave;
|
||||
}
|
||||
|
||||
@Override public void onCreatedFromScratch() {
|
||||
super.onCreatedFromScratch();
|
||||
runIdMigrator = new RunIdMigrator();
|
||||
runIdMigrator.created(getBuildDir());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(ItemGroup<? extends Item> parent, String name)
|
||||
throws IOException {
|
||||
super.onLoad(parent, name);
|
||||
|
||||
File buildDir = getBuildDir();
|
||||
runIdMigrator = new RunIdMigrator();
|
||||
runIdMigrator.migrate(buildDir, Jenkins.get().getRootDir());
|
||||
// see https://github.com/jenkinsci/jenkins/pull/10456#issuecomment-2748112449
|
||||
// This code can be deleted after several Jenkins releases,
|
||||
// when it is likely that everyone is running a version equal or higher to this version.
|
||||
var buildDirPath = getBuildDir().toPath();
|
||||
if (Files.deleteIfExists(buildDirPath.resolve("legacyIds"))) {
|
||||
LOGGER.info("Deleting legacyIds file in " + buildDirPath + ". See https://issues.jenkins"
|
||||
+ ".io/browse/JENKINS-75465 for more information.");
|
||||
}
|
||||
|
||||
TextFile f = getNextBuildNumberFile();
|
||||
if (f.exists()) {
|
||||
|
|
|
@ -45,10 +45,8 @@ import java.util.Objects;
|
|||
import java.util.SortedMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.model.RunIdMigrator;
|
||||
import jenkins.model.lazy.AbstractLazyLoadRunMap;
|
||||
import jenkins.model.lazy.BuildReference;
|
||||
import jenkins.model.lazy.LazyBuildMixIn;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
|
@ -72,10 +70,6 @@ public final class RunMap<R extends Run<?, R>> extends AbstractLazyLoadRunMap<R>
|
|||
|
||||
private Constructor<R> cons;
|
||||
|
||||
/** Normally overwritten by {@link LazyBuildMixIn#onLoad} or {@link LazyBuildMixIn#onCreatedFromScratch}, in turn created during {@link Job#onLoad}. */
|
||||
@Restricted(NoExternalUse.class)
|
||||
public RunIdMigrator runIdMigrator = new RunIdMigrator();
|
||||
|
||||
// TODO: before first complete build
|
||||
// patch up next/previous build link
|
||||
|
||||
|
@ -156,7 +150,6 @@ public final class RunMap<R extends Run<?, R>> extends AbstractLazyLoadRunMap<R>
|
|||
@Override
|
||||
public boolean removeValue(R run) {
|
||||
run.dropLinks();
|
||||
runIdMigrator.delete(dir, run.getId());
|
||||
return super.removeValue(run);
|
||||
}
|
||||
|
||||
|
@ -227,14 +220,13 @@ public final class RunMap<R extends Run<?, R>> extends AbstractLazyLoadRunMap<R>
|
|||
return super._put(r);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override public R getById(String id) {
|
||||
int n;
|
||||
try {
|
||||
n = Integer.parseInt(id);
|
||||
} catch (NumberFormatException x) {
|
||||
n = runIdMigrator.findNumber(id);
|
||||
return getByNumber(Integer.parseInt(id));
|
||||
} catch (NumberFormatException e) { // see https://issues.jenkins.io/browse/JENKINS-75476
|
||||
return null;
|
||||
}
|
||||
return getByNumber(n);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Jesse Glick.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.FINER;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Util;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Run;
|
||||
import hudson.util.AtomicFileWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
/**
|
||||
* Converts legacy {@code builds} directories to the current format.
|
||||
*
|
||||
* There would be one instance associated with each {@link Job}, to retain ID → build# mapping.
|
||||
*
|
||||
* The {@link Job#getBuildDir} is passed to every method call (rather than being cached) in case it is moved.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public final class RunIdMigrator {
|
||||
|
||||
private final DateFormat legacyIdFormatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
|
||||
|
||||
static final Logger LOGGER = Logger.getLogger(RunIdMigrator.class.getName());
|
||||
private static final String MAP_FILE = "legacyIds";
|
||||
/** avoids wasting a map for new jobs */
|
||||
private static final Map<String, Integer> EMPTY = new TreeMap<>();
|
||||
|
||||
private @NonNull Map<String, Integer> idToNumber = EMPTY;
|
||||
|
||||
public RunIdMigrator() {}
|
||||
|
||||
/**
|
||||
* @return whether there was a file to load
|
||||
*/
|
||||
private boolean load(File dir) {
|
||||
File f = new File(dir, MAP_FILE);
|
||||
if (!f.isFile()) {
|
||||
return false;
|
||||
}
|
||||
if (f.length() == 0) {
|
||||
return true;
|
||||
}
|
||||
idToNumber = new TreeMap<>();
|
||||
try {
|
||||
for (String line : Files.readAllLines(Util.fileToPath(f), StandardCharsets.UTF_8)) {
|
||||
int i = line.indexOf(' ');
|
||||
idToNumber.put(line.substring(0, i), Integer.parseInt(line.substring(i + 1)));
|
||||
}
|
||||
} catch (Exception x) { // IOException, IndexOutOfBoundsException, NumberFormatException
|
||||
LOGGER.log(WARNING, "could not read from " + f, x);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void save(File dir) {
|
||||
File f = new File(dir, MAP_FILE);
|
||||
try (AtomicFileWriter w = new AtomicFileWriter(f)) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
for (Map.Entry<String, Integer> entry : idToNumber.entrySet()) {
|
||||
w.write(entry.getKey() + ' ' + entry.getValue() + '\n');
|
||||
}
|
||||
}
|
||||
w.commit();
|
||||
} finally {
|
||||
w.abort();
|
||||
}
|
||||
} catch (IOException x) {
|
||||
LOGGER.log(WARNING, "could not save changes to " + f, x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a job is first created.
|
||||
* Just saves an empty marker indicating that this job needs no migration.
|
||||
* @param dir as in {@link Job#getBuildDir}
|
||||
*/
|
||||
public void created(File dir) {
|
||||
save(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform one-time migration if this has not been done already.
|
||||
* Where previously there would be a {@code 2014-01-02_03-04-05/build.xml} specifying {@code <number>99</number>} plus a symlink {@code 99 → 2014-01-02_03-04-05},
|
||||
* after migration there will be just {@code 99/build.xml} specifying {@code <id>2014-01-02_03-04-05</id>} and {@code <timestamp>…</timestamp>} according to local time zone at time of migration.
|
||||
* Newly created builds are untouched.
|
||||
* Does not throw {@link IOException} since we make a best effort to migrate but do not consider it fatal to job loading if we cannot.
|
||||
* @param dir as in {@link Job#getBuildDir}
|
||||
* @param jenkinsHome root directory of Jenkins (for logging only)
|
||||
* @return true if migration was performed
|
||||
*/
|
||||
public synchronized boolean migrate(File dir, @CheckForNull File jenkinsHome) {
|
||||
if (load(dir)) {
|
||||
LOGGER.log(FINER, "migration already performed for {0}", dir);
|
||||
return false;
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
LOGGER.log(/* normal during Job.movedTo */FINE, "{0} was unexpectedly missing", dir);
|
||||
return false;
|
||||
}
|
||||
LOGGER.log(INFO, "Migrating build records in {0}. See https://www.jenkins.io/redirect/build-record-migration for more information.", dir);
|
||||
doMigrate(dir);
|
||||
save(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final Pattern NUMBER_ELT = Pattern.compile("(?m)^ <number>(\\d+)</number>(\r?\n)");
|
||||
|
||||
private void doMigrate(File dir) {
|
||||
idToNumber = new TreeMap<>();
|
||||
File[] kids = dir.listFiles();
|
||||
// Need to process symlinks first so we can rename to them.
|
||||
List<File> kidsList = new ArrayList<>(Arrays.asList(kids));
|
||||
Iterator<File> it = kidsList.iterator();
|
||||
while (it.hasNext()) {
|
||||
File kid = it.next();
|
||||
String name = kid.getName();
|
||||
try {
|
||||
Integer.parseInt(name);
|
||||
} catch (NumberFormatException x) {
|
||||
LOGGER.log(FINE, "ignoring nonnumeric entry {0}", name);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (Util.isSymlink(kid)) {
|
||||
LOGGER.log(FINE, "deleting build number symlink {0} → {1}", new Object[] {name, Util.resolveSymlink(kid)});
|
||||
} else if (kid.isDirectory()) {
|
||||
LOGGER.log(FINE, "ignoring build directory {0}", name);
|
||||
continue;
|
||||
} else {
|
||||
LOGGER.log(WARNING, "need to delete anomalous file entry {0}", name);
|
||||
}
|
||||
Util.deleteFile(kid);
|
||||
it.remove();
|
||||
} catch (Exception x) {
|
||||
LOGGER.log(WARNING, "failed to process " + kid, x);
|
||||
}
|
||||
}
|
||||
it = kidsList.iterator();
|
||||
while (it.hasNext()) {
|
||||
File kid = it.next();
|
||||
try {
|
||||
String name = kid.getName();
|
||||
try {
|
||||
Integer.parseInt(name);
|
||||
LOGGER.log(FINE, "skipping new build dir {0}", name);
|
||||
continue;
|
||||
} catch (NumberFormatException x) {
|
||||
// OK, next…
|
||||
}
|
||||
if (!kid.isDirectory()) {
|
||||
LOGGER.log(FINE, "skipping non-directory {0}", name);
|
||||
continue;
|
||||
}
|
||||
long timestamp;
|
||||
try {
|
||||
synchronized (legacyIdFormatter) {
|
||||
timestamp = legacyIdFormatter.parse(name).getTime();
|
||||
}
|
||||
} catch (ParseException x) {
|
||||
LOGGER.log(WARNING, "found unexpected dir {0}", name);
|
||||
continue;
|
||||
}
|
||||
File buildXml = new File(kid, "build.xml");
|
||||
if (!buildXml.isFile()) {
|
||||
LOGGER.log(WARNING, "found no build.xml in {0}", name);
|
||||
continue;
|
||||
}
|
||||
String xml = Files.readString(Util.fileToPath(buildXml), StandardCharsets.UTF_8);
|
||||
Matcher m = NUMBER_ELT.matcher(xml);
|
||||
if (!m.find()) {
|
||||
LOGGER.log(WARNING, "could not find <number> in {0}/build.xml", name);
|
||||
continue;
|
||||
}
|
||||
int number = Integer.parseInt(m.group(1));
|
||||
String nl = m.group(2);
|
||||
xml = m.replaceFirst(" <id>" + name + "</id>" + nl + " <timestamp>" + timestamp + "</timestamp>" + nl);
|
||||
File newKid = new File(dir, Integer.toString(number));
|
||||
move(kid, newKid);
|
||||
Files.writeString(Util.fileToPath(newKid).resolve("build.xml"), xml, StandardCharsets.UTF_8);
|
||||
LOGGER.log(FINE, "fully processed {0} → {1}", new Object[] {name, number});
|
||||
idToNumber.put(name, number);
|
||||
} catch (Exception x) {
|
||||
LOGGER.log(WARNING, "failed to process " + kid, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to move/rename a file from one path to another.
|
||||
* Uses {@link java.nio.file.Files#move} when available.
|
||||
* Does not use {@link java.nio.file.StandardCopyOption#REPLACE_EXISTING} or any other options.
|
||||
* TODO candidate for moving to {@link Util}
|
||||
*/
|
||||
static void move(File src, File dest) throws IOException {
|
||||
try {
|
||||
Files.move(src.toPath(), dest.toPath());
|
||||
} catch (IOException x) {
|
||||
throw x;
|
||||
} catch (RuntimeException x) {
|
||||
throw new IOException(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a historical run by ID.
|
||||
* @param id a nonnumeric ID which may be a valid {@link Run#getId}
|
||||
* @return the corresponding {@link Run#number}, or 0 if unknown
|
||||
*/
|
||||
public synchronized int findNumber(@NonNull String id) {
|
||||
Integer number = idToNumber.get(id);
|
||||
return number != null ? number : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the record of a build.
|
||||
* @param dir as in {@link Job#getBuildDir}
|
||||
* @param id a {@link Run#getId}
|
||||
*/
|
||||
public synchronized void delete(File dir, String id) {
|
||||
if (idToNumber.remove(id) != null) {
|
||||
save(dir);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,7 +49,6 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.model.RunIdMigrator;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
|
||||
|
@ -147,9 +146,6 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT, RunT> & Queue.Task &
|
|||
return loadBuild(dir);
|
||||
}
|
||||
});
|
||||
RunIdMigrator runIdMigrator = asJob().runIdMigrator;
|
||||
assert runIdMigrator != null;
|
||||
r.runIdMigrator = runIdMigrator;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2014, CloudBees, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
|
||||
<l:layout title="Jenkins">
|
||||
<l:header />
|
||||
<l:main-panel>
|
||||
<p>
|
||||
To reverse the effect of build record migration, run the following command
|
||||
on the server. See <a href="https://www.jenkins.io/redirect/build-record-migration">documentation</a>
|
||||
for more details:
|
||||
</p>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<td style="line-height:2em; width:80%">
|
||||
<input type="text" value="${it.command}" style="width:100%"/>
|
||||
</td>
|
||||
<td style="line-height:2em">
|
||||
<l:copyButton text="${it.command}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
</j:jelly>
|
|
@ -1,24 +0,0 @@
|
|||
# The MIT License
|
||||
#
|
||||
# Bulgarian translation: Copyright (c) 2016, Alexander Shopov <ash@kambanaria.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
Copied=\
|
||||
Копирано
|
|
@ -1,23 +0,0 @@
|
|||
# The MIT License
|
||||
#
|
||||
# Copyright (c) 2017 Daniel Beck and a number of other of contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
Copied=Kopiert
|
|
@ -1,24 +0,0 @@
|
|||
# The MIT License
|
||||
#
|
||||
# Italian localization plugin for Jenkins
|
||||
# Copyright © 2020 Alessandro Menti
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
Copied=Copiato
|
|
@ -1,23 +0,0 @@
|
|||
# The MIT License
|
||||
#
|
||||
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number
|
||||
# of other of contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# This file is under the MIT License by authors
|
||||
|
||||
Copied=Ископирано
|
|
@ -1,3 +0,0 @@
|
|||
# This file is under the MIT License by authors
|
||||
|
||||
Copied=Kopierades
|
|
@ -1,23 +0,0 @@
|
|||
# The MIT License
|
||||
#
|
||||
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
Copied=已複製
|
|
@ -112,7 +112,6 @@
|
|||
<Class name="jenkins.fingerprints.FingerprintStorage"/>
|
||||
<Class name="jenkins.install.SetupWizard"/>
|
||||
<Class name="jenkins.model.Jenkins"/>
|
||||
<Class name="jenkins.model.RunIdMigrator"/>
|
||||
<Class name="jenkins.mvn.SettingsPathHelper"/>
|
||||
<Class name="jenkins.security.CustomClassFilter$Contributed"/>
|
||||
<Class name="jenkins.security.ResourceDomainConfiguration"/>
|
||||
|
|
|
@ -1,319 +0,0 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Jesse Glick.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
|
||||
import hudson.Functions;
|
||||
import hudson.Util;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
public class RunIdMigratorTest {
|
||||
|
||||
@Rule public TemporaryFolder tmp = new TemporaryFolder();
|
||||
|
||||
private static TimeZone defaultTimezone;
|
||||
|
||||
/** Ensures that legacy timestamps are interpreted in a predictable time zone. */
|
||||
@BeforeClass public static void timezone() {
|
||||
defaultTimezone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
|
||||
}
|
||||
|
||||
@AfterClass public static void tearDown() {
|
||||
TimeZone.setDefault(defaultTimezone);
|
||||
}
|
||||
|
||||
// TODO could use LoggerRule only if it were extracted to an independent library
|
||||
@BeforeClass public static void logging() {
|
||||
RunIdMigrator.LOGGER.setLevel(Level.ALL);
|
||||
Handler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.ALL);
|
||||
RunIdMigrator.LOGGER.addHandler(handler);
|
||||
}
|
||||
|
||||
private RunIdMigrator migrator;
|
||||
private File dir;
|
||||
|
||||
@Before public void init() {
|
||||
migrator = new RunIdMigrator();
|
||||
dir = tmp.getRoot();
|
||||
}
|
||||
|
||||
@Test public void newJob() throws Exception {
|
||||
migrator.created(dir);
|
||||
assertEquals("{legacyIds=''}", summarize());
|
||||
assertEquals(0, migrator.findNumber("whatever"));
|
||||
migrator.delete(dir, "1");
|
||||
migrator = new RunIdMigrator();
|
||||
assertFalse(migrator.migrate(dir, null));
|
||||
assertEquals("{legacyIds=''}", summarize());
|
||||
}
|
||||
|
||||
@Test public void legacy() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
write(
|
||||
"2014-01-02_03-04-05/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <number>99</number>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
link("99", "2014-01-02_03-04-05");
|
||||
link("lastFailedBuild", "-1");
|
||||
link("lastSuccessfulBuild", "99");
|
||||
assertEquals(
|
||||
"{2014-01-02_03-04-05={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <number>99</number>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, 99=→2014-01-02_03-04-05, lastFailedBuild=→-1, lastSuccessfulBuild=→99}",
|
||||
summarize());
|
||||
assertTrue(migrator.migrate(dir, null));
|
||||
assertEquals(
|
||||
"{99={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <id>2014-01-02_03-04-05</id>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, lastFailedBuild=→-1, lastSuccessfulBuild=→99, legacyIds='2014-01-02_03-04-05 99\n"
|
||||
+ "'}",
|
||||
summarize());
|
||||
assertEquals(99, migrator.findNumber("2014-01-02_03-04-05"));
|
||||
migrator = new RunIdMigrator();
|
||||
assertFalse(migrator.migrate(dir, null));
|
||||
assertEquals(99, migrator.findNumber("2014-01-02_03-04-05"));
|
||||
migrator.delete(dir, "2014-01-02_03-04-05");
|
||||
FileUtils.deleteDirectory(new File(dir, "99"));
|
||||
new File(dir, "lastSuccessfulBuild").delete();
|
||||
assertEquals("{lastFailedBuild=→-1, legacyIds=''}", summarize());
|
||||
}
|
||||
|
||||
@Test public void reRunMigration() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
write("2014-01-02_03-04-04/build.xml", "<run>\n <number>98</number>\n</run>");
|
||||
link("98", "2014-01-02_03-04-04");
|
||||
write(
|
||||
"99/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
link("lastFailedBuild", "-1");
|
||||
link("lastSuccessfulBuild", "99");
|
||||
assertEquals(
|
||||
"{2014-01-02_03-04-04={build.xml='<run>\n"
|
||||
+ " <number>98</number>\n"
|
||||
+ "</run>'}, 98=→2014-01-02_03-04-04, 99={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, lastFailedBuild=→-1, lastSuccessfulBuild=→99}",
|
||||
summarize());
|
||||
assertTrue(migrator.migrate(dir, null));
|
||||
assertEquals(
|
||||
"{98={build.xml='<run>\n"
|
||||
+ " <id>2014-01-02_03-04-04</id>\n"
|
||||
+ " <timestamp>1388649844000</timestamp>\n"
|
||||
+ "</run>'}, 99={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, lastFailedBuild=→-1, lastSuccessfulBuild=→99, legacyIds='2014-01-02_03-04-04 98\n"
|
||||
+ "'}",
|
||||
summarize());
|
||||
}
|
||||
|
||||
@Test public void reverseImmediately() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
File root = dir;
|
||||
dir = new File(dir, "jobs/somefolder/jobs/someproject/promotions/OK/builds");
|
||||
write(
|
||||
"99/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <id>2014-01-02_03-04-05</id>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
link("lastFailedBuild", "-1");
|
||||
link("lastSuccessfulBuild", "99");
|
||||
write("legacyIds", "2014-01-02_03-04-05 99\n");
|
||||
assertEquals(
|
||||
"{99={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <id>2014-01-02_03-04-05</id>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, lastFailedBuild=→-1, lastSuccessfulBuild=→99, legacyIds='2014-01-02_03-04-05 99\n"
|
||||
+ "'}",
|
||||
summarize());
|
||||
}
|
||||
|
||||
@Test public void reverseAfterNewBuilds() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
File root = dir;
|
||||
dir = new File(dir, "jobs/someproject/modules/test$test/builds");
|
||||
write(
|
||||
"1/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
write("legacyIds", "");
|
||||
assertEquals(
|
||||
"{1={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, legacyIds=''}",
|
||||
summarize());
|
||||
}
|
||||
|
||||
@Test public void reverseMatrixAfterNewBuilds() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
File root = dir;
|
||||
dir = new File(dir, "jobs/someproject/Environment=prod/builds");
|
||||
write(
|
||||
"1/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
write("legacyIds", "");
|
||||
assertEquals(
|
||||
"{1={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, legacyIds=''}",
|
||||
summarize());
|
||||
}
|
||||
|
||||
@Test public void reverseMavenAfterNewBuilds() throws Exception {
|
||||
assumeFalse("Symlinks don't work well on Windows", Functions.isWindows());
|
||||
File root = dir;
|
||||
dir = new File(dir, "jobs/someproject/test$test/builds");
|
||||
write(
|
||||
"1/build.xml",
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>");
|
||||
write("legacyIds", "");
|
||||
assertEquals(
|
||||
"{1={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
+ "<run>\n"
|
||||
+ " <stuff>ok</stuff>\n"
|
||||
+ " <timestamp>1388649845000</timestamp>\n"
|
||||
+ " <otherstuff>ok</otherstuff>\n"
|
||||
+ "</run>'}, legacyIds=''}",
|
||||
summarize());
|
||||
}
|
||||
|
||||
// TODO test sane recovery from various error conditions
|
||||
|
||||
private void write(String file, String text) throws Exception {
|
||||
Path path = new File(dir, file).toPath();
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.writeString(path, text, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
private void link(String symlink, String dest) throws Exception {
|
||||
Util.createSymlink(dir, dest, symlink, new StreamTaskListener(System.out, Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
private String summarize() throws Exception {
|
||||
return summarize(dir);
|
||||
}
|
||||
|
||||
private static String summarize(File dir) throws Exception {
|
||||
File[] kids = dir.listFiles();
|
||||
Map<String, String> m = new TreeMap<>();
|
||||
for (File kid : kids) {
|
||||
String notation;
|
||||
String symlink = Util.resolveSymlink(kid);
|
||||
if (symlink != null) {
|
||||
notation = "→" + symlink;
|
||||
} else if (kid.isFile()) {
|
||||
notation = "'" + Files.readString(kid.toPath(), Charset.defaultCharset()) + "'";
|
||||
} else if (kid.isDirectory()) {
|
||||
notation = summarize(kid);
|
||||
} else {
|
||||
notation = "?";
|
||||
}
|
||||
m.put(kid.getName(), notation);
|
||||
}
|
||||
return m.toString();
|
||||
}
|
||||
|
||||
@Test public void move() throws Exception {
|
||||
File src = tmp.newFile();
|
||||
File dest = new File(tmp.getRoot(), "dest");
|
||||
RunIdMigrator.move(src, dest);
|
||||
File dest2 = tmp.newFile();
|
||||
assertThrows(IOException.class, () -> RunIdMigrator.move(dest, dest2));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2023 CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import static hudson.cli.CLICommandInvoker.Matcher.succeededSilently;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import hudson.cli.CLICommandInvoker;
|
||||
import hudson.cli.CreateJobCommand;
|
||||
import hudson.model.FreeStyleProject;
|
||||
import hudson.model.Item;
|
||||
import hudson.model.User;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.htmlunit.HttpMethod;
|
||||
import org.htmlunit.WebRequest;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.jvnet.hudson.test.Issue;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import org.jvnet.hudson.test.MockAuthorizationStrategy;
|
||||
|
||||
public class RunIdMigratorTest {
|
||||
|
||||
@Rule
|
||||
public JenkinsRule j = new JenkinsRule();
|
||||
|
||||
@Test
|
||||
public void legacyIdsPresent() throws Exception {
|
||||
FreeStyleProject p = j.createFreeStyleProject();
|
||||
File legacyIds = new File(p.getBuildDir(), "legacyIds");
|
||||
assertTrue(legacyIds.exists());
|
||||
}
|
||||
|
||||
@Issue("JENKINS-64356")
|
||||
@Test
|
||||
public void legacyIdsPresentViaRestApi() throws Exception {
|
||||
User user = User.getById("user", true);
|
||||
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
|
||||
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy()
|
||||
.grant(Jenkins.READ, Item.CREATE)
|
||||
.everywhere()
|
||||
.to(user.getId()));
|
||||
String jobName = "test" + j.jenkins.getItems().size();
|
||||
try (JenkinsRule.WebClient wc = j.createWebClient()) {
|
||||
wc.login(user.getId());
|
||||
WebRequest req = new WebRequest(wc.createCrumbedUrl("createItem?name=" + jobName), HttpMethod.POST);
|
||||
req.setAdditionalHeader("Content-Type", "application/xml");
|
||||
req.setRequestBody("<project/>");
|
||||
wc.getPage(req);
|
||||
}
|
||||
FreeStyleProject p = j.jenkins.getItemByFullName(jobName, FreeStyleProject.class);
|
||||
assertNotNull(p);
|
||||
File legacyIds = new File(p.getBuildDir(), "legacyIds");
|
||||
assertTrue(legacyIds.exists());
|
||||
}
|
||||
|
||||
@Issue("JENKINS-64356")
|
||||
@Test
|
||||
public void legacyIdsPresentViaCli() {
|
||||
String jobName = "test" + j.jenkins.getItems().size();
|
||||
CLICommandInvoker invoker = new CLICommandInvoker(j, new CreateJobCommand());
|
||||
CLICommandInvoker.Result result = invoker.withStdin(
|
||||
new ByteArrayInputStream("<project/>".getBytes(StandardCharsets.UTF_8)))
|
||||
.invokeWithArgs(jobName);
|
||||
assertThat(result, succeededSilently());
|
||||
FreeStyleProject p = j.jenkins.getItemByFullName(jobName, FreeStyleProject.class);
|
||||
assertNotNull(p);
|
||||
File legacyIds = new File(p.getBuildDir(), "legacyIds");
|
||||
assertTrue(legacyIds.exists());
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue