提交 ea383598 编写于 作者: K kohsuke

added a new extension point to define permalinks within builds.

The primary use case of this for now is the promoted build plugin.

I then intend to use this in the ZFS plugin so that I can control what snapshots to leave and what to dispose of, based on the permalinks.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@12185 71c3de6d-444a-0410-be80-ed276b4c234a
上级 e19e2ced
......@@ -219,7 +219,7 @@
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
<version>1.82</version>
<version>1.83</version>
</dependency>
<dependency>
<!-- until the next rev of stapler picks up localizer 1.5 -->
......
......@@ -4,10 +4,16 @@ import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Describable;
import hudson.model.Job;
import hudson.model.TaskListener;
import hudson.util.DescriptorList;
import java.io.IOException;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Prepares and provisions workspaces for {@link AbstractProject}s.
......@@ -19,11 +25,53 @@ import java.io.File;
* STILL A WORK IN PROGRESS. SUBJECT TO CHANGE!
*
* TODO: is this per {@link Computer}? Per {@link Job}?
* -> probably per slave.
*
* <h2>Design Problems</h2>
* <ol>
* <li>
* Garbage collection of snapshots. When do we discard snapshots?
* In one use case, it would be convenient to keep the snapshot of the
* last promoted/successful build. So we need to define a mechanism
* to veto GC of snapshot? like an interface that Action can implement?
*
* Snapshot should be obtained per user's direction. That would be a good
* moment for the user to specify the retention policy.
*
* <li>
* Configuration mechanism. Should we auto-detect FileSystemProvisioner
* per OS? (but for example, zfs support would require the root access.)
* People probably needs to be able to disable this feature, which means
* one more configuration option. It's especially tricky because
* during the configuration we don't know the OS type.
*
* OTOH special slave type like the ones for network.com grid can
* hide this.
* </ol>
*
*
* <h2>Recap</h2>
*
* To recap,
*
* - when a slave connects, we auto-detect the file system provisioner.
* (for example, ZFS FSP would check the slave root user prop
* and/or attempt to "pfexec zfs create" and take over.)
* -> hmm, is it better to do this manually?
*
* - the user may configure jobs for snapshot collection, along with
* the retention policy.
*
* Can't the 2nd step happen automatically, when someone else depends on
* the workspace snapshot of the upstream?
*
* To support promoted builds, we need an abstraction for permalinks.
* This is also needed for other UI.
*
* @author Kohsuke Kawaguchi
* @since 1.235
*/
public abstract class FileSystemProvisioner implements ExtensionPoint {
public abstract class FileSystemProvisioner implements ExtensionPoint, Describable<FileSystemProvisioner> {
/**
* Called very early in the build (before a build places any files
* in the workspace, such as SCM checkout) to provision a workspace
......@@ -31,18 +79,25 @@ public abstract class FileSystemProvisioner implements ExtensionPoint {
*
* <p>
* This method can prepare the underlying file system in preparation
* for the later {@link #snapshot(AbstractBuild)}.
*
* for the later {@link #snapshot(AbstractBuild, FilePath, TaskListener)}.
*
* TODO: the method needs to be able to see the snapshot would
* TODO : the method needs to be able to see the snapshot would
* be later needed. In fact, perhaps we should only call this method
* when Hudson knows that a snapshot is later needed?
*
* @param ws
* New workspace should be prepared in this location. This is the same value as
* {@code build.getProject().getWorkspace()} but passed separately for convenience.
*/
public abstract void prepareWorkspace(AbstractBuild<?,?> build) throws IOException;
public abstract void prepareWorkspace(AbstractBuild<?,?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException;
public abstract void discardWorkspace(AbstractProject<?,?> project) throws IOException;
/**
* When a project is deleted, this method is called to undo the effect of
* {@link #prepareWorkspace(AbstractBuild, FilePath, TaskListener)}.
*/
public abstract void discardWorkspace(AbstractProject<?,?> project, TaskListener listener) throws IOException, InterruptedException;
public abstract void moveWorkspace(AbstractProject<?,?> project, File oldWorkspace, File newWorkspace) throws IOException;
// public abstract void moveWorkspace(AbstractProject<?,?> project, File oldWorkspace, File newWorkspace) throws IOException;
/**
* Obtains the snapshot of the workspace of the given build.
......@@ -53,7 +108,62 @@ public abstract class FileSystemProvisioner implements ExtensionPoint {
* but for example {@link MatrixBuild} would call this after
* SCM check out so that the state of the fresh workspace
* can be then propagated to elsewhere.
*
* <p>
* If the implementation of this method needs to store data in a file system,
* do so under {@link AbstractBuild#getRootDir()}, since the lifecycle of
* the snapshot is tied to the life cycle of a build.
*
* @param ws
* New workspace should be prepared in this location. This is the same value as
* {@code build.getProject().getWorkspace()} but passed separately for convenience.
*/
public abstract WorkspaceSnapshot snapshot(AbstractBuild<?,?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException;
public abstract FileSystemProvisionerDescriptor getDescriptor();
/**
* A list of available file system provider types.
*/
public abstract WorkspaceSnapshot snapshot(AbstractBuild<?,?> build) throws IOException;
public static final DescriptorList<FileSystemProvisioner> LIST = new DescriptorList<FileSystemProvisioner>();
/**
* Default implementation that doesn't rely on any file system specific capability,
* and thus can be used anywhere that Hudson runs.
*/
public static final class Default extends FileSystemProvisioner {
public void prepareWorkspace(AbstractBuild<?, ?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException {
ws.mkdirs();
}
public void discardWorkspace(AbstractProject<?, ?> project, TaskListener listener) throws IOException, InterruptedException {
project.getWorkspace().deleteRecursive();
}
/**
* Creates a tar ball.
*/
public WorkspaceSnapshot snapshot(AbstractBuild<?, ?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException {
File wss = new File(build.getRootDir(),"workspace.zip");
OutputStream os = new BufferedOutputStream(new FileOutputStream(wss));
try {
ws.createZipArchive(os);
} finally {
os.close();
}
return new WorkspaceSnapshotImpl();
}
public FileSystemProvisionerDescriptor getDescriptor() {
return null;
}
public static final class WorkspaceSnapshotImpl extends WorkspaceSnapshot {
public void restoreTo(AbstractBuild<?,?> owner, FilePath dst, TaskListener listener) throws IOException, InterruptedException {
File wss = new File(owner.getRootDir(),"workspace.zip");
new FilePath(wss).unzip(dst);
}
}
}
}
package hudson;
import hudson.model.Descriptor;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
import java.io.IOException;
/**
* {@link Descriptor} for {@link FileSystemProvisioner}.
*
* @author Kohsuke Kawaguchi
*/
public abstract class FileSystemProvisionerDescriptor extends Descriptor<FileSystemProvisioner> implements ExtensionPoint {
protected FileSystemProvisionerDescriptor(Class<? extends FileSystemProvisioner> clazz) {
super(clazz);
}
/**
* Called to clean up a workspace that may potentially belong to this {@link FileSystemProvisioner}.
*
* <p>
* Because users may modify the file system behind Hudson, and slaves may come and go when
* configuration changes hapen, in general case Hudson is unable to keep track of which jobs
* have workspaces in which slaves.
*
* <p>
* So instead we rey on a garbage collection mechanism, to look at workspaces left in the file system
* without the contextual information of the owner project, and try to clean that up.
*
* <p>
* This method is called to do this, after Hudson determines that the workspace should be deleted
* to reclaim disk space. The implementation of this method is expected to sniff the contents of
* the workspace, and if it looks like the one created by {@link FileSystemProvisioner#prepareWorkspace(AbstractBuild, TaskListener)},
* perform the necessary deletion operation, and return <tt>true</tt>.
*
* <p>
* If the workspace isn't the one created by this {@link FileSystemProvisioner}, or if the
* workspace can be simply deleted by {@link FilePath#deleteRecursive()}, then simply
* return <tt>false</tt> to give other {@link FileSystemProvisionerDescriptor}s a chance to
* discard them.
*
* @param ws
* The workspace directory to be removed.
* @param listener
* The status of the operation, error message, etc., should go here.
* @return
* true if this {@link FileSystemProvisionerDescriptor} is responsible for de-alocating the workspace.
* false otherwise, in which case the other {@link FileSystemProvisionerDescriptor}s are asked to
* clean up the workspace.
*/
public abstract boolean discard(FilePath ws, TaskListener listener) throws IOException, InterruptedException;
}
package hudson;
import hudson.model.Action;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
import java.io.IOException;
/**
* Represents a workspace snapshot created by {@link FileSystemProvisioner}.
*
* <p>
* This class encapsulates a logic to use the snapshot elsewhere.
* This class encapsulates a logic to use the snapshot elsewhere.
* The instance will be persisted with the {@link AbstractBuild} object
* as an {@link Action}.
*
* <p>
* TODO: how to garbage-collect this object, especially for zfs?
* perhaps when a new build is started?
*
* @see FileSystemProvisioner
* @author Kohsuke Kawaguchi
*/
public abstract class WorkspaceSnapshot implements Action {
public abstract void restoreTo(FilePath dst);
/**
* Restores the snapshot to the given file system location.
*
* @param owner
* The build that owns this action. It's always the same value for any given {@link WorkspaceSnapshot},
* but passed in separately so that implementations don't need to keep them in fields.
* @param dst
* The file path to which the snapshot shall be restored to.
* @param listener
* Send the progress of the restoration to this listener. Never null.
*/
public abstract void restoreTo(AbstractBuild<?,?> owner, FilePath dst, TaskListener listener) throws IOException, InterruptedException;
public String getIconFileName() {
// by default, hide from the UI
return null;
}
public String getDisplayName() {
return "Workspace";
}
public String getUrlName() {
return "workspace";
}
}
......@@ -5,6 +5,7 @@ import hudson.model.Descriptor;
import org.apache.commons.jelly.JellyException;
import org.kohsuke.stapler.MetaClass;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
/**
......@@ -50,7 +51,7 @@ public abstract class MavenReporterDescriptor extends Descriptor<MavenReporter>
* Returns true if this descriptor has <tt>config.jelly</tt>.
*/
public final boolean hasConfigScreen() {
MetaClass c = MetaClass.get(getClass());
MetaClass c = WebApp.getCurrent().getMetaClass(getClass());
try {
JellyClassTearOff tearOff = c.loadTearOff(JellyClassTearOff.class);
return tearOff.findScript(getConfigPage())!=null;
......
......@@ -9,6 +9,7 @@ import hudson.model.Descriptor.FormException;
import hudson.model.Fingerprint.RangeSet;
import hudson.model.RunMap.Constructor;
import hudson.model.listeners.RunListener;
import hudson.model.PermalinkProjectAction.Permalink;
import hudson.remoting.AsyncFutureImpl;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
......@@ -621,6 +622,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
/**
* Gets the {@link Resource} that represents the workspace of this project.
* Useful for locking and mutual exclusion control.
*/
public Resource getWorkspaceResource() {
return new Resource(getFullDisplayName()+" workspace");
......
......@@ -6,6 +6,7 @@ import hudson.Util;
import hudson.XmlFile;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.ItemListener;
import hudson.model.PermalinkProjectAction.Permalink;
import hudson.search.QuickSilver;
import hudson.search.SearchIndex;
import hudson.search.SearchIndexBuilder;
......@@ -554,6 +555,12 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return w;
}
// is this a permalink?
for (Permalink p : getPermalinks()) {
if(p.getUrl().equals(token))
return p.resolve(this);
}
return super.getDynamic(token, req, rsp);
}
}
......@@ -669,6 +676,21 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return r;
}
/**
* Gets all the {@link Permalink}s defined for this job.
*/
public List<Permalink> getPermalinks() {
// TODO: shall we cache this?
List<Permalink> permalinks = new ArrayList<Permalink>(Permalink.BUILTIN);
for (Action a : getActions()) {
if (a instanceof PermalinkProjectAction) {
PermalinkProjectAction ppa = (PermalinkProjectAction) a;
permalinks.addAll(ppa.getPermalinks());
}
}
return permalinks;
}
/**
* Used as the color of the status ball for the project.
*/
......
......@@ -65,12 +65,18 @@ public abstract class JobProperty<J extends Job<?,?>> implements Describable<Job
* Returning non-null from this method allows a job property to add an item
* to the left navigation bar in the job page.
*
* <p>
* {@link Action} can implement additional marker interface to integrate
* with the UI in different ways.
*
* @param job
* Always the same as {@link #owner} but passed in anyway for backward compatibility (I guess.)
* You really need not use this value at all.
* @return
* null if there's no such action.
* @since 1.102
* @see ProminentProjectAction
* @see PermalinkProjectAction
*/
public Action getJobAction(J job) {
return null;
......
package hudson.model;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Optional interface for {@link Action}s that are attached
* to {@link AbstractProject} (through {@link JobProperty#getJobAction(Job)}),
* which allows plugins to define additional permalinks in the project.
*
* <p>
* Permalinks are listed together in the UI for better ease of use,
* plus other plugins can use this information elsewhere (for example,
* a plugin to download an artifact from one of the permalinks.)
*
* @author Kohsuke Kawaguchi
* @since 1.253
* @see JobProperty
*/
public interface PermalinkProjectAction extends Action {
/**
* Gets the permalinks defined for this project.
*
* @return
* can be empty, but never null.
*/
List<Permalink> getPermalinks();
public static abstract class Permalink {
/**
* String to be displayed in the UI, such as "Last successful build".
* The convention is to upper case the first letter.
*/
public abstract String getDisplayName();
/**
* The URL token to represent the permalink, such as "lastSuccessfulBuild".
* This becomes the part of the permanent URL.
*/
public abstract String getUrl();
/**
* Resolves the permalink to a build.
*
* @return null
* if the target of the permalink doesn't exist.
*/
public abstract Run<?,?> resolve(Job<?,?> job);
/**
* List of {@link Permalink}s that are built into Hudson.
*/
public static final List<Permalink> BUILTIN = new CopyOnWriteArrayList<Permalink>();
static {
BUILTIN.add(new Permalink() {
public String getDisplayName() {
return Messages.Permalink_LastBuild();
}
public String getUrl() {
return "lastBuild";
}
public Run<?,?> resolve(Job<?,?> job) {
return job.getLastBuild();
}
});
BUILTIN.add(new Permalink() {
public String getDisplayName() {
return Messages.Permalink_LastStableBuild();
}
public String getUrl() {
return "lastStableBuild";
}
public Run<?,?> resolve(Job<?,?> job) {
return job.getLastStableBuild();
}
});
BUILTIN.add(new Permalink() {
public String getDisplayName() {
return Messages.Permalink_LastSuccessfulBuild();
}
public String getUrl() {
return "lastSuccessfulBuild";
}
public Run<?,?> resolve(Job<?,?> job) {
return job.getLastSuccessfulBuild();
}
});
BUILTIN.add(new Permalink() {
public String getDisplayName() {
return Messages.Permalink_LastFailedBuild();
}
public String getUrl() {
return "lastFailedBuild";
}
public Run<?,?> resolve(Job<?,?> job) {
return job.getLastFailedBuild();
}
});
}
}
}
......@@ -4,6 +4,10 @@ package hudson.model;
* Marker interface for {@link Action}s that should be displayed
* at the top of the project page.
*
* {@link #getIconFileName()}, {@link #getUrlName()}, {@link #getDisplayName()}
* are used to create a large, more visible icon in the top page to draw
* users' attention.
*
* @author Kohsuke Kawaguchi
*/
public interface ProminentProjectAction extends Action {
......
package hudson.util;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.XMLFilter;
import org.xml.sax.ContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.apache.commons.jelly.XMLOutput;
import java.util.Stack;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
/**
* {@link XMLFilter} that checks the proper nesting of table related tags.
*
* <p>
* Browser often "fixes" HTML by moving tables into the right place,
* so failure to generate proper tables can result in a hard-to-track bugs.
*
* <p>
* TODO: where to apply this in stapler?
* JellyClassTearOff creates XMLOutput. Perhaps we define a decorator?
* We can also wrap Script. would that work better?
*
* @author Kohsuke Kawaguchi
*/
public class TableNestChecker extends XMLFilterImpl {
private final Stack<Checker> elements = new Stack<Checker>();
private final Stack<String> tagNames = new Stack<String>();
public static void applyTo(XMLOutput xo) {
xo.setContentHandler(new TableNestChecker(xo.getContentHandler()));
}
public TableNestChecker() {
elements.push(ALL_ALLOWED);
}
public TableNestChecker(ContentHandler target) {
this();
setContentHandler(target);
}
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
String tagName = localName.toUpperCase();
// make sure that this tag occurs in the proper context
if(!elements.peek().isAllowed(tagName))
throw new SAXException(tagName+" is not allowed inside "+tagNames.peek());
Checker next = CHECKERS.get(tagName);
if(next==null) next = ALL_ALLOWED;
elements.push(next);
tagNames.push(tagName);
super.startElement(uri, localName, qName, atts);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
elements.pop();
tagNames.pop();
super.endElement(uri, localName, qName);
}
private interface Checker {
boolean isAllowed(String childTag);
}
private static final Checker ALL_ALLOWED = new Checker() {
public boolean isAllowed(String childTag) {
return true;
}
};
private static final class InList implements Checker {
private final Set<String> tags;
private InList(String... tags) {
this.tags = new HashSet<String>(Arrays.asList(tags));
}
public boolean isAllowed(String childTag) {
return tags.contains(childTag);
}
}
private static final Map<String,Checker> CHECKERS = new HashMap<String, Checker>();
static {
CHECKERS.put("TABLE",new InList("TR","THEAD","TBODY"));
InList rows = new InList("TR");
CHECKERS.put("THEAD",rows);
CHECKERS.put("THEAD",rows);
CHECKERS.put("TR", new InList("TD","TH"));
}
}
......@@ -36,13 +36,7 @@
<p:upstream-downstream />
<h2>${%Permalinks}</h2>
<ul>
<p:build-permalink property="lastBuild" title="${%Last build}" />
<p:build-permalink property="lastStableBuild" title="${%Last stable build}" />
<p:build-permalink property="lastSuccessfulBuild" title="${%Last successful build}" />
<p:build-permalink property="lastFailedBuild" title="${%Last failed build}" />
</ul>
<st:include page="permalinks.jelly" />
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
Workspace=Arbeitsbereich
Recent\ Changes=Letzte Änderungen
Latest\ Test\ Result=Letztes Testergebnis
Permalinks=Permalinks
Last\ build=Letzter Build
Last\ stable\ build=Letzter stabiler Build
Last\ successful\ build=Letzter erfolgreicher Build
Last\ failed\ build=Letzter fehlgeschlagener Build
Workspace=Espace de travail
Recent\ Changes=Changements rcents
Latest\ Test\ Result=Derniers rsultats des tests
Permalinks=Liens permanents
Last\ build=Dernier build
Last\ stable\ build=Dernier build stable
Last\ successful\ build=Dernier build avec succs
Last\ failed\ build=Dernier build en chec
Last\ Successful\ Artifacts=Derniers artefacts construits avec succs
......@@ -8,9 +8,4 @@ Overview={0}\u306e\u6982\u8981
Workspace=\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9
Recent\ Changes=\u6700\u65b0\u306e\u5909\u66f4
Latest\ Test\ Result=\u6700\u65b0\u306e\u30c6\u30b9\u30c8\u7d50\u679c
Permalinks=\u6c38\u7d9a\u30ea\u30f3\u30af
Last\ build=\u6700\u65b0\u306e\u30d3\u30eb\u30c9
Last\ stable\ build=\u6700\u65b0\u306e\u5b89\u5b9a\u30d3\u30eb\u30c9
Last\ successful\ build=\u6700\u65b0\u306e\u6210\u529f\u30d3\u30eb\u30c9
Last\ failed\ build=\u6700\u65b0\u306e\u5931\u6557\u30d3\u30eb\u30c9
Last\ Successful\ Artifacts=\u6700\u65b0\u6210\u529f\u30d3\u30eb\u30c9\u306e\u6210\u679c\u7269
\ No newline at end of file
Last\ Successful\ Artifacts=\u6700\u65b0\u6210\u529f\u30d3\u30eb\u30c9\u306e\u6210\u679c\u7269
......@@ -6,8 +6,3 @@ Overview={0} Overzicht
Workspace=Werkplaats
Recent\ Changes=Recente veranderingen
Latest\ Test\ Result=Laatste testresultaat
Permalinks=Permanente referenties
Last\ build=Laatste bouwpoging
Last\ stable\ build=Laatste stabiele bouwpoging
Last\ successful\ build=Laatste succesvolle bouwpoging
Last\ failed\ build=Laaste gefaalde bouwpoging
......@@ -6,8 +6,3 @@ Overview={0}Vis\u00E3o Geral
Workspace=
Recent\ Changes=Mudan\u00E7as Recentes
Latest\ Test\ Result=\u00DAltimo Resultado de Teste
Permalinks=
Last\ build=\u00DAltima constru\u00E7\u00E3o
Last\ stable\ build=\u00DAltima constru\u00E7\u00E3o est\u00E1vel
Last\ successful\ build=\u00DAltima constru\u00E7\u00E3o com sucesso
Last\ failed\ build=\u00DAltima constru\u00E7\u00E3o com falha
Workspace=\u0421\u0431\u043e\u0440\u043e\u0447\u043d\u0430\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f
Recent\ Changes=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f
Latest\ Test\ Result=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432
Permalinks=\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438
Last\ build=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Last\ stable\ build=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Last\ successful\ build=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Last\ failed\ build=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0448\u0430\u044f\u0441\u044f \u0441\u0431\u043e\u0440\u043a\u0430
......@@ -7,9 +7,4 @@ A\ build\ is\ in\ progress\ to\ parse\ the\ list\ of\ modules\ from\ POM.=POM i\
Workspace=\u00e7al\u0131\u015fma Alan\u0131
Recent\ Changes=Son De\u011fi\u015fiklikler
Latest\ Test\ Result=En son test sonu\u00e7lar\u0131
Permalinks=Permalinks
Last\ build=Son yap\u0131land\u0131rma
Last\ stable\ build=Son stabil yap\u0131land\u0131rma
Last\ successful\ build=Son ba\u015far\u0131l\u0131 yap\u0131land\u0131rma
Last\ failed\ build=Son ba\u015far\u0131s\u0131z yap\u0131land\u0131rma
Last\ Successful\ Artifacts=Son Ba\u015far\u0131l\u0131 Artefaktlar
......@@ -24,7 +24,7 @@
</t:buildCaption>
<div>
<t:editableDescription permission="${app.ADMINISTER}" />
<t:editableDescription permission="${it.UPDATE}" />
</div>
<table style="margin-top: 1em; margin-left:1em;">
......
......@@ -14,13 +14,7 @@
<!-- inject main part here -->
<st:include page="main.jelly" />
<h2>${%Permalinks}</h2>
<ul>
<p:build-permalink property="lastBuild" title="${%Last build}" />
<p:build-permalink property="lastStableBuild" title="${%Last stable build}" />
<p:build-permalink property="lastSuccessfulBuild" title="${%Last successful build}" />
<p:build-permalink property="lastFailedBuild" title="${%Last failed build}" />
</ul>
<st:include page="permalinks.jelly" />
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
<!-- display permalinks of the page -->
<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" xmlns:p="/lib/hudson/project">
<h2>${%Permalinks}</h2>
<ul>
<j:set var="job" value="${it}" />
<j:forEach var="p" items="${it.permalinks}">
<st:include page="link.jelly" it="${p}"/>
</j:forEach>
</ul>
</j:jelly>
\ No newline at end of file
Permalinks=\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438
......@@ -94,3 +94,8 @@ UpdateCenter.Status.Success=Success
UpdateCenter.Status.UnknownHostException=\
<span class=error>Failed to resolve host name {0}. \
Perhaps you need to <a href='../pluginManager/advanced'>configure HTTP proxy?</a></span>
Permalink.LastBuild=Last build
Permalink.LastStableBuild=Last stable build
Permalink.LastSuccessfulBuild=Last successful build
Permalink.LastFailedBuild=Last failed build
......@@ -87,3 +87,7 @@ Slave.UnixSlave=Dies ist ein UNIX-Slave
Slave.WindowsSlave=Dies ist ein Windows-Slave
View.Permissions.Title=Anzeigen
Permalink.LastBuild=Letzter Build
Permalink.LastStableBuild=Letzter stabiler Build
Permalink.LastSuccessfulBuild=Letzter erfolgreicher Build
Permalink.LastFailedBuild=Letzter fehlgeschlagener Build
AbstractBuild.BuildingRemotely=Construction distance sur {0}
AbstractBuild.KeptBecause=conserv cause de {0}
AbstractBuild.BuildingRemotely=Construction \u00e0 distance sur {0}
AbstractBuild.KeptBecause=conserv\u00e9 \u00e0 cause de {0}
AbstractProject.NewBuildForWorkspace=Demande d''un nouveau build afin d''avoir un rpertoire de travail
AbstractProject.NewBuildForWorkspace=Demande d''un nouveau build afin d''avoir un r\u00e9pertoire de travail
AbstractProject.Pronoun=Projet
AbstractProject.Aborted=Annul
AbstractProject.BuildInProgress=Le build #{0} est dj en cours {1}
AbstractProject.Disabled=Build dsactiv
AbstractProject.ETA=\ (fin prvue :{0})
AbstractProject.Aborted=Annul\u00e9
AbstractProject.BuildInProgress=Le build #{0} est d\u00e9j\u00e0 en cours {1}
AbstractProject.Disabled=Build d\u00e9sactiv\u00e9
AbstractProject.ETA=\ (fin pr\u00e9vue \u00e0:{0})
AbstractProject.NoSCM=Pas d''outil de gestion de version
AbstractProject.NoWorkspace=Pas rpertoire de travail disponible, donc impossible de rcuprer les mises jour.
AbstractProject.PollingABorted=Scrutation de l''outil de gestion de version annule
AbstractProject.ScmAborted=Rcupration des mises jour partir de l''outil de gestion de version annule
AbstractProject.WorkspaceOffline=Le rpertoire de travail est dconnect.
AbstractProject.NoWorkspace=Pas r\u00e9pertoire de travail disponible, donc impossible de r\u00e9cup\u00e9rer les mises \u00e0 jour.
AbstractProject.PollingABorted=Scrutation de l''outil de gestion de version annul\u00e9e
AbstractProject.ScmAborted=R\u00e9cup\u00e9ration des mises \u00e0 jour \u00e0 partir de l''outil de gestion de version annul\u00e9e
AbstractProject.WorkspaceOffline=Le r\u00e9pertoire de travail est d\u00e9connect\u00e9.
Api.MultipleMatch=Le XPath "{0}" correspond {1} noeuds. \
Merci de fournir un XPath qui ne correspond qu' un noeud, ou utilisez le paramtre de requte "wrapper" pour les encapsuler tous dans un lment racine.
Api.MultipleMatch=Le XPath "{0}" correspond \u00e0 {1} noeuds. \
Merci de fournir un XPath qui ne correspond qu'\u00e0 un noeud, ou utilisez le param\u00e8tre de requ\u00e8te "wrapper" pour les encapsuler tous dans un \u00e9l\u00e9ment racine.
Api.NoXPathMatch=Pas de correspondance avec le XPath {0}
BallColor.Aborted=Annul
BallColor.Disabled=Dsactiv
BallColor.Failed=En chec
BallColor.Aborted=Annul\u00e9
BallColor.Disabled=D\u00e9sactiv\u00e9
BallColor.Failed=En \u00e9chec
BallColor.InProgress=En cours
BallColor.Pending=En attente
BallColor.Success=Succs
BallColor.Success=Succ\u00e8s
BallColor.Unstable=Instable
Computer.Caption=Esclave {0}
Executor.NotAvailable=N/A
ExternalJob.DisplayName=Contrler un job externe
ExternalJob.DisplayName=Contr\u00f4ler un job externe
ExternalJob.Pronoun=Job
FreeStyleProject.DisplayName=Construire un projet free-style
Hudson.BadPortNumber=Numro de port incorrect {0}
Hudson.Computer.Caption=Matre
Hudson.Computer.DisplayName=matre
Hudson.ControlCodeNotAllowed=Code de contrle non autoris
Hudson.BadPortNumber=Num\u00e9ro de port incorrect {0}
Hudson.Computer.Caption=Ma\u00eetre
Hudson.Computer.DisplayName=ma\u00eetre
Hudson.ControlCodeNotAllowed=Code de contr\u00f4le non autoris\u00e9
Hudson.DisplayName=Hudson
Hudson.JobAlreadyExists=Un job existe dj avec le nom ''{0}''
Hudson.NoJavaInPath=java n''est pas dans votre PATH. Peut-tre avez-vous besoin de <a href=''{0}/configure''>configurer les JDKs</a>?
Hudson.NoName=Aucune nom n''est spcifi
Hudson.NoSuchDirectory=Le rpertoire n''existe pas: {0}
Hudson.NotADirectory={0} n''est pas un rpertoire
Hudson.JobAlreadyExists=Un job existe d\u00e9j\u00e0 avec le nom ''{0}''
Hudson.NoJavaInPath=java n''est pas dans votre PATH. Peut-\u00eatre avez-vous besoin de <a href=''{0}/configure''>configurer les JDKs</a>?
Hudson.NoName=Aucune nom n''est sp\u00e9cifi\u00e9
Hudson.NoSuchDirectory=Le r\u00e9pertoire n''existe pas: {0}
Hudson.NotADirectory={0} n''est pas un r\u00e9pertoire
Hudson.NotAPlugin={0} n''est pas un plugin Hudson
Hudson.NotJDKDir={0} ne semble pas tre un rpertoire contenant un JDK
Hudson.NotJDKDir={0} ne semble pas \u00eatre un r\u00e9pertoire contenant un JDK
Hudson.Permissions.Title=Global
Hudson.UnsafeChar=''{0}'' est un caractre dangereux
Hudson.UnsafeChar=''{0}'' est un caract\u00e8re dangereux
Hudson.ViewName=Tous
Item.Permissions.Title=Job
Job.AllRecentBuildFailed=Tous les builds rcents ont chou.
Job.BuildStability=Stabilit du build: {0}
Job.NOfMFailed={0} des {1} derniers builds ont chou.
Job.NoRecentBuildFailed=Aucun build rcent n''a chou.
Job.AllRecentBuildFailed=Tous les builds r\u00e9cents ont \u00e9chou\u00e9.
Job.BuildStability=Stabilit\u00e9 du build: {0}
Job.NOfMFailed={0} des {1} derniers builds ont \u00e9chou\u00e9.
Job.NoRecentBuildFailed=Aucun build r\u00e9cent n''a \u00e9chou\u00e9.
Job.Pronoun=job
Job.minutes=mins
Queue.BlockedBy=Bloqu par {0}
Queue.InProgress=Un build est dj en cours
Queue.InQuietPeriod=En priode d''attente. Expire dans {0}
Queue.BlockedBy=Bloqu\u00e9 par {0}
Queue.InProgress=Un build est d\u00e9j\u00e0 en cours
Queue.InQuietPeriod=En p\u00e9riode d''attente. Expire dans {0}
Queue.Unknown=???
Run.BuildAborted=Le build a t annul
Run.MarkedExplicitly=marqu explicitement pour conserv l''enregistrement
Run.BuildAborted=Le build a \u00e9t\u00e9 annul\u00e9
Run.MarkedExplicitly=marqu\u00e9 explicitement pour conserv\u00e9 l''enregistrement
Run.Permissions.Title=Lancer
Run.UnableToDelete=Impossible de supprimer {0}: {1}
Slave.InvalidConfig.Executors=Configuration esclave invalide pour {0}. Nombre d''excuteurs invalide.
Slave.InvalidConfig.Executors=Configuration esclave invalide pour {0}. Nombre d''ex\u00e9cuteurs invalide.
Slave.InvalidConfig.NoName=Configuration esclave invalide. Le nom est vide.
Slave.InvalidConfig.NoRemoteDir=Configuration esclave invalide pour {0}. Pas de rpertoire distant fourni
Slave.InvalidConfig.NoRemoteDir=Configuration esclave invalide pour {0}. Pas de r\u00e9pertoire distant fourni
Slave.Launching={0} Lancement de l''agent esclave
Slave.Terminated={0} l'agent esclave a t termin
Slave.Terminated={0} l'agent esclave a \u00e9t\u00e9 termin\u00e9
Slave.UnableToLaunch=Impossible de lancer l''agent esclave pour {0} {1}
Slave.UnixSlave=Ceci est un esclave Unix
Slave.WindowsSlave=Ceci est un esclave Windows
View.Permissions.Title=Voir
UpdateCenter.Status.CheckingInternet=Vrification de la connexion internet
UpdateCenter.Status.CheckingJavaNet=Vrification de la connexion java.net
UpdateCenter.Status.Success=Succs
Permalink.LastBuild=Dernier build
Permalink.LastStableBuild=Dernier build stable
Permalink.LastSuccessfulBuild=Dernier build avec succ\u00e8s
Permalink.LastFailedBuild=Dernier build en \u00e9chec
UpdateCenter.Status.CheckingInternet=V\u00e9rification de la connexion \u00e0 internet
UpdateCenter.Status.CheckingJavaNet=V\u00e9rification de la connexion \u00e0 java.net
UpdateCenter.Status.Success=Succ\u00e8s
UpdateCenter.Status.UnknownHostException=\
<span class=error>Impossible de rsoudre le nom de host {0}. \
Peut-tre devez-vous <a href='../pluginManager/advanced'>configurer un proxy HTTP?</a></span>
<span class=error>Impossible de r\u00e9soudre le nom de host {0}. \
Peut-\u00eatre devez-vous <a href='../pluginManager/advanced'>configurer un proxy HTTP?</a></span>
......@@ -29,4 +29,7 @@ UpdateCenter.Status.CheckingJavaNet=java.net\u3068\u306e\u63a5\u7d9a\u3092\u30c1
UpdateCenter.Status.Success=\u6210\u529f
UpdateCenter.Status.UnknownHostException=\
<span class=error>\u30db\u30b9\u30c8\u540d {0}\u306e\u89e3\u6c7a\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 \
\u305f\u3076\u3093\u3001<a href='../pluginManager/advanced'>HTTP\u30d7\u30ed\u30af\u30b7\u30fc\u3092\u8a2d\u5b9a</a>\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</span>
\ No newline at end of file
\u305f\u3076\u3093\u3001<a href='../pluginManager/advanced'>HTTP\u30d7\u30ed\u30af\u30b7\u30fc\u3092\u8a2d\u5b9a</a>\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002</span>Permalink.LastBuild=\u6700\u65b0\u306e\u30d3\u30eb\u30c9
Permalink.LastStableBuild=\u6700\u65b0\u306e\u5b89\u5b9a\u30d3\u30eb\u30c9
Permalink.LastSuccessfulBuild=\u6700\u65b0\u306e\u6210\u529f\u30d3\u30eb\u30c9
Permalink.LastFailedBuild=\u6700\u65b0\u306e\u5931\u6557\u30d3\u30eb\u30c9
......@@ -78,3 +78,7 @@ Slave.UnixSlave=Dit is een Unix slaaf node
Slave.WindowsSlave=Dit is een Windows slaaf node
View.Permissions.Title=Tonen
Permalink.LastBuild=Laatste bouwpoging
Permalink.LastStableBuild=Laatste stabiele bouwpoging
Permalink.LastSuccessfulBuild=Laatste succesvolle bouwpoging
Permalink.LastFailedBuild=Laatst gefaalde bouwpoging
......@@ -78,3 +78,7 @@ Slave.UnixSlave=Este \u00e9 um slave Unix
Slave.WindowsSlave=Este \u00e9 um slave Windows
View.Permissions.Title=Vis\u00e3o
Permalink.LastBuild=\u00DAltima constru\u00E7\u00E3o
Permalink.LastStableBuild=\u00DAltima constru\u00E7\u00E3o est\u00E1vel
Permalink.LastSuccessfulBuild=\u00DAltima constru\u00E7\u00E3o bem sucedida
Permalink.LastFailedBuild=\u00DAltima constru\u00E7\u00E3o que falhou
......@@ -78,3 +78,7 @@ Slave.UnixSlave=\u0423\u0437\u0435\u043b \u043f\u043e\u0434 \u0443\u043f\u0440\u
Slave.WindowsSlave=\u0423\u0437\u0435\u043b \u043f\u043e\u0434 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c Windows
View.Permissions.Title=\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440
Permalink.LastBuild=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Permalink.LastStableBuild=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Permalink.LastSuccessfulBuild=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430
Permalink.LastFailedBuild=\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0448\u0430\u044f\u0441\u044f \u0441\u0431\u043e\u0440\u043a\u0430
......@@ -79,3 +79,7 @@ Slave.UnixSlave=Bu bir unix slave
Slave.WindowsSlave=Bu bir windows slave
View.Permissions.Title=G\u00f6r\u00fcnt\u00fc
Permalink.LastBuild=Son yap\u0131land\u0131rma
Permalink.LastStableBuild=Son stabil yap\u0131land\u0131rma
Permalink.LastSuccessfulBuild=Son ba\u015far\u0131l\u0131 yap\u0131land\u0131rma
Permalink.LastFailedBuild=Son ba\u015far\u0131s\u0131z yap\u0131land\u0131rma
<!--
Permalink to a build from a project page
'job' must be bound to the parent job scope.
-->
<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:p="/lib/hudson/project">
<j:set var="b" value="${it.resolve(job)}"/>
<j:if test="${b!=null}">
<li>
<a href="${it.url}/">${%format(it.displayName,b.number,b.timestampString)}</a>
</li>
</j:if>
</j:jelly>
\ No newline at end of file
......@@ -32,11 +32,10 @@
<j2se version="1.5+" />
</j:otherwise>
</j:choose>
<jar href="${rootURL}jnlpJars/jnlp-agent.jar"/>
<jar href="${rootURL}jnlpJars/remoting.jar"/>
</resources>
<application-desc main-class="hudson.jnlp.Main">
<application-desc main-class="hudson.remoting.jnlp.Main">
<argument>${h.getServerName()}</argument>
<argument>${rootURL}tcpSlaveAgentListener/</argument>
<argument>${app.secretKey}</argument>
......
......@@ -6,6 +6,7 @@
@attrs title
Title of the link
-->
<!-- TODO: delete once batch-task plugin is updated. -->
<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:p="/lib/hudson/project">
<j:set var="b" value="${it[property]}"/>
<j:if test="${b!=null}">
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册